pax_global_header00006660000000000000000000000064152113611370014512gustar00rootroot0000000000000052 comment=8504e95f0160f61d1b617ca66f779646d2ee609e scikit-learn-contrib-imbalanced-learn-8504e95/000077500000000000000000000000001521136113700211765ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/.circleci/000077500000000000000000000000001521136113700230315ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/.circleci/config.yml000066400000000000000000000024161521136113700250240ustar00rootroot00000000000000version: 2.1 jobs: python3: docker: - image: cimg/python:3.13 environment: - OMP_NUM_THREADS: 1 steps: - checkout - run: ./build_tools/circle/checkout_merge_commit.sh - run: command: ./build_tools/circle/build_doc.sh no_output_timeout: 30m - store_artifacts: path: doc/_build/html destination: doc - store_artifacts: path: ~/log.txt destination: log.txt - persist_to_workspace: root: doc/_build/html paths: . deploy: docker: - image: cimg/python:3.13 environment: - USERNAME: "glemaitre" - ORGANIZATION: "imbalanced-learn" - DOC_REPO: "imbalanced-learn.github.io" - EMAIL: "g.lemaitre58@gmail.com" steps: - checkout - run: ./build_tools/circle/checkout_merge_commit.sh - attach_workspace: at: doc/_build/html - run: ls -ltrh doc/_build/html - deploy: command: | if [[ "${CIRCLE_BRANCH}" =~ ^master$|^[0-9]+\.[0-9]+\.X$ ]]; then bash ./build_tools/circle/push_doc.sh doc/_build/html fi workflows: version: 2 build-doc-and-deploy: jobs: - python3 - deploy: requires: - python3 scikit-learn-contrib-imbalanced-learn-8504e95/.coveragerc000066400000000000000000000002561521136113700233220ustar00rootroot00000000000000[run] branch = True [report] exclude_lines = if self.debug: pragma: no cover raise NotImplementedError ignore_errors = True omit = */tests/* **/setup.py scikit-learn-contrib-imbalanced-learn-8504e95/.github/000077500000000000000000000000001521136113700225365ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/.github/ISSUE_TEMPLATE.md000066400000000000000000000031711521136113700252450ustar00rootroot00000000000000 #### Description #### Steps/Code to Reproduce #### Expected Results #### Actual Results #### Versions scikit-learn-contrib-imbalanced-learn-8504e95/.github/ISSUE_TEMPLATE/000077500000000000000000000000001521136113700247215ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000030641521136113700274160ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us reproduce and correct the bug title: "[BUG]" labels: bug assignees: '' --- #### Describe the bug A clear and concise description of what the bug is. #### Steps/Code to Reproduce ``` Sample code to reproduce the problem ``` #### Expected Results #### Actual Results #### Versions scikit-learn-contrib-imbalanced-learn-8504e95/.github/ISSUE_TEMPLATE/documentation-improvement.md000066400000000000000000000006231521136113700324600ustar00rootroot00000000000000--- name: Documentation improvement about: Create a report to help us improve the documentation title: "[DOC]" labels: Documentation, help wanted, good first issue assignees: '' --- #### Describe the issue linked to the documentation Tell us about the confusion introduce in the documentation. #### Suggest a potential alternative/fix Tell us how we could improve the documentation in this regard. scikit-learn-contrib-imbalanced-learn-8504e95/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000010271521136113700304460ustar00rootroot00000000000000--- name: Feature request about: Suggest an new algorithm, enhancement to an existing algorithm, etc. title: "[ENH]" labels: enhancement assignees: '' --- <-- If you want to propose a new algorithm, please refer first to the scikit-learn inclusion criterion: https://scikit-learn.org/stable/faq.html#what-are-the-inclusion-criteria-for-new-algorithms --> #### Is your feature request related to a problem? Please describe #### Describe the solution you'd like #### Describe alternatives you've considered #### Additional context scikit-learn-contrib-imbalanced-learn-8504e95/.github/ISSUE_TEMPLATE/other--blank-template-.md000066400000000000000000000001771521136113700314210ustar00rootroot00000000000000--- name: Other (blank template) about: For all other issues to reach the community... title: '' labels: '' assignees: '' --- scikit-learn-contrib-imbalanced-learn-8504e95/.github/ISSUE_TEMPLATE/question.md000066400000000000000000000003701521136113700271120ustar00rootroot00000000000000--- name: Question about: If you have a usage question title: '' labels: '' assignees: '' --- ** If your issue is a usage question, submit it here instead: - The imbalanced learn gitter: https://gitter.im/scikit-learn-contrib/imbalanced-learn ** scikit-learn-contrib-imbalanced-learn-8504e95/.github/ISSUE_TEMPLATE/usage-question.md000066400000000000000000000007371521136113700302230ustar00rootroot00000000000000--- name: Usage question about: If you have a usage question title: "[SO]" labels: question assignees: '' --- ** If your issue is a usage question, submit it here instead:** - **The imbalanced learn gitter: https://gitter.im/scikit-learn-contrib/imbalanced-learn** - **StackOverflow with the imblearn (or imbalanced-learn) tag:https://stackoverflow.com/questions/tagged/imblearn** We are going to automatically close this issue if this is not link to a bug or an enhancement. scikit-learn-contrib-imbalanced-learn-8504e95/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000014631521136113700263430ustar00rootroot00000000000000 #### Reference Issue #### What does this implement/fix? Explain your changes. #### Any other comments? scikit-learn-contrib-imbalanced-learn-8504e95/.github/check-changelog.yml000066400000000000000000000057461521136113700262770ustar00rootroot00000000000000name: Check Changelog # This check makes sure that the changelog is properly updated # when a PR introduces a change in a test file. # To bypass this check, label the PR with "No Changelog Needed". on: pull_request: types: [opened, edited, labeled, unlabeled, synchronize] jobs: check: name: A reviewer will let you know if it is required or can be bypassed runs-on: ubuntu-latest if: ${{ contains(github.event.pull_request.labels.*.name, 'No Changelog Needed') == 0 }} steps: - name: Get PR number and milestone run: | echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> $GITHUB_ENV echo "TAGGED_MILESTONE=${{ github.event.pull_request.milestone.title }}" >> $GITHUB_ENV - uses: actions/checkout@v4 with: fetch-depth: '0' - name: Check the changelog entry run: | set -xe changed_files=$(git diff --name-only origin/main) # Changelog should be updated only if tests have been modified if [[ ! "$changed_files" =~ tests ]] then exit 0 fi all_changelogs=$(cat ./doc/whats_new/v*.rst) if [[ "$all_changelogs" =~ :pr:\`$PR_NUMBER\` ]] then echo "Changelog has been updated." # If the pull request is milestoned check the correspondent changelog if exist -f ./doc/whats_new/v${TAGGED_MILESTONE:0:4}.rst then expected_changelog=$(cat ./doc/whats_new/v${TAGGED_MILESTONE:0:4}.rst) if [[ "$expected_changelog" =~ :pr:\`$PR_NUMBER\` ]] then echo "Changelog and milestone correspond." else echo "Changelog and milestone do not correspond." echo "If you see this error make sure that the tagged milestone for the PR" echo "and the edited changelog filename properly match." exit 1 fi fi else echo "A Changelog entry is missing." echo "" echo "Please add an entry to the changelog at 'doc/whats_new/v*.rst'" echo "to document your change assuming that the PR will be merged" echo "in time for the next release of imbalanced-learn." echo "" echo "Look at other entries in that file for inspiration and please" echo "reference this pull request using the ':pr:' directive and" echo "credit yourself (and other contributors if applicable) with" echo "the ':user:' directive." echo "" echo "If you see this error and there is already a changelog entry," echo "check that the PR number is correct." echo "" echo "If you believe that this PR does not warrant a changelog" echo "entry, say so in a comment so that a maintainer will label" echo "the PR with 'No Changelog Needed' to bypass this check." exit 1 fi scikit-learn-contrib-imbalanced-learn-8504e95/.github/dependabot.yml000066400000000000000000000010761521136113700253720ustar00rootroot00000000000000version: 2 updates: # Maintain dependencies for GitHub Actions as recommended in SPEC8: # https://github.com/scientific-python/specs/pull/325 # At the time of writing, release critical workflows such as # pypa/gh-action-pypi-publish should use hash-based versioning for security # reasons. This strategy may be generalized to all other github actions # in the future. - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" groups: actions: patterns: - "*" reviewers: - "glemaitre" scikit-learn-contrib-imbalanced-learn-8504e95/.github/workflows/000077500000000000000000000000001521136113700245735ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/.github/workflows/circleci-artifacts-redirector.yml000066400000000000000000000017311521136113700332130ustar00rootroot00000000000000name: CircleCI artifacts redirector on: [status] # Restrict the permissions granted to the use of secrets.GITHUB_TOKEN in this # github actions workflow: # https://docs.github.com/en/actions/security-guides/automatic-token-authentication permissions: statuses: write jobs: circleci_artifacts_redirector_job: runs-on: ubuntu-latest # For testing this action on a fork, remove the "github.repository =="" condition. if: "github.repository == 'scikit-learn-contrib/imbalanced-learn' && github.event.context == 'ci/circleci: doc'" name: Run CircleCI artifacts redirector steps: - name: GitHub Action step uses: scientific-python/circleci-artifacts-redirector-action@5d358ff96e96429a5c64a969bb4a574555439f4f # v1.3.1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} api-token: ${{ secrets.CIRCLE_CI }} artifact-path: 0/doc/index.html circleci-jobs: doc job-title: Check the rendered docs here! scikit-learn-contrib-imbalanced-learn-8504e95/.github/workflows/linters.yml000066400000000000000000000010061521136113700267730ustar00rootroot00000000000000name: Run code format checks on: push: branches: - "main" pull_request: branches: - '*' jobs: run-pre-commit-checks: name: Run pre-commit checks runs-on: ubuntu-latest steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - uses: prefix-dev/setup-pixi@5185adfbffb4bd703da3010310260805d89ebb11 # v0.9.6 with: pixi-version: v0.70.1 frozen: true - name: Run tests run: pixi run -e linters linters scikit-learn-contrib-imbalanced-learn-8504e95/.github/workflows/tests.yml000066400000000000000000000034051521136113700264620ustar00rootroot00000000000000name: 'tests' on: push: branches: - "main" pull_request: branches: - '*' jobs: test: strategy: matrix: os: [windows-latest, ubuntu-latest, macos-latest] environment: [ ci-py310-min-dependencies, ci-py310-min-optional-dependencies, ci-py310-min-keras, ci-py310-min-tensorflow, ci-py311-sklearn-1-4, ci-py311-sklearn-1-5, ci-py312-sklearn-1-6, ci-py311-latest-keras, ci-py311-latest-tensorflow, ci-py314-latest-dependencies, ci-py314-latest-optional-dependencies, ] exclude: - os: windows-latest environment: ci-py310-min-keras - os: windows-latest environment: ci-py310-min-tensorflow - os: windows-latest environment: ci-py311-latest-keras - os: windows-latest environment: ci-py311-latest-tensorflow runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - uses: prefix-dev/setup-pixi@5185adfbffb4bd703da3010310260805d89ebb11 # v0.9.6 with: pixi-version: v0.70.1 environments: ${{ matrix.environment }} # we can freeze the environment and manually bump the dependencies to the # latest version time to time. frozen: true - name: Run tests run: pixi run -e ${{ matrix.environment }} tests -n 3 - name: Upload coverage reports to Codecov uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: token: ${{ secrets.CODECOV_TOKEN }} slug: scikit-learn-contrib/imbalanced-learn scikit-learn-contrib-imbalanced-learn-8504e95/.gitignore000066400000000000000000000026351521136113700231740ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg Pipfile Pipfile.lock # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover .pytest_cache/ # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ # vim *.swp # emacs *~ # Visual Studio *.sln *.pyproj *.suo *.vs .vscode/ # PyCharm .idea/ # Cython *.pyc *.pyo __pycache__ *.so *.o *.egg *.egg-info Cython/Compiler/*.c Cython/Plex/*.c Cython/Runtime/refnanny.c Cython/Tempita/*.c Cython/*.c Tools/*.elc /TEST_TMP/ /build/ /wheelhouse*/ !tests/build/ /dist/ .gitrev .coverage *.orig *.rej *.dep *.swp *~ .ipynb_checkpoints docs/build tags TAGS MANIFEST .tox cythonize.dat # build documentation doc/_build/ doc/auto_examples/ doc/generated/ doc/references/generated/ doc/bibtex/auto doc/min_dependency_table.rst # MacOS .DS_Store # Pixi folder .pixi/ # Generated files doc/min_dependency_substitutions.rst doc/sg_execution_times.rst scikit-learn-contrib-imbalanced-learn-8504e95/.pre-commit-config.yaml000066400000000000000000000006531521136113700254630ustar00rootroot00000000000000repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.3.0 hooks: - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. rev: v0.4.8 hooks: - id: ruff args: ["--fix", "--output-format=full"] - repo: https://github.com/psf/black rev: 23.3.0 hooks: - id: black scikit-learn-contrib-imbalanced-learn-8504e95/AUTHORS.rst000066400000000000000000000010631521136113700230550ustar00rootroot00000000000000History ------- Development lead ~~~~~~~~~~~~~~~~ The project started in August 2014 by Fernando Nogueira and focused on SMOTE implementation. Together with Guillaume Lemaitre, Dayvid Victor, and Christos Aridas, additional under-sampling and over-sampling methods have been implemented as well as major changes in the API to be fully compatible with scikit-learn_. Contributors ------------ Refers to GitHub contributors page_. .. _scikit-learn: http://scikit-learn.org .. _page: https://github.com/scikit-learn-contrib/imbalanced-learn/graphs/contributors scikit-learn-contrib-imbalanced-learn-8504e95/CONTRIBUTING.md000066400000000000000000000155201521136113700234320ustar00rootroot00000000000000Contributing code ================= This guide is adapted from [scikit-learn](https://github.com/scikit-learn/scikit-learn/blob/master/CONTRIBUTING.md). How to contribute ----------------- The preferred way to contribute to imbalanced-learn is to fork the [main repository](https://github.com/scikit-learn-contrib/imbalanced-learn) on GitHub: 1. Fork the [project repository](https://github.com/scikit-learn-contrib/imbalanced-learn): click on the 'Fork' button near the top of the page. This creates a copy of the code under your account on the GitHub server. 2. Clone this copy to your local disk: $ git clone git@github.com:YourLogin/imbalanced-learn.git $ cd imblearn 3. Create a branch to hold your changes: $ git checkout -b my-feature and start making changes. Never work in the ``master`` branch! 4. Work on this copy on your computer using Git to do the version control. When you're done editing, do: $ git add modified_files $ git commit to record your changes in Git, then push them to GitHub with: $ git push -u origin my-feature Finally, go to the web page of your fork of the imbalanced-learn repo, and click 'Pull request' to send your changes to the maintainers for review. This will send an email to the committers. (If any of the above seems like magic to you, then look up the [Git documentation](https://git-scm.com/documentation) on the web.) Contributing Pull Requests -------------------------- It is recommended to check that your contribution complies with the following rules before submitting a pull request: - Follow the [coding-guidelines](http://scikit-learn.org/dev/developers/contributing.html#coding-guidelines) as for scikit-learn. - When applicable, use the validation tools and other code in the `sklearn.utils` submodule. A list of utility routines available for developers can be found in the [Utilities for Developers](http://scikit-learn.org/dev/developers/utilities.html#developers-utils) page. - If your pull request addresses an issue, please use the title to describe the issue and mention the issue number in the pull request description to ensure a link is created to the original issue. - All public methods should have informative docstrings with sample usage presented as doctests when appropriate. - Please prefix the title of your pull request with `[MRG]` if the contribution is complete and should be subjected to a detailed review. Incomplete contributions should be prefixed `[WIP]` to indicate a work in progress (and changed to `[MRG]` when it matures). WIPs may be useful to: indicate you are working on something to avoid duplicated work, request broad review of functionality or API, or seek collaborators. WIPs often benefit from the inclusion of a [task list](https://github.com/blog/1375-task-lists-in-gfm-issues-pulls-comments) in the PR description. - All other tests pass when everything is rebuilt from scratch. On Unix-like systems, check with (from the toplevel source folder): $ make - When adding additional functionality, provide at least one example script in the ``examples/`` folder. Have a look at other examples for reference. Examples should demonstrate why the new functionality is useful in practice and, if possible, compare it to other methods available in scikit-learn. - Documentation and high-coverage tests are necessary for enhancements to be accepted. - At least one paragraph of narrative documentation with links to references in the literature (with PDF links when possible) and the example. You can also check for common programming errors with the following tools: - Code with good unittest coverage (at least 80%), check with: $ pip install pytest pytest-cov $ pytest --cov=imblearn imblearn - No pyflakes warnings, check with: $ pip install pyflakes $ pyflakes path/to/module.py - No PEP8 warnings, check with: $ pip install pycodestyle $ pycodestyle path/to/module.py - AutoPEP8 can help you fix some of the easy redundant errors: $ pip install autopep8 $ autopep8 path/to/pep8.py Filing bugs ----------- We use Github issues to track all bugs and feature requests; feel free to open an issue if you have found a bug or wish to see a feature implemented. It is recommended to check that your issue complies with the following rules before submitting: - Verify that your issue is not being currently addressed by other [issues](https://github.com/scikit-learn-contrib/imbalanced-learn/issues) or [pull requests](https://github.com/scikit-learn-contrib/imbalanced-learn/pulls). - Please ensure all code snippets and error messages are formatted in appropriate code blocks. See [Creating and highlighting code blocks](https://help.github.com/articles/creating-and-highlighting-code-blocks). - Please include your operating system type and version number, as well as your Python, scikit-learn, numpy, and scipy versions. This information can be found by runnning the following code snippet: ```python import platform; print(platform.platform()) import sys; print("Python", sys.version) import numpy; print("NumPy", numpy.__version__) import scipy; print("SciPy", scipy.__version__) import sklearn; print("Scikit-Learn", sklearn.__version__) import imblearn; print("Imbalanced-Learn", imblearn.__version__) ``` - Please be specific about what estimators and/or functions are involved and the shape of the data, as appropriate; please include a [reproducible](https://stackoverflow.com/help/mcve) code snippet or link to a [gist](https://gist.github.com). If an exception is raised, please provide the traceback. Documentation ------------- We are glad to accept any sort of documentation: function docstrings, reStructuredText documents (like this one), tutorials, etc. reStructuredText documents live in the source code repository under the doc/ directory. You can edit the documentation using any text editor and then generate the HTML output by typing ``make html`` from the doc/ directory. Alternatively, ``make`` can be used to quickly generate the documentation without the example gallery. The resulting HTML files will be placed in _build/html/ and are viewable in a web browser. See the README file in the doc/ directory for more information. For building the documentation, you will need [sphinx](http://sphinx-doc.org), [matplotlib](https://matplotlib.org), and [pillow](https://pillow.readthedocs.io). When you are writing documentation, it is important to keep a good compromise between mathematical and algorithmic details, and give intuition to the reader on what the algorithm does. It is best to always start with a small paragraph with a hand-waving explanation of what the method does to the data and a figure (coming from an example) illustrating it. scikit-learn-contrib-imbalanced-learn-8504e95/LICENSE000066400000000000000000000021451521136113700222050ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014-2020 The imbalanced-learn developers. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. scikit-learn-contrib-imbalanced-learn-8504e95/MANIFEST.in000066400000000000000000000002051521136113700227310ustar00rootroot00000000000000 recursive-include doc * recursive-include examples * include AUTHORS.rst include CONTRIBUTING.md include LICENSE include README.rst scikit-learn-contrib-imbalanced-learn-8504e95/README.rst000066400000000000000000000136641521136113700226770ustar00rootroot00000000000000.. -*- mode: rst -*- .. _scikit-learn: http://scikit-learn.org/stable/ .. _scikit-learn-contrib: https://github.com/scikit-learn-contrib |GitHubActions|_ |Codecov|_ |CircleCI|_ |PythonVersion|_ |Pypi|_ |Gitter|_ |Black|_ .. |GitHubActions| image:: https://github.com/scikit-learn-contrib/imbalanced-learn/actions/workflows/tests.yml/badge.svg .. _GitHubActions: https://github.com/scikit-learn-contrib/imbalanced-learn/actions/workflows/tests.yml .. |Codecov| image:: https://codecov.io/gh/scikit-learn-contrib/imbalanced-learn/branch/master/graph/badge.svg .. _Codecov: https://codecov.io/gh/scikit-learn-contrib/imbalanced-learn .. |CircleCI| image:: https://circleci.com/gh/scikit-learn-contrib/imbalanced-learn.svg?style=shield .. _CircleCI: https://circleci.com/gh/scikit-learn-contrib/imbalanced-learn/tree/master .. |PythonVersion| image:: https://img.shields.io/pypi/pyversions/imbalanced-learn.svg .. _PythonVersion: https://img.shields.io/pypi/pyversions/imbalanced-learn.svg .. |Pypi| image:: https://badge.fury.io/py/imbalanced-learn.svg .. _Pypi: https://badge.fury.io/py/imbalanced-learn .. |Gitter| image:: https://badges.gitter.im/scikit-learn-contrib/imbalanced-learn.svg .. _Gitter: https://gitter.im/scikit-learn-contrib/imbalanced-learn?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge .. |Black| image:: https://img.shields.io/badge/code%20style-black-000000.svg .. _Black: :target: https://github.com/psf/black .. |PythonMinVersion| replace:: 3.10 .. |NumPyMinVersion| replace:: 1.25.2 .. |SciPyMinVersion| replace:: 1.11.4 .. |ScikitLearnMinVersion| replace:: 1.4.2 .. |MatplotlibMinVersion| replace:: 3.7.3 .. |PandasMinVersion| replace:: 2.0.3 .. |TensorflowMinVersion| replace:: 2.16.1 .. |KerasMinVersion| replace:: 3.3.3 .. |SeabornMinVersion| replace:: 0.12.2 .. |PytestMinVersion| replace:: 7.2.2 imbalanced-learn ================ imbalanced-learn is a python package offering a number of re-sampling techniques commonly used in datasets showing strong between-class imbalance. It is compatible with scikit-learn_ and is part of scikit-learn-contrib_ projects. Documentation ------------- Installation documentation, API documentation, and examples can be found on the documentation_. .. _documentation: https://imbalanced-learn.org/stable/ Installation ------------ Dependencies ~~~~~~~~~~~~ `imbalanced-learn` requires the following dependencies: - Python (>= |PythonMinVersion|) - NumPy (>= |NumPyMinVersion|) - SciPy (>= |SciPyMinVersion|) - Scikit-learn (>= |ScikitLearnMinVersion|) - Pytest (>= |PytestMinVersion|) Additionally, `imbalanced-learn` requires the following optional dependencies: - Pandas (>= |PandasMinVersion|) for dealing with dataframes - Tensorflow (>= |TensorflowMinVersion|) for dealing with TensorFlow models - Keras (>= |KerasMinVersion|) for dealing with Keras models The examples will requires the following additional dependencies: - Matplotlib (>= |MatplotlibMinVersion|) - Seaborn (>= |SeabornMinVersion|) Installation ~~~~~~~~~~~~ From PyPi or conda-forge repositories ..................................... imbalanced-learn is currently available on the PyPi's repositories and you can install it via `pip`:: pip install -U imbalanced-learn The package is release also in Anaconda Cloud platform:: conda install -c conda-forge imbalanced-learn From source available on GitHub ............................... If you prefer, you can clone it and run the setup.py file. Use the following commands to get a copy from Github and install all dependencies:: git clone https://github.com/scikit-learn-contrib/imbalanced-learn.git cd imbalanced-learn pip install . Be aware that you can install in developer mode with:: pip install --no-build-isolation --editable . If you wish to make pull-requests on GitHub, we advise you to install pre-commit:: pip install pre-commit pre-commit install Testing ~~~~~~~ After installation, you can use `pytest` to run the test suite:: make coverage Development ----------- The development of this scikit-learn-contrib is in line with the one of the scikit-learn community. Therefore, you can refer to their `Development Guide `_. Endorsement of the Scientific Python Specification -------------------------------------------------- We endorse good practices from the Scientific Python Ecosystem Coordination (SPEC). The full list of recommendations is available `here`_. See below the list of recommendations that we endorse for the imbalanced-learn project. |SPEC 0 — Minimum Supported Dependencies| .. |SPEC 0 — Minimum Supported Dependencies| image:: https://img.shields.io/badge/SPEC-0-green?labelColor=%23004811&color=%235CA038 :target: https://scientific-python.org/specs/spec-0000/ .. _here: https://scientific-python.org/specs/ About ----- If you use imbalanced-learn in a scientific publication, we would appreciate citations to the following paper:: @article{JMLR:v18:16-365, author = {Guillaume Lema{{\^i}}tre and Fernando Nogueira and Christos K. Aridas}, title = {Imbalanced-learn: A Python Toolbox to Tackle the Curse of Imbalanced Datasets in Machine Learning}, journal = {Journal of Machine Learning Research}, year = {2017}, volume = {18}, number = {17}, pages = {1-5}, url = {http://jmlr.org/papers/v18/16-365} } Most classification algorithms will only perform optimally when the number of samples of each class is roughly the same. Highly skewed datasets, where the minority is heavily outnumbered by one or more classes, have proven to be a challenge while at the same time becoming more and more common. One way of addressing this issue is by re-sampling the dataset as to offset this imbalance with the hope of arriving at a more robust and fair decision boundary than you would otherwise. You can refer to the `imbalanced-learn`_ documentation to find details about the implemented algorithms. .. _imbalanced-learn: https://imbalanced-learn.org/stable/user_guide.html scikit-learn-contrib-imbalanced-learn-8504e95/build_tools/000077500000000000000000000000001521136113700235155ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/build_tools/circle/000077500000000000000000000000001521136113700247565ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/build_tools/circle/build_doc.sh000077500000000000000000000010311521136113700272340ustar00rootroot00000000000000#!/usr/bin/env bash set -x set -e # deactivate circleci virtualenv and setup a miniconda env instead if [[ `type -t deactivate` ]]; then deactivate fi # Install pixi # Keep this version in sync with `pixi-version` in `.github/workflows/*.yml`. PIXI_VERSION="v0.70.1" curl -fsSL https://pixi.sh/install.sh | PIXI_VERSION="${PIXI_VERSION}" bash export PATH=/home/circleci/.pixi/bin:$PATH # The pipefail is requested to propagate exit code set -o pipefail && pixi run --frozen -e docs build-docs 2>&1 | tee ~/log.txt set +o pipefail scikit-learn-contrib-imbalanced-learn-8504e95/build_tools/circle/checkout_merge_commit.sh000077500000000000000000000016411521136113700316530ustar00rootroot00000000000000#!/bin/bash # Add `master` branch to the update list. # Otherwise CircleCI will give us a cached one. FETCH_REFS="+master:master" # Update PR refs for testing. if [[ -n "${CIRCLE_PR_NUMBER}" ]] then FETCH_REFS="${FETCH_REFS} +refs/pull/${CIRCLE_PR_NUMBER}/head:pr/${CIRCLE_PR_NUMBER}/head" FETCH_REFS="${FETCH_REFS} +refs/pull/${CIRCLE_PR_NUMBER}/merge:pr/${CIRCLE_PR_NUMBER}/merge" fi # Retrieve the refs. git fetch -u origin ${FETCH_REFS} # Checkout the PR merge ref. if [[ -n "${CIRCLE_PR_NUMBER}" ]] then git checkout -qf "pr/${CIRCLE_PR_NUMBER}/merge" || ( echo Could not fetch merge commit. >&2 echo There may be conflicts in merging PR \#${CIRCLE_PR_NUMBER} with master. >&2; exit 1) fi # Check for merge conflicts. if [[ -n "${CIRCLE_PR_NUMBER}" ]] then git branch --merged | grep master > /dev/null git branch --merged | grep "pr/${CIRCLE_PR_NUMBER}/head" > /dev/null fi scikit-learn-contrib-imbalanced-learn-8504e95/build_tools/circle/linting.sh000077500000000000000000000142621521136113700267660ustar00rootroot00000000000000#!/bin/bash # This script is used in CircleCI to check that PRs do not add obvious # flake8 violations. It relies on two things: # - find common ancestor between branch and # scikit-learn/scikit-learn remote # - run flake8 --diff on the diff between the branch and the common # ancestor # # Additional features: # - the line numbers in Travis match the local branch on the PR # author machine. # - ./build_tools/circle/flake8_diff.sh can be run locally for quick # turn-around set -e # pipefail is necessary to propagate exit codes set -o pipefail PROJECT=scikit-learn-contrib/imbalanced-learn PROJECT_URL=https://github.com/$PROJECT.git # Find the remote with the project name (upstream in most cases) REMOTE=$(git remote -v | grep $PROJECT | cut -f1 | head -1 || echo '') # Add a temporary remote if needed. For example this is necessary when # Travis is configured to run in a fork. In this case 'origin' is the # fork and not the reference repo we want to diff against. if [[ -z "$REMOTE" ]]; then TMP_REMOTE=tmp_reference_upstream REMOTE=$TMP_REMOTE git remote add $REMOTE $PROJECT_URL fi echo "Remotes:" echo '--------------------------------------------------------------------------------' git remote --verbose # Travis does the git clone with a limited depth (50 at the time of # writing). This may not be enough to find the common ancestor with # $REMOTE/master so we unshallow the git checkout if [[ -a .git/shallow ]]; then echo -e '\nTrying to unshallow the repo:' echo '--------------------------------------------------------------------------------' git fetch --unshallow fi if [[ "$TRAVIS" == "true" ]]; then if [[ "$TRAVIS_PULL_REQUEST" == "false" ]] then # In main repo, using TRAVIS_COMMIT_RANGE to test the commits # that were pushed into a branch if [[ "$PROJECT" == "$TRAVIS_REPO_SLUG" ]]; then if [[ -z "$TRAVIS_COMMIT_RANGE" ]]; then echo "New branch, no commit range from Travis so passing this test by convention" exit 0 fi COMMIT_RANGE=$TRAVIS_COMMIT_RANGE fi else # We want to fetch the code as it is in the PR branch and not # the result of the merge into master. This way line numbers # reported by Travis will match with the local code. LOCAL_BRANCH_REF=travis_pr_$TRAVIS_PULL_REQUEST # In Travis the PR target is always origin git fetch origin pull/$TRAVIS_PULL_REQUEST/head:refs/$LOCAL_BRANCH_REF fi fi # If not using the commit range from Travis we need to find the common # ancestor between $LOCAL_BRANCH_REF and $REMOTE/master if [[ -z "$COMMIT_RANGE" ]]; then if [[ -z "$LOCAL_BRANCH_REF" ]]; then LOCAL_BRANCH_REF=$(git rev-parse --abbrev-ref HEAD) fi echo -e "\nLast 2 commits in $LOCAL_BRANCH_REF:" echo '--------------------------------------------------------------------------------' git --no-pager log -2 $LOCAL_BRANCH_REF REMOTE_MASTER_REF="$REMOTE/master" # Make sure that $REMOTE_MASTER_REF is a valid reference echo -e "\nFetching $REMOTE_MASTER_REF" echo '--------------------------------------------------------------------------------' git fetch $REMOTE master:refs/remotes/$REMOTE_MASTER_REF LOCAL_BRANCH_SHORT_HASH=$(git rev-parse --short $LOCAL_BRANCH_REF) REMOTE_MASTER_SHORT_HASH=$(git rev-parse --short $REMOTE_MASTER_REF) COMMIT=$(git merge-base $LOCAL_BRANCH_REF $REMOTE_MASTER_REF) || \ echo "No common ancestor found for $(git show $LOCAL_BRANCH_REF -q) and $(git show $REMOTE_MASTER_REF -q)" if [ -z "$COMMIT" ]; then exit 1 fi COMMIT_SHORT_HASH=$(git rev-parse --short $COMMIT) echo -e "\nCommon ancestor between $LOCAL_BRANCH_REF ($LOCAL_BRANCH_SHORT_HASH)"\ "and $REMOTE_MASTER_REF ($REMOTE_MASTER_SHORT_HASH) is $COMMIT_SHORT_HASH:" echo '--------------------------------------------------------------------------------' git --no-pager show --no-patch $COMMIT_SHORT_HASH COMMIT_RANGE="$COMMIT_SHORT_HASH..$LOCAL_BRANCH_SHORT_HASH" if [[ -n "$TMP_REMOTE" ]]; then git remote remove $TMP_REMOTE fi else echo "Got the commit range from Travis: $COMMIT_RANGE" fi echo -e '\nRunning flake8 on the diff in the range' "$COMMIT_RANGE" \ "($(git rev-list $COMMIT_RANGE | wc -l) commit(s)):" echo '--------------------------------------------------------------------------------' # We ignore files from sklearn/externals. Unfortunately there is no # way to do it with flake8 directly (the --exclude does not seem to # work with --diff). We could use the exclude magic in the git pathspec # ':!sklearn/externals' but it is only available on git 1.9 and Travis # uses git 1.8. # We need the following command to exit with 0 hence the echo in case # there is no match MODIFIED_FILES="$(git diff --name-only $COMMIT_RANGE | grep -v 'sklearn/externals' | \ grep -v 'doc/sphinxext' || echo "no_match")" check_files() { files="$1" shift options="$*" if [ -n "$files" ]; then # Conservative approach: diff without context (--unified=0) so that code # that was not changed does not create failures git diff --unified=0 $COMMIT_RANGE -- $files | flake8 --diff --max-line-length=88 --show-source $options fi } if [[ "$MODIFIED_FILES" == "no_match" ]]; then echo "No file outside sklearn/externals and doc/sphinxext has been modified" else check_files "$(echo "$MODIFIED_FILES" | grep -v ^examples)" check_files "$(echo "$MODIFIED_FILES" | grep ^examples)" \ --config ./setup.cfg fi echo -e "No problem detected by flake8\n" # For docstrings and warnings of deprecated attributes to be rendered # properly, the property decorator must come before the deprecated decorator # (else they are treated as functions) # do not error when grep -B1 "@property" finds nothing set +e bad_deprecation_property_order=`git grep -A 10 "@property" -- "*.py" | awk '/@property/,/def /' | grep -B1 "@deprecated"` if [ ! -z "$bad_deprecation_property_order" ] then echo "property decorator should come before deprecated decorator" echo "found the following occurrencies:" echo $bad_deprecation_property_order exit 1 fi scikit-learn-contrib-imbalanced-learn-8504e95/build_tools/circle/push_doc.sh000077500000000000000000000024621521136113700271250ustar00rootroot00000000000000#!/bin/bash # This script is meant to be called in the "deploy" step defined in # circle.yml. See https://circleci.com/docs/ for more details. # The behavior of the script is controlled by environment variable defined # in the circle.yml in the top level folder of the project. GENERATED_DOC_DIR=$1 if [[ -z "$GENERATED_DOC_DIR" ]]; then echo "Need to pass directory of the generated doc as argument" echo "Usage: $0 " exit 1 fi # Absolute path needed because we use cd further down in this script GENERATED_DOC_DIR=$(readlink -f $GENERATED_DOC_DIR) if [ "$CIRCLE_BRANCH" = "master" ] then dir=dev else # Strip off .X dir="${CIRCLE_BRANCH::-2}" fi MSG="Pushing the docs to $dir/ for branch: $CIRCLE_BRANCH, commit $CIRCLE_SHA1" cd $HOME if [ ! -d $DOC_REPO ]; then git clone --depth 1 --no-checkout -b master "git@github.com:"$ORGANIZATION"/"$DOC_REPO".git"; fi cd $DOC_REPO git config core.sparseCheckout true echo $dir > .git/info/sparse-checkout git checkout master git reset --hard origin/master git rm -rf $dir/ && rm -rf $dir/ cp -R $GENERATED_DOC_DIR $dir touch $dir/.nojekyll git config --global user.email $EMAIL git config --global user.name $USERNAME git config --global push.default matching git add -f $dir/ git commit -m "$MSG" $dir git push origin master echo $MSG scikit-learn-contrib-imbalanced-learn-8504e95/conftest.py000066400000000000000000000020361521136113700233760ustar00rootroot00000000000000# This file is here so that when running from the root folder # ./imblearn is added to sys.path by pytest. # See https://docs.pytest.org/en/latest/pythonpath.html for more details. # For example, this allows to build extensions in place and run pytest # doc/modules/clustering.rst and use imblearn from the local folder # rather than the one from site-packages. import os import numpy as np import pytest from sklearn.utils.fixes import parse_version # use legacy numpy print options to avoid failures due to NumPy 2.+ scalar # representation if parse_version(np.__version__) > parse_version("2.0.0"): np.set_printoptions(legacy="1.25") def pytest_runtest_setup(item): fname = item.fspath.strpath if ( fname.endswith(os.path.join("keras", "_generator.py")) or fname.endswith(os.path.join("tensorflow", "_generator.py")) or fname.endswith("miscellaneous.rst") ): try: import tensorflow # noqa except ImportError: pytest.skip("The tensorflow package is not installed.") scikit-learn-contrib-imbalanced-learn-8504e95/doc/000077500000000000000000000000001521136113700217435ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/doc/Makefile000066400000000000000000000156421521136113700234130ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = -v SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* -rm -rf auto_examples/ -rm -rf generated/* -rm -rf modules/generated/* html: # These two lines make the build a bit more lengthy, and the # the embedding of images more robust rm -rf $(BUILDDIR)/html/_images #rm -rf _build/doctrees/ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html touch $(BUILDDIR)/html/.nojekyll @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/imbalanced-learn.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/imbalanced-learn.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/imbalanced-learn" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/imbalanced-learn" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." scikit-learn-contrib-imbalanced-learn-8504e95/doc/_static/000077500000000000000000000000001521136113700233715ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/doc/_static/css/000077500000000000000000000000001521136113700241615ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/doc/_static/css/imbalanced-learn.css000066400000000000000000000022131521136113700300470ustar00rootroot00000000000000@import url("theme.css"); .highlight a { text-decoration: underline; } .deprecated p { padding: 10px 7px 10px 10px; color: #b94a48; background-color: #f3e5e5; border: 1px solid #eed3d7; } .deprecated p span.versionmodified { font-weight: bold; } .wy-nav-content { max-width: 1200px !important; } /* Override some aspects of the pydata-sphinx-theme */ /* Main index page overview cards */ .intro-card { padding: 30px 10px 20px 10px; } .intro-card .sd-card-img-top { margin: 10px; height: 52px; background: none !important; } .intro-card .sd-card-title { color: var(--pst-color-primary); font-size: var(--pst-font-size-h5); padding: 1rem 0rem 0.5rem 0rem; } .intro-card .sd-card-footer { border: none !important; } .intro-card .sd-card-footer p.sd-card-text { max-width: 220px; margin-left: auto; margin-right: auto; } .intro-card .sd-btn-secondary { background-color: #6c757d !important; border-color: #6c757d !important; } .intro-card .sd-btn-secondary:hover { background-color: #5a6268 !important; border-color: #545b62 !important; } .card, .card img { background-color: var(--pst-color-background); } scikit-learn-contrib-imbalanced-learn-8504e95/doc/_static/img/000077500000000000000000000000001521136113700241455ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/doc/_static/img/favicon.ico000066400000000000000000000021761521136113700262740ustar00rootroot00000000000000 h(    ytwr}toxsźzynpga~{tsypjulflD~`TOӳ:9~5|4swÿɃ՜)З#޹mE1^:l8e9`#Ybu{͛6̗-ȏÈ=8f;@CAEƋΚ1۶iQ.Ypqk] :547(v(.0-/p`iscikit-learn-contrib-imbalanced-learn-8504e95/doc/_static/img/logo.png000066400000000000000000001046061521136113700256220ustar00rootroot00000000000000PNG  IHDR1WiCCPICC profile(}=H@_SE* ␡:Yq*BZu0 4$).kŪ "%/)=B4gt̤b.*^! 0efsGw ѯ,DYf6mp'tAG+q.,̘LjR+]ʦFHss9{kb<ɩz-+tFN?1z%xG?nI_9!DkƠ(f@#H}qvp=0bnFu*"jaWUi.VPUU+w&b'^Y뜑"y,Yؓ1-X٩M^4LtOa8>ɲxG=y ƤUfGlnUlӵlaQ:'y XǏZ[#{|(9NĔ7uj^X9Y<2G=3ڡs.p[ȉZU&.ɃCL\0I8~gM`uwXuR =' l8Վ ^"GZy#;j뱳=R#D]oI܃Cj \ v>1b{噙Tx:TE * (@4 e7ɿQ6&D6ADGd7$)j!uƴ̴ 7[v O_<{bb>4s"8thH`-+\OLx X̋ú R غ#=녰UuF$jC`*zTX!.(bo:n}ST[SU,+36.yՇyt) M[-mį0.,bdT  LTՊ,hO4``ݿcF-Zuvk o"X$sdDfs]ŎDpb Ԋ`~M.D__gX"Ǹh#6*EDNg[㺇 WO{绌IE@s=GkUh}pѾ6V}lv;&l#|z>0a^GM]sUwё^ TѓQNӣܔrp*yT,v:xzUNՁgLNE$dORUTDoѣ,{ ^fxm[rhe ^ K i'}uƺmf).2DcG\RsQUtJQ;Yh`_]ud7YO}6@hT0iA2:,N_wI !^+r;p\78y|v#!W<ƪr7n376wi ==Y.iBVVóظʽѵ ep 7ٸcoDWx<1b(-\~}?k-pDjCvlA^Te, $B_xAF#j`t2ttocZ {oÉ"N^wQuL57+H X:HqLcGC(ASjcUu-͈A+ àIOTVkg QܽSH x#շ*mpӋFJΧGjF1y=o>m3m؞<˯(LĀ3]2n2 q.lU^UB'6s܀\wtD-9[K\[pTkطft2fɨu_2y}~2cdd')UV }?O,ˮ2>cRE)\}GOU4"{o+7i(,+f?t\0~ϲ8M{] ;-5ޙ?.ĩݔMpx0_8uڀn(_kq!G(vDD.em!yu*{AN7 yj6Ӥ*Y\E#0V~|/3n קxLs5֬cb[+*5Z"[(9֊ށ21P~oim)֥ƕFνI)QQnxN8:nra#RUߤo_L4.& y )2xPU_BpzEo,%(vWr16s+f\E0{mc` |Db;3u/_lX/,B*K`Xoe#46:SXј^?C~)`mT 5\Ai/j}W,)E>?Fn.a|m{MUo+DTy.I-7}R㏫w.{gݎK82'72Q% ?i§ƵmY%xՈfBY `"&(qWI8D(̣=eޝWoKf7&*>]fԅ{ΟP^m);fCyZk2Pa/afmկ2a%V]y C!F:3RQʥӜZg7B>\zeŊ6N?am[#=T/9WTDŽr9D|XZUvVD~$xQ׏uXߧ7-\/}kr䶵Ȩ#>b5VK$seGl[[Q}E ̷ƕqWeEm4: >ޛn<աKqfkgթm{<_ra1_A?0,\sb |.E72F`weZX{2e ~`nkNՏDcL9?a /%_=5wN.en.|_6DK\=xXk]m0O)Kqm_4,@*aJU.pA*P[27InD~(gCdQߩ ,_zjcHCnKSBEr6ŚO3L'<2.1cK %l,$,SBcvWm3ձT,^I-'xL1!vڰ*$~4v.LWk] /q[̉U'I0BU9P9t˨~چMFr5DI B`&gVr^ #L Ä_fG"t[$JKujk8C|"nߗ>i:W#jaezK%XU}¼'D؉M`㄃[/-57&%)oܙaWVÏy1)(WMZ3 7O\P)3̽RW>a"eN {w= WJ"&ojNZ +uM,?HL=O+w^#lX^]cqCv҄=S;\4b:dl$> WIXGIxv7{6y=糩;O[Wl3]N2j}WµہRQ5Ʈjᖗ"|E+#9wa[Jgi𕙎<8WKMտ/[%ZGxa$=_9a85~bK X Ю3Z\zW8x89\!r{2qcpD=" ;ƖmxMaC O'^Aݕcx-&m w_C=])-gqmp.-)1 \A "K . 1;Λ)n;r1]kk-TgX"p_8C8\Dơ&*]rբb ϾoOΉ0uQym yK|n@&t]*Tүݝo/;k\2i5Sec:V`P8CT :MNfӆ* VFQn>H?/{<„m9Cd<r^w{F+WǺxz>]mN:gסd$L"p`~ č 9^n_0+|7>{U3\*uVb2 Uy?'PNyƘ66=(Jr]v9'O55ǎO V8M+1Nr@巯X,8}g*Os9xk/ "SUy@`͙$ ^nG,yա_r˧c cO{νyUq;A8xga<#b2Ua_W!ڣ?Þ Q@.5qBs&;OB3=,/\䯾1Qx0ݭ-f'sM^됼B0F.|K87B< `UP8/$XɦJ!nlnq83B|1]AT1fwEK3' =?.k86 'Uz0#dʹrb[8}\IH1N gjB=-AɦM"Ӑrp讬ےZ d`T8OC#e1ۥ!Y 2&@S$K#fyu<i_!BtΤxd ,HQyթeVU7ZLn[5Ľc IPڞH, ~ cT51{b3䯜nLJk0C O+"D{kbIP\`zzx=AzBdÉ'hR,o؊zT7 IJ%GmF14SwقڮlU[(RBh tf_ F`J^"2YU/z=A-gtmaRe'Qy3948 :%☺od͞m!8_3F"W$DÑr/*I/¢\D&*U]| 8t%=V PNAu6x C8 $DǑvLVKFz 1`"t9Q+cs0o È]ܣrU]\M5*ˀWbp;x0 $Pi1`l!Bjr20~E!MN\#ѨW^dxYQQupGf/kT=U& F輨ε 95m'*hu0ӌ9PN72=h$`=@' 엾S|B\ :;!jsR1I`y)9!{_5p)䍷X 0'Y)02vQs'T<΂DsBYG+RtS3I S5'9k:7[0$\J{OkS耴'0"rn#x O$`oaؐrBtZ(U9[5cܩN{ͽx$;4eR]{]*X`$ dEjx(C_srS FUy5ƙ+~k]yy|V$b)[]9+hA`+ ,_(Vp[&,_^̙{-DtG1Nsk;<ꮪ󁇁 eN?wڗNgS@?yKHIXVmw=h" k XD{E`U%.h\|ij>CH1Zo:A$q0eX_>)*C{t'J c%Lߡ`CU~ݓ_wk`t&U/j~uErpt]z8;)k,av~ ߦ=m< ,N^Jvh#ZǨѨ@B!UU[-q̐h]5%Y\5 Lů=# LؓOC⼔|C}9 Rk$!>w / 'r-^T ]{bu5l"#V6rb2_H#1;t=SĞ  a ?YɀQpGu}&̄YDFXH=`:?ou3~NN~5tަelˏ.mk`tqQnBD? }To Ū~k<<3CLJ֤_YWFr)HqEg,wB7V ={琒.XL}cb,)->3h>ŏ})Ԟ IDATu'!EEw|!3_=kƵeHQd)S>t>BH[=s`Y8˞[(Z HbzT"ފg\O^o| {!bz:7b@ ގ_l(fܙtIԟ%ȩ'/wko=^=BLH&nBJۅ[EC`( G.ԭi?-,@}3)NSG+_S690Gc9 H#ˬYd)ަ=#/##`c .y;D]F}y\{trSbʑRbs{w mx[_?]p 81zyn֖g=,5n&2/1-RPk7.Xnkg.azy׾𝍏,? 8_U|<,ꪪsH(A3ohϏ/]GW85Z]ZO^}-} +a Dh5΢$ו/ſSk,K8@s.o_p?ZcwxU.Yx58 ðh>UտzaG"H<[|żEdvGz-"х]'ų it;^ŭ$:d< wێQ+T_Mwۭj{Qqy5Ķ%+>z"o6MڅX&!HZEWs!X,թh٦1q.Mtwi9Z*y5oUȍS]tuO^X DJnH<6 n;Isޮ#"lIwoC'Yxem l|mug0Dl颎5gO }_8ll⤐`7*Nkul% V!4) 30}G }.)kM8v..kx_<iʅֿ;{6p ]>_kС>rR5ԥ,bC t`K3I$:Κc쉟(E۹ݱ{Ym~:S Hz>pXL$sY1 W#)zBG-;gǀg[q'FA#.u>arjFֽ"ݧ@ērD#-MJ|^'kU2ֈޭɼ%7Ri*=\i>l$p"HIϱoGXcʱFZtߊZ[[f9wњ 9e(k/F\TߺD޽Q$.^@)3tg1f4puOD\@=waռ7YsE"^b11ZcOwKCֵ-"۞mr|Mq% & kpzuW͋ۤ5c7~_v/k!`JZhX1=o50V^FZ`2$<ϻ-Th7uȷrmN/•OZ}5|T?[`*+AcB|X&oû =弶ǹr]Ylveݱ$kB۾ͻh >G X#8^sa,_ R"%"m;j:UӓRL y'7bvG1|9ڼKxtܱ԰',ih4poF0S0 JG!ec~V$ަkON1/}Cj=%j~,U?}"f@Qf9D#R=Q݈zoH1C> EHXvbƼwgm0N>.?ic۴o7ͮWg eEQT5=@s2)1ǽ5?7^SϦN 'ߊ֘,}#;qŞx2b$oJ| 43gDNңkX#Mlּm? |.IAiK{ &KjXU)i_|FPU]k{[ /1< TnqD'4 =H!kFmZ>tć#}ހ5tbo心v̈K ˀ?R <aݾʹ`kNk ,j1^s4-?wtgN׻WK-[* {{n8k6W`L6~NȵM^T^<Ɠ; lℶiWLDɎ Wxf H;ݼI <~! "zK|,Zy@ H$>]C ᓿĴI`;8wbę)J\sy~pt.] t epk%7$-w}!%|-jO`:%t@ _][aֆ?t/ ,/xmUJZa; v8+;:y;vV ,4e[q=z~zi!d՝D׼2`.D–ؙ͸E,פ3RYK6q`}bɼ\9f}Oxzu< ~%&pt(jJ:f?h_ǏnYuM\tW*Uxr5"VV6*ű$!1= ko p /v^8 _ݬr=>JЄy޵ou-..5Q|^_I8:^PkSsm5?} m5 y\#7{Vִ$a岹D,/}S^ΡǑK`b|.EUT-KQ-WsgeP o|GC?zԥ`;.Eiǀ r"3IH`9x`Ajm&CÞ_0eBA=n`r!|([;r&gUu*NjDr" L`E8rBԟX*bA[:^ . -I4{pH^!Z(A_0k2v594ʁI^ܲ[x#[ͳ(IhX(s"8thH`!N#ϳH#njc;+ͩv'0`#ֿ`O*̘[lC%vH Y.J0.EG_-/ѕ~_kxo&ko R۽0ٹes} K7l ?&8/S5|7D9.Uv%0#ΧG7U0vg]6;|2S=zX'Ͷ}݄,L:b\7uZ1`5 T K!6,R ÖrN>]ª-nۄM{B"U& _ؽcNTa.aVû[w>YGtS2H4أkqpүYfqےxYaF|u^XaW,=^,YxuXeQH{@p8ao|ysm #FUOVϞ5&'50TW?LH.ئ قE3 ¯& fF2ߊnzN0UZncZ|,nz4"ʊl֐5 .xv}r"=͝Kmf>+Tt7_l%lnNߧƺךqb_U-[O7gr TݪlE@Z%_ E|GϱgERsgSzZ^C X(ڿP8J2;*ĝjZqUm(܆fHcoDWx<1b(-\~}?k/pVɫ̂;Ov1 u^RLq1Iy?G$/C'dU`}Хmk y& v8~ku1?xϨyX˒j_F5$5jd29ml5]֩W |<^ kc^J,GǸ2eOgl\%Iளc{tA2E=<3iNnmx`}B+9kx`~zEoݰ8꼋FYo=Q,2kÄ#ވP\ o,f,؆–`LYjaʼn.f.'%/:gP=+ʶ߻|r`i4r\{kcps7V/D,َ˱s!Gۊ_3'.w58/z !K(Kjx/Xp3&O6wtϟ/@IW~`[z2+BWcۦrf=8&vGg.͢KkӻfM HX; 0jb['֋eЇXz+,gF)GUES @ hA=>ax g.5;"ozɕXzP]<3vQ*b b9m%)!李ցsv'[(A&GxLnSaO^CoOs22e xdYS<_i_7|'}M!.~U+/b>WM64C(jB^{ݲHaLܧt A#|ݠh=]Ze5em-Yj퍶 OWuEm''8NZ.kC\6-X*ƈ+|crAs i;p⑇ UF.@c;Z4Ml]7ʮ-?LUW CZiWdwh-Fp/ 2/z}C7JtTQFÇ׾^Ye. kJ  & J]ں[O  ~y8*R;,6qS2+EDn8N٫/]cJa%s\Y__W=5C3 IDAT*Z1y&g$1WҎ]stR% DwK}XgO k?1|5oMSΝܶuؼP7>s8iJ"N@I._nR y+SFx+fѥjQ\RH`H9#iz:t),y:J`^FpzXSK8<ò4mxzgK6'iCSe/ψqjÝYkr!mcAUI,sùoQIsc li YJ'5bU 'ޕSWc|n,qø /-W[k_GHo{ >0W߭Ϟ2 .vZc{~ŝQt5Ӻ6´hjPvNJϧݎϱP7{]YYvNԍvYkn4P,䛌cm .5554iA–׾y҉՟0+wSa=0L:l5k=8`cIfgtW9Q$8iدV꜖c;[ʮ]QNj)]3 ֔:DĨ8`X9-8\M07EÂȕ~uF&YTPKm[[ê4x<K9`K&:gJM1mHj)~Cķk/ ȋG=O[ĸrgJ}qYXsHs][Oџ<-XkhWrSU/)Q|P\\YR[[KMM X UM3ۣg)aarY*ży 3*v_ޔ) KRpꙢRkީ?Ggyb7JpJU%Țي*]#5uP<2e \+|6b yjŷP5:Leґ@J2AjBUy(䝌a1`-$V6IV C1ƥZ'5 dFV؁{:qٿ`sRś;R߹5['}kU]|UX|&YO-A[aZ~kk%nVMz9s|hqt7V-{KVA20 L7 uz30U<uy Zp3RzRum27OcݗĢewy .nFQu^jڗ-]#ܺ8OOqچ֪1Ñ$Ixc'z.U~`vuI* ?9(ٸCf^?nS4+qV"_V!1˲1? +}-v]474~]Zb󫅩iy_UK6U~lj[mޝK\]-|pYZ/JxpVѭcWmj&.IzxQqT\aV>x2ǙwIJ=5tTRkI;sN7j!،e]AHK3ᡨ-D=<*Xd&]~n>~*pcE ֠uRngQx#3o>^Rd B llTՌL^xCa3A2"adnK&ZH%[Iة>+WݢD2%ZXw~ף\,uR5|zq:܏-^fiJW%v=f !B|:d2ܖfs[ o/P_Tҩ`kIe`o\~xeTvěeϛ|fmڼ73()5{结I.;-0nxt᛻ >w+$C$/.?bw yag[ݬf2 %:-KbQmbKtH`53~rVW谄G1o> 4I?kZ68xQRxKg0T6% 01 +o+] W+)ɌIB{ܦ0u R v 3' <9*suyTTQr/=[;cUk6Zy/#ϒ-ʽU&wv!NIzagGuD-?`s_p|蛾+qv^R|ň%:V$p3R`"~O# GdJVwkZO(~%_CB!Ru 05<ڮAYqA?u^wtOcl~[^9~9Y}˸/!-EVS"+= _{%gy;+˾ם/hΙ,y.I%MT`lL0; xy9~viAߞ R ^%}o$*֫*OapGeqq"^e}Qo/@Q,CwI|Tײ8uAB\5W3aR~GJyQj*YK"P)(V6Two>d6 fzL#I*kŖM~NfO'n^]GI\ _Ya21ˏ6g(IKBew<% nk(wJ0e?{T׮% +}쿜C^%W:c%g|t 7ZW;$%`} DUF}!2vfێ{ 9~HXօB_(xOu'=vس܀HX'XxA*~䪳^j+!IVĬf"j- W/n^LiyZcGK>t\~uŷ}dGOp0 qRn?tӽL"c4v-`--%|Iá4 u-YgaG+s }c揋Kq e:tl\~l>Wӷ<Θ0xV{:$60׼Pɕg&)/isߞTM^w>ˢk_ЂQiD]2G:OWz ^kuݕsCh@U\}Ia9T8phyDy8w*GOL8ڽ>XK;{r2)g_|0r=B^4'v"Y]^R.uџ=WL\s7L/8AJv3dԵdѶG!C@)s-ʇmv $؋gy0kV7[:ky|]LJe%MX/Ub;%~Ȗ;kϤTyxɯ>p_gf_to 8;X-}!RՙsRGRYH[P̕~ӫWQ&ķ|W 7&+X Hx0eG1sɢ4%U ݠJST}bWL6Ki^ɤps&W/}*Y8~œ|>zWv]]>]9U]3o,: KIA5eHB!䍥v\ݵJ׀)ImrDuڈ!/O ?%GFŎMb۴Og43ܼrBRcw|؞;Yr^9K; ~ݷM\~njI~wm4H#vqpe}p]6w~kR vGƺRKO#,0ր(0\ZaJ*gDr< b_&7WzHö 75鿎~qkI<66kJ\D* 7|k@sɢ74:1BY >gh[ I.=V2GO ־j \LǦvQ5l-[n̞%)mp)sQ YUxƷ%4 #3[K)Ån٫į-/Wў- JOX |()X+_XF5o )aZs5ǵ܎ QMfGGГT1_s*~`{@l@;$-gIR^&^Z`B'WzŶdέd|f͂g ^#%9JrH8f>G&WR潂/{`[7aVr$:Ff(g=Xk7eLiL3cd$#$kp}χML,ظ]Y>'q ?2e/cYK!1lN]"Mf; bzܵ a"!9LQa)+|)耸g%HwX*Q2"yF]sb<ς#;׻rObL򎚮{FBf.o]10(F.H*3;mDk=+5 N4Ĝ20C}8XF B<0mPt;{3f4"D!ıRX3Zzb;~V6 ̙f=8wI# WvjHZb~ r#B\#u KlKg.Iܫ($)w80'  Leo<)|h"#  (e; O\)fsQMx/?*Nx6ITA@7>1$yc ^.f]gAwEI`S 6 8-ThˁԶ!}wVzrcbb#<{gBpbVE`QOõHaoe{@yL W'P.M߆f-ٰArq:/*nRc`bJr6;4(B} lNEkW@r $ 1Jl,dNKP]h[CbB!)}g]щ{3VjPHԂ$*~T[M; "f 6.rN$"e=h D_|cb-̓B[De6 l^`7PYROn`2@8v |rmH~N~Y ෆߝ&*D뮜R#3,zOFx<H~okWfߗ)D\;>:%ͅ>m`|.1! $*EtwX9 )нQt!e !$Hid2Yd2fJ"RJZJ|3Wq0 ,ȫH(?~\V@b*FP΀vx ~ʌg|Z"(ڌ C\oK޽Pgg'"jNs7+(v{XfG1X(a.Tk QR~" 'iJ&0$fFCWE!)NC9xB%Tq = g)`0),K,vd:Q"~\pSq 4yy94M ! 8S}EVD*vHeǠjw rNhiM#θ P" +x֣<(727,FK\@d+Qr18_ZCf 0Yx'5M5̪&~-V|hcC#PM F%Ш|zoam(ǒPYL>{睄t0+{ 'ބM`Q{W$<Ҋ?d kCCDfƠۈ`EF-B[`4M_R 8';7ٟ|dc#|tC֥'\ h'ׄBQ铼z뭼ඨG;4sΝ;-*C&% Hnrri| uѣzHikQAޯᎀԫq9dLhD8(盧 S>9'O,/@Ь8uj{'@HD DH(2x&B1/Ũ-tJx#;v.!yjp/ߜ BA(p@^E.ՓqE'sG=.2gpBݵE%,@T|UK1R m)BC= GITǢB&ZӈIp~D:ۇVÌRFI^vs/+̋h2L/R!ik~L:T'D71n*<қFLP&QPj]+kT@bJ! i4 MLB'TBt72zR#|* ~Xֶe7LgB ՆQnQPZ:i lJ'M#V؅RAHp/m Օ2 RX_D"y$+ h/D۸;@o&#'5J/m("׉m m7*RSh[sdd>a&X6EKVX9C&ؐE1N;c7܋(/ɧQjW2 azA*X⨗barjIMK`dF>C=90f P^;Q6WI9=n 8SWs]7]a Z6|7z5PǮEQ*#]=@`;8fG~zO\`AVQz5d ]5ib`2HnGgӣlQ"xGBbN E!G`5!!4|aB2X5{]*" ,_oH6)%s/(GqJS"?89 |k_ATIjͥ%^5'L pRw:ZکP#XKu"1=W|¶|¡'c>,\ns/+{2J5΀ГK߆4Hl-ZC,M\ĉB2OXz5SEnZ{gu3sܻ^^\--c93} .pT@lP/PP ,o >Z@3~9蠃Նl.9$e*6W?x2C{/1j {?pk̶NzlyXMXAͺcY[6oc̘1S-G=))n&v֎ivHPǎFآYBiC{KƎmPcbl ?&8”RNQ${ږrW0W+Uoq9={yv:Ӵ:zI6 283 (@ӕMvZ4wɓ'kSvfĤ"lI$vz\z*j՞x󞑦9/_{x/pWX={o_GFB#hzEyKRQl)iHakȰ@!vwbTشjؾ ~!<2dJlw!/4&/5$]-~Lu}4mJGt:M\GUKoB#\\6 es;&X!${^sJ;sr^XG/fjA^[r3=<66RB%լ17rLOǼO#=pϟTZa85`%ߣl6\Xps..Lp\ %^>JK5lX!7n,_z`iz+V]{[~bMd91#qPs*'- >%<kz EDOnVEXөk"} 6ՁsĄM3Q/Z2;i3a.O3`sNd+=D!|aBԫ},5A?, Yx6j/ (΋(mhk a~ư6-D(3›^=#b'64Og#%v|fIry)l9p? g_;iXwS46-j  bҪ@ ٿ!vd@^Yz;1pD8oöz xۊk:4dHf D'}M3~o~yצu+__rĈ~!iRJo^`6鴁fhkk N5 |xlL5˲r !,+Wh\!BlXqAw7I'W9Y8jb#3yM?~^\=V-^B)ѣGoZp2md22 RJm(+XЉòk ) 2캓VZK3*iF+AnS oJxOA9EEp r)޾ >~ˤwl^34-⧾_ &jqm);mX͸ 12 eL&ANشn(/W imdR!V4WðB=`I[5 ABܳ{ լ~6|h;<<]tud}qhBwhf֐:P[ ~N=gR@R_,5ox$ _5y94 W8 F4 ycUOF6ĺ'VQ>/*qʃZr~~:*xj Ja8~CHXqՎR7AYēPÀq@y@RXl*9(nT'ǜ2"C0XL91'qoNU?=))3\qTFfnjآ^ . 7m*aZ53"1=Qw&?4q,%LTR۵oW=~FOГ+aH+]iοsYK &׶!Zսc-|p5M3o2,̞> fTZo8iŔd=/{礫R^Yc4^vrVeIlETZb뛼 a5bD\\<҆8q=LSM01b97+j$xPXc2F.z~0zhZR)4鴪1ѽ% 1YRW.T_ s[Ra+[CՕˤMol>oB |östiٟ^;r’m%t6(%ElZj[Tϳ1#S8v%x \GGI%xDP#8;QzBCN3xM(`Pam۶j.!.%c[޳λV>gپćp1 eI-_B$/]uaXLOh/*.ǞX϶L:Tj_> N+ߎahmM&s_0{TS䤔- =3I& ;7D:l6s)wryLAŴYy}N8g4R/$ 잛^<{2 O?'u]Lb)g쌹QY}I1FN]jd2ZwϔV{;hkk!Lj~,!rRP5ZI9Znq &$ahK$5j?q2L^RCKlد(r!޽a|܋jrGWQW]Ws3V"Vo΅x=n(XJXjIڧ.O 'l68ȑ#l;Gx1mˁ1@z{hY.u9q_#*(x1Jhq&|SRʜgeY2Ap>\Ȅ9-O{Q(B2o3fLd4 V#=:̂~8VOykӝWطoe岨G_l[}|2Ş|l9BDQfDVl m?i:;; l>mye&tP9,$:G!0[ [q"pWA 1\{G`@ʬ_AIo'˴<l'$"8"}F=QE@ IcӆD3A7]ud2BU4D3g1n6_oY'~j*<NG &$r[j1$` n24suc`իب0ܣ '⥘lU ŨxXf!yEBShh4sC=Рz' T;QV=J^ym̿$ȡBrG}-Vtx(Xm')#\ݓa?%/ %ZH_xضjs 6qxUh_~TDBﻐĊq}S{YFXMKJ^X8! O=&)eƩJ_ l^9/l "$¸p+!^a9.8#߼4MZhl ,.ZSHkr9/T @M* |5& X GMٶ+KEaQ*E&f<RRa9)XJ]QYCXK_O?~@-d,YU0.ǥ*^-Iڊ!N8B?/lKpH%p}`Ց80 M`rxUOֶeY/GC)L㥁nk>f'r1c1 7eL{흴T +X{k;7FXm/Y7 3_O8|Cm!dxXѭ@\%H<*9\BCcI`Bu5|;+V u%A[ۈz4{$/Q$њXhqby Uy:%b. $>-+N ^W3ٮkw:hU_Z~?͠ [Ƕ(R=ϣ;n0PqpP9cxM/IDATH&KH=ihkD G?JA@9J`◾Nٹsg\c6,gV#J}8PV!0>N JXԈ0F7.>H.l0ml6Kgg'0a&Nȑ#sp>{NY <u3 t2` Q WL`Iq:::fw>e!$"A"4l\3gEhVTևj:dJ ̚7,{V/V7>dD$bln$IRT>JesҜ[WbTCxj Le P*QPZQ4y}Q{mxeY3&} h w{iڊ&ua=}CiLGSd!xN@28ވ#r|fD`\3*bw`܄7t\oM`h~]C҂+s<|vuQXa0U=lww ”ј6ucFO.}=-b<:m29/۪geKث1f OJkhX4k}yFl6[Z?ѯxSvjmMUB+0JjhX(l6K"-:ݸ˼Q..G mS ˲jQLCchNlέ5yiTIv@:AˊMZ(qسg&&C4*(r0òsi1BT/ uwwjhTRe/V<_b :hmI/QF3X#&I5*r]R"Z1Q`k(-`S9;;{iz>Ʉ38N8Km2a MPi . &0NRr{2jԨa\c]͉{@TM/|yՀO[[[.Qҋ8y54 ,a69l6 wD";Hcxئu7i& TXIEWkh U tih4ZZZrVrD1lLjh4w&D">m˲d2s4=a6[35!l#neYS46'@IENDB`scikit-learn-contrib-imbalanced-learn-8504e95/doc/_static/img/logo.xcf000066400000000000000000001542471521136113700256240ustar00rootroot00000000000000gimp xcf v011 1CC gimp-commentCreated with GIMPgimp-image-grid(style solid) (fgcolor (color-rgba 0 0 0 1)) (bgcolor (color-rgba 1 1 1 1)) (xspacing 10) (yspacing 10) (spacing-unit inches) (xoffset 0) (yoffset 0) (offset-unit inches) #|Baulearn!? "     Y%$#dgimp-text-layerH(markup "learn") (font ".SF Compact Ultra-Bold #4") (font-size 62) (font-size-unit pixels) (antialias yes) (language "en-fr") (base-direction ltr) (color (color-rgb 0 0 0)) (justify left) (box-mode dynamic) (box-unit pixels) (hinting yes) u #X#d#pu , 1 :,@i  "###(#8#H ,56o82S/8 o,{) `.%Eo#*!O  x.   u!D$ o-r* L"o @ @ @ @Kl\^ =  H 3c `=  k#  Kf I J' ` ( |N Ce * t U| L 8.  L  ^T  e j~ V;: JOjsUP1keDs-k5#Zi^|A, KA9Zb )a (l{o-E@p"5o& b" F!v! ">"r#$ / 7 $0 s $  *   M   8u o' Z L  ^ W , ]J F @   ! yD 5 N, 3- - )dZϠn"/- W)_1& t_#pm!RY!"tE"6Y kR9W !y i[ "l 2 6t 9N .R? 3 ; nE P QLi  F n G & MbY  l # J$6aQl<  L% Z . $  Vz  I q $n   m ?P H  Y EE s t 'NT  8  UA(L8m) (\'{'&vi * a +LjV%@q  I{ "Sw 3A,]  9 ^_* Vp On XOJd F < AO> M M *_  ox5 }0 *5' Y  4lF,*7u @ @ @0.t('x#  {?['U;|  +3; >!?" @[[ ;l ( .   z E:     -`   ;p 0g"sY## #r$##<1#wP#m#"~"#q"Eg"ca"d!zq ~ ! 8 XW   2 Tsm(+ WZ  @3 ,fD'O N. , cH+P )( ?L( %c #'Y!0m"V%)- D-Fݾe, >  l   8r ^ '"o B}@ SD I$3nz?\ "(b%VT (q* )'<&%#^"% 5_Ii|7pE504 y 'TA / e9  4d 5 {4"2o /F E.# , )]('!#$s" E.P I6%+ٗI*U׾xMjf _ S \f#"}##WY$$#:$$5%%f9&P&FW'< '7(F7(X )#mG# L $H$/ X % M&1Q&F:76-5J5kp4A3 ! 5  +  )dO I; P( >TO vO J a" )zZ +˚Y l MxBb    xu $ 4S 2, ? v p  L3 B Nt } BQz  $, > P TacjTt_J wR rd n2 Nf 0G?YA;hI2$*$"#! ce G!)"F$%D(]*z5-g =0.[45ܴ{+T 2{M$nMw" |+R+R z%  T=   k A k! W) L3 P@ bP~ f\  > 5  5 g1 a8} + "     l E  %   | o x 1v vu Bdt R9 ? ֚^"-+NjO{?6l0 @ @ @ bo*\ W9tu,K-/76 @{@d$@@@.6@m}@y@w~@ L 55 %W9 ,xX 10s0/:/g\ ' & % v$ O# !g:w1|H h9T:! x%r^bbbbm]6.X Imbalanced!? "     %$#gimp-text-layerk(markup "Imbalanced") (font ".SF Compact Ultra-Bold #4") (font-size 62) (font-size-unit pixels) (antialias yes) (language "en-fr") (base-direction ltr) (color (color-rgb 0 0 0)) (justify left) (box-mode dynamic) (box-unit pixels) (hinting yes) &X&JBlBxBX&*.D25X8n;y9?c@\A;B3G7G7G7G7G7G7G7G7G7G7G7G7G7G7G>۰r$ 6G3p 0^G37 r)G3[mJG3Y)GG37%G3k/0vQ#G3 N x G3 : G3 #? G3!  G3s G3 WG3AiG3[6GG3733G3+3+G3+3+G3+3+G3+3+G3+3+G3+3+G3+3+G3+3+G3+3+G3+3+G3+3+G3+3+G3+3+G3+3+G3+3+G3+3+g8g8g8g8g8g8g8g8g8g8g8g8g8g Z g<~ԭz3  g n f g\O o g F gh   7T {=;w  S G<     ]  x*W36 T+ @+c8 6+ 3+L 3+]z 3+d+ 3+I 3+: 3+/ 3+;  3+J 3+d7 3+b 3+] 3+/ 3+e& 3+ 3+]\ 3+ 3+q8888888888888 N~Ĩw<L  4Z e Y  N  Nd8 -J~   v  A dHHa(:,%,?,',% ," <,Fj , C,},!M, !h ݵa44, h~ӝlH%4, ~R}! 4,R4,4,;,Z,4,4Ez,EH,H:/: N~Ĩw<;̢gL  3j 4Z 3-e 3D)Y 39*3d8 -J~3˨Y.,j v3 _A d3+ D3 a(3I:,3%,38?,3~,3w ,3bw <,3HwFj ,36w C,3,w},3+wM,3+w ݵa44,3+wӝlH%4,3+w}! 4,3+w4,3+w4,3+w;,3+wZ,3+w,3+wz,3+w,3+w/3+wDؿ]/! fjl QQ2 ^H  >  M$ $Ti  r  = [* W-or|W,=:Zy YY.R  09?CGzGH{2?1x /}BO1i t h+ &V3 kh:::::::::::::/tԧv'aٶ~< OG y %\G]X{E o~o10l A~@5j30 Z+  zV t dhS% j3TcvO L[ "$F;cDiIxnB~t7 JQcQuc/`G{2WkssssssssssssssssssssssssssssssssssssssssssssssG3+3+G3+3+G3+3+G3+3+G3+3+G3+3+G3+3+G3+3+ 3+ k 3+ gx 3+I  (X 3+ 3+gK'3+gK3+g%z3+gJ #*+b۶Cw <ud Zz; /^ (o(o(J$( ( bj( (`ǟf":ܩSw <3+wud Z3+wz; /^ 3+w(3+w(3+w(3+wJ$(3+w bj(3+w`ǟf":ܩS  _ #E @i lp5 Az ! # E 2 N 247  Q %!]ѷ~B  ]d? _G ^ َI* K> %S%  (X K W ~@!5 U6 HDœ\Zŝ`(+ssssssss+],a0  6 Background!? "     %$#C 6C1=IU 6EFqFGKMQSSSSSSSW\ ]acchUhehuhhhhmnqwzqt;o nEUe[Ȅ!$1231<1'121<1)1211$&'<<$&'<<R 11 121 11 12 122}2 16 16 16 26 26 + ''' 'y'&Z ':'& ''\' 'j' 0 /|/^'p /Q_/x&B /v5/& //C'` /JY/m 67 <769<:'6: <;6:<9'6; <967<8&68 <768<;'6: <;6;<:64z42m 1* /u .-S+*).(K'b&x%$#"!w gQ@, "!+">#$a&&'a'7(")*+t,4,--X../ :;;~#), 0 368; ;;#(, / 278; 67967;67;67~667876767$69)67+6 706 73676667867;67 6 'Eb}0[N9w$5}) a- $w0s3 X6+9M; ', / 2  -&!"'*-/ 3 4 567999;;<<<<==>>(, / 2  -&! "(+-/ 2 3 567899:;;<<<==>>>@6967'678678+67: 69068 685367 67 6 67876-6:&6767! 67%69'678*67:.68067 36 46; 5677676796968:6:68;6;67<6<67=6=6<=67=67>676̺x^D( -ʭc9&Ɠc5 !і\%!g$%ږG(R +9.g 0} 2k 4!5=64789Z9::;;N>>/////>>>=====<<<<<;;;;;:::::::999/////>>>=====<<<<<;;;;;:::::::999/6/6/6/6/68>7>6>69=65=6=67=6=68<67<6<6<6<67;67;6;6;6;68:67:67:6:6:6:6:68968969 >>>>==[===<P<<<<$;P;;;; :":D:e::::999=31192152135 112 1/61-321,1+21*1)21(1'1&1%21$41#51#1"21"1!21!1 31 1212114121 12 1!1!1?!12 1!12 1!1!1!1!1!1!1!1!1!1!12 1=953 1 /-,+*)('&%$##""!!    !!!! !!! !!!!!!!!!=953 1 /-,+*)('&%$##""!!     !!!  !!!!!!!!!!  ="3915 .3 1! / -C,+.*k)f('i&q%6$ ##Z" "!,! % P `  3R ~             o  12123.1+1'1%1;!1 124!12$1$12&12(1(121+1,13-13.1201012 21 31 313 412 515616717129191916:1:12:12;10<1<13=1=1=1>1>1~12>121.+'%! !$%&()*,-.00 2 3 3 4 567788::::;<<<==>>>.+'%! !#%&()*,-.00 1 3 3 5 5667989:;:;<=<==>>>~NgiP6&. ׷j+4'a% !K : S" #%7&(%)v+,-).Q/E 0k 1H 3U 3* 4 56q7&78W899:; ;; <6<==h===->m> >>>>================1<1< >>>>================<< 7>7>6>6>6=86=76=76=76=6=6=6=6=6=6=6=6=6=6=6=6<6<6 >2>`>>====1=M=f=|=========)<m< 1 1 2 2 3 3 3 4 5 56667767889:::::;;<<<<==>>>>>.6G0;=}}}}f} 1 1 2 2 3 3 3 4 5 566677688899:::;;<<<<===>2 >>n +G0;=}}}}՚f} 16 16 26 26 36 36 36 46 56 566666667676776867869696:6:6:67:6;6;;67;6<65<67<6=6=6546767=62687 636769>67n67;67y67;67=6767}67T67f67=676 / k0 0 1 1 41 2 /2 3 V44(4x5566{77K888x9949::6;;;#<o<<<A=== =8>o>>>њM'2Z>>>>>>>-  <#  696;6@6@M6;'6>26C6CZ6@>6@>6@>6@>6@>6@>6@>6@68-6@6@6@6@6@ 68 6;6@ 6; 686767 67686866767 686786676767686767676767 67867686: 696766776?>>=Ӛ $'0e&Qg>>==6C6>686@6>696@$6>%686>06@686@&69Q6@696867>6>6=67>>m===u=999999999999999999999999999999999:::::::;;;;;<<<<<=====>>999999999999999999999999999999999:::::::;;;;;<<<<<=====>>689679696967969696969696969696969696969696969696969696796969679679696796896:6:67:6:67:67:69:6;6;6;67;6;6<6<6<6<68<6=6=6=68=6:=6>6>7$9>9S9c9s999999999999999999999w9e9R9=9%99 99:::d:@:: :;;z;L;!;<<<O<<==]===>M>!1!1!13 1 121 112111 1 1!1!1!1"1"31#1#21$1$1%1%31&21'1'21(1)1(131*21+21,1-1-31.1/2104 113 132 132 15 16216.191;21<12'<!!!     !!!""##$$%%&''()(*+,--./0 1 3 3 5669;</<!!!     !!!""##$$%%&''()(*+,--./0 1 3 3 5669;<<<M 2}(:  R!!l!""+##'$$&%}%&_''(k)( *(+U,--./ 0 1 3 3 5V66y9;-<'M6,$"+3 999::*::999 1=12=1=1=1=1;12:12912371412012 .2-1;*1M~>>=<<<;:981 -'M~>>=<=<;:973 -$M>w>2===q=<;-;;9S87#3H/w }(_-}"|0< 5-%!(082<16;12;1;1;1;1;1;12;11;11<11<12<11<12<11<11=1=12=1=1=12=12=1=1=1=1=1=12=1>1>1>1<;;;;;;;;;<<<<<<=============>>><6;6;6;6;6;76;76;6;86;86<6<6<6<7<7<7=============>>><;;;);:;gQ;Nd;.;;<<<y<K<<======w=^=I=6='===>>m>->62}}}}'}(=j*B>%$!=<4<<   , + 7    ' 4 "  2 %  /-,,+*)(&%$"! 2}}}}'}(=j*>>%$!=<4<;   + * 7    & 3 "  1 %  .-,++*(''%#" 267}67}67}67676768>67#676#6767!67676=6=65676;<6<6767667 6767:6:6676596,67 678686 67*67$676"676676 6767 6 846 46 36 67'6 716 706/67< 67"68.68-67,67+6+67)67(68'65&697$67#67"67 6 6>>>w>;>==m= =<<*<;s;::C99c98p8 7m76W65.4 4 3 C2 2 1 (0 [0/.-,,+5*7)5(.' &%$b".! N4((<== '====4}=t4G2{ .;<;}&=)"2- ==4) 4((<== '====4}=t4G2{ .;<;}&=)"2- ==4) >6767(6767(6767=67'6767'6767=67676767}6726767}67=67=6767E67.67 67=6767!6767=67(676767%67467{67 67 67676<<=<   ): -,  % 7 4 2     $  &  &0 ' ('  ' &', &' & (''( &'' ' &' '& ('' ''(('!'&' '()' '+$)'('' '+('' '#&'' '3&''x'('' "'(('' ')(''('"#'' ('+'' & '(" ' '('+ ' @('&'('(&'(*+-. / 1 3 468:<<=<   )9 - ,  %6 5 2      .  .  .0k / 6/  ./ /, / / 0//0 / / -/ /0 1.// 0//./!/U/ /3/ /+$././ /30// /./ /.3/Q// 3/00//./.-//./3,// . /.+//  /3 / . /2 / @0/./6./&'(*+-. / 1 3468:=6=6<68<6 676767;651676:69967675676767679656766 67(677666766676756 468 67&67 67 67676 6767 @067 = 6767686 =069 <%67686 6<676 <67 67 6 =<<65<;=><<6;=<;D><<67<=@<<69; <;D < 6768>;<=9 <676: @<<=<6<67&67'67(6*6+67-67. 67=/ 6781 6736746667867:<< <;=<; <=<;= <;<;9 <;;<; <;<>: <= <<= <@= <=;<9= < <>< <> <<= <9 ;< < < < <; < <> < =<= < <=3 < ;<;<=; <;U<=@; <=@<=+;<;<<:<:<7<5 <5 <5 <3 <3 <3 <2 <1 <1 < F(R$) D('%-32,('.)('(+-*('-'()('+'+'+'+'+'+'+'+''+''+'('+'('+')'+ D0/.1321/.0/0/-/+/+/+/+/+/+/+/+//+//+//+//+/0/+ D <.< <-<+<+<+<+<+<+<+<+<<+<<+<<+<<+<<+8876 4 1+#"*2 : < 5-%!(08 &<}= A}/'<}=D}3*<}= L}</'',6>@>//)! !         (+,-----,-,-,---#!"$&(*-/2 6: !"$&(*-/ 2 6: 6!76"576$86&6(96*76-6/762577 6676:876 !:"i$&(z*\(-"C>#Y';p+;;: 4,%!)/3 3 3 2 100///.-.-.-.--.-.-..-%:763 0 -)$ ;953 0 -)$ ~67=67;688679667367 067 -67:)67:%676 67B6877678 };9\ 613b 0{% ., *j&DžA!w? ĝoA7XwïiL*< 4,%!)1 8< 4,%!)/6=< ''D'@'D'C'< //D/@/D/C/67<8 <6.&#&()**+++,,,,+++** % . 9@D@A@D?C @f1 '0'0'0'/'.)'.'.'-'-',.('+,)('++*('+)('+'*)'*('* &'*0)')74/)')431+('*.-*('*)(')'()'+'+'+')('(('('(')('(('('(''#''#''"'(%'"'(33+("'(21-)"'(0/-*"'),//)**("').=:' '(*$' '3+(' '@1/)' '30/+(' (',)'71+('()**'('42.)')-0-'0/.,)'(,,)' +*)('(' (' ('*!(''' ''' '''$'&'''(' 1 /0/0/0///./././-/-/,1/+0/+0/+0/+/*0/*/*-/*20/)431/)3220/*210/*0/)/)/+/+/+/)/(/(/(/)/(/(/(//#//#//"//"/230/"/210"/2110"/0110/"/165/ /0./ /30/ /721/ /3210/ /10/420/0/3210/121/210/01/ 0/ 0/ 0/0!/// /// ///$/&/'/(/ 1 <0<0<09+('5:97/)'5100,)'5*))('5'5 '5 '5 '5 '4('()4('(+,,-4'*063').<<4'():5'()**6')*,5'(*++-,H9'+.02&'.452'''+046"4+')./,''('(,4:!A3+ '( ')!(+81*'& ('')*)''('('(*+*)'*.1'(''*/455(')+,+'''(*45( '+.01 ' ')-22 '()**" ')4 '(-27 '('$ '(.;_) '()** '(* ')*++ ' '(),, ''('''(''''''''''('( (''( ('' ,*(',('( +*('+)('( )('+**)('( '('*)('( =.''((H9-(''((&-0,('//+//+//+//+//+//+//+//+//+//+/ /+//,//-//-0750/554410/52120/50/5/5 /5 /5 /5 /4/040/014/0133/1554/55/06/05/0,:4/0122&/1332///0233"30 /1//035!830/0!/0420/ /0///0//012//013 /0//033 /0122 / /0123 /0. /03 /124/ /16E/ /0 /0 //0 //////////////// // // 0/0/ 0/0/ 0/0/ // 61//0;40///10/<<+<<+<<+<<+<<+<<+<<+<<+<<+<<+< <+<<,<<-<<-<<5 <5 <5 <5<5 <5 <5 <5 <4 <4 <4 <3 <4 <5<6<5 <,<<& < <"< 99>/./02332/011'610/0/01%861/0110$0241/0/"/0/!/42!/5430 /0/.1320/010////011/0120/ /013422/ /0137//012 //011 // // // // / / / / /0 /0 / / / /0 / / / / / / / / / ////// // /// <.<'<%<$<"<!<#<%<<<<<<<<<< << << << << << < < < < < < < < < < < < < < < < < < < <<<<<< << <<<'D65*'')0<;<=< <@?>< << <;< <;< <>< <=< << <;< <;< <=<<=<=<<:;<<:;<<;<<<<-<=,<.<.<;+<;)<;,<=,<.<=<<D><<@>=<<>=<<=<<=<#&'((''&%&'&'&&'(*(&'&('&')(( '& (''(('&'(('() ('('( '() ('( '(* ('( ' ('('&'+,('( '%*)('( '#*(('( ')('( '('(' '&#' '%#(!'( '&#'( '&#'( '&&$'( '&%%& '&' '&%& '&$&&' '&& '%$&&''& '(''& ')**(''&'',*)('&'( '&('&'( '+''&'('(/'& '&'('& '&%&' '%!'( '%"(')'%$'('(*'&"'&'&% '&'& '&%%20/ /.-*/0//.. /.-.14 / /. /.//11 /0 /. /0 /0 / /0 /./0 ././0/./ /020//./0 /./1./0 /./0 /0-./ /01./. /02!/. /0"/. /$/. /0%/. / /./.-, /.- /.//.-./.-% /-. /.-.--/./.,././/-*.//./.-,//./.,(300//0 /-10//0/0.0/0/0 /02/ /0// /010./ /14/ /13/0/0 /1.0/0/-.//01 //0 0/.=<=>=<=<=>@ <= <=>=> <= <= <; <=<< <; < <; <<= =< <=<=< <;?=<<=< <8<==< <;=< <<;<=< <= <; <>;<; <; <; <;"<; <;;$< <<;;$<= <; <=<<=; <><<=<<= <:9:<<=<=9=;;<=<= <?=< <=:>=<;:9<; <;6=<;<= <;=< <:?=< <=>=<<=AD;<;<=?A;<;<=C<;<=<= =<=< <=<= +>(>'>'>'>'>'r 2>0>/>/>/>/>/r >><><><><><><r >>F>l>q>a>(r : < E?>=<<;;;:99999999999;<;   ,-,,,-,-,,,----,-,--../00 1 1   .-..---.--.-.-....-..--..--)**++,,,,,++*)(        '(#'(1('(6+('(2,('(+)'(('('('('('('('('('('('('(('(''''''&'&'%!')('+)(',+)('-,('''(' '!'"'#'$')+*( '())'%'&./-('(')+,)'(53/)''(*-.,(')-./+53/)'&-58870')-///(./(20/(40/(20/(0/(/(/(/(/(/(/(/(/(/(/(/(/(/'/'/'/&/&/%!/!/0/0/10//// /!/"/#/$/0/%/1 /0/(31/0110/1+31/141/1<(<(<(<(<(<(<(<(<(<(<(<(<(<(<(<(<(<(<'<'<'<&<&<%!<!<!<!<!<<<< /0&/0/03&/0/03%/./!/./0/!/.//"/./0/"/0//01.//./0-.//./,-/./0/.,-/../1 +././. ,.//0. //0/0/,/013 /01./00//../02101./0/0<<<;);<<:86;<<;:;<<=<<==<<<<;9:<<;<<=9:;;<<7:;<=><8:;<><=9;.< >=:;<=,< =/<= ;0<= ;0<=;<=<<;<=<=;<=><;;<=><:9B@;==<=<;=;"<;'<=<;96<<;9'<;<;%<:;;<!<;:;<!<;:8=<"<:6>==<"<>=<<;:><<;<=<;><<;<<==<><<;<=>=<<> ?=<<<;<=> ?=<:;<;<>?== <=<;;6;<<;<;<><=<;;A=<; <=<:8 <=>BF=<=<;:;<:<=;:;<<=> ' ('&%' &'' ''( ''( '((')+'(' %')'(( %&'(') '''&( $'&% &%')'&&('( ('('()* $&''( &'' '' %&'&' '&'& '&'% '&'% '&'& ''(+( '').*( ''(*6)''( '('()('('('('&'&%'&&%'&%'&#'(("'(*'(,')*(('+('!('('"('('(&&'()*)%')('&'&'(()/. /./- // /0/ /0/ /.-,/.. 2/.,/-- /.-/( /.00/-/0 70/// 830/1/0/ 0/./. 100// 0//0 //0 /1/0 -/0/0-../0/0/./././0 /./100 /0/30/0 /0/.620 /./1-./0 /./. //. /,/*/.'/.$/01$/010/./01050/./2; /03/0/1/02!/03"/&0/.)0/././.< ;<<;= <<; <<; <=<; <;>< =<;=< <;<<:<= 6<<@ 79;<><< =<=<; >==<< =<< ;<;< <:;< ><;<=@>=<=<;<==<<; <<; <=<= <=<;;<;<= <=<;;<;74=<;<<= <=<;:;<<;<,<*<(<%<;:#<=>?=<=>@<=><=<<>;<=:;<;!:<;<;"<;&;<=>>);<;<=> Md&2 1Selection Mask!? " &  1[w؃؏؛ 1sw{׃ׇ׋׏דחכןףק׫ׯ׳׷׻׿ #'+/37;?CGKOSW[_cgkos @ @ @ @ @ @ @ @ @ @ @ @ Ld&2scikit-learn-contrib-imbalanced-learn-8504e95/doc/_static/img/logo_wide.png000066400000000000000000000566431521136113700266410ustar00rootroot00000000000000PNG  IHDR 1eiCCPICC profile(}=H@_S*vQP,HMP VhIC(X:8* nnN.RB L5;Ut".fs+bBӈIgS$<=||,s^%o2'0ݰ׉'7->q$xԠ ?r]vsagLz8L,XncV2T ∢j/d]V8oqVUּ'a0-/q X"R! a!JF4=?E.\`G*$5 17):_lcmv?WZ_SZZm-M.w']2$G )=no}>*y#E^xwW{oijrޡ2bKGD pHYs.#.#x?vtIME  ĚBAtEXtCommentCreated with GIMPW IDATxw]EsP $ -D"~UTlbd74 JU@P@D ` ׄ@;?g{m3{zdsfs|<3 B!B!BQk@&fMiִ`̆dMla<5Q`U1c+־dh1g@='BH!RLFډXJaW ee! BYS[eGkHt2E1iNc-cm:aE!BS7xY+O4l:cbD!DVC 4*(#pss=d! BdcFY'pY,6 n1[q#B!$@U.:.2f́c}uH-ù`ҮZB!$@UFԉڙuda>k2uƜE2B !DG-`YjYLtcB!$@ax֞-vh,_EjƘOw=+S!BTic-k l$,oA-s)BH!)6`O6E%cuL!BD@"u&yCB_s Zk/[69p4lBe!jΩ`Of.)BFNk'[k&xLfB!*\:l?mćf&3!HZ5q/b9 hED K0f2BJ!Ω g?-! `5 hD*sX 5D,l4f.ł!ʂ%D1tCCXkOC/D50KBQ)4JJ}(1͓>+S! 5e'kၶ)2B !ĈwL(Yl&kXqB mN\Bxs~%OmBT9zs U|L9k/B"22B BcH|BFBQ 6%D9Wd 7hlz>B!DhDtK|X [eB!"D1e { BD:s-,!B !Ω'*\TC B!$@q|BT k/#!Gǔ?: BHQs 佲B$ۇ 9u}65D*&3}YB!DhDDǔ-*KFB!"D)*kTBh HD| @)dL BD<`7bv B$\1I"9u>~2{2MI<Þ4B2?ǵe !RB_ǔqe Q4AfB B\`#AT]d! BT)}S%D1Q&B!"Duot}*c;@!'>Fp`ִ2B !G|n65D2Z BD*c.65DbaKYA!1q2p,!{ -d! B)ߕ%D "BD*4t.$@B !B1 O5bBHr FXw`ִFA!)cZd QCH!4B eQCaLKsj>7 G6`0 x x`7cnྨ ,XX Kׁǁn&T1]VՁ eȞwG6km,DuX3@4f^Jད=w+E {k{gC 'D G]]{I/7-yԯݞf2;{V6|6zQ3LfTco$& ?4'_x 8x0Ϥ(E`p5hW Y WIG1<t&?fWJ,HO@fݛqaw#-kH!g1 >c< |/#/~E_~ &+3Xg|"=B%Di|BC$Y[zSs).DHl{+xY>q ̀? x&:m_F uxspoJ6̉E!ZO\1ϣ~o$\CW[EsݶXٟ6~<W "DuLY8S5՘Tу|2hĽ;u0:&Chp.Y #jl#QK^Xp+_ĭ0iL_ \Qѵv'wW6ͣqn^PZQ[^ "D| ze*8O坃[ _}qmoN½/g3q.]!MO@6-862U{Me{. ]k,oFBGEAAD"Ҁ{#*DbILŹE|Dq OMqq 3ݦX>|'qIeҡWcŭpz%vhڵ7Ųp.&MQ>TO h'"Dh#!Rv495^_.wGqq#|r^f4u&ͷ`L& {\eZ ^&6|gEp~;Q[Zpw&=?1$_Eu_O2:mqXSpMҿRB6DQkfSX`Hȯp<T-+y oKm8ۉ.\E]bf1/D:ow9ZI( E12_f"uS=2 H >ZX{m&&ee Gk ~8IOx.P!ܱ.Cф[ELuɵrM G41538`_5 ;h,e+_98Da>2LF B}*Xvev1^“hRy}!3p"I2\zο6k͑.3W89CKſ|9gz۬Z4X[[V`V s|nBT17T?/P|wKQك93VkvOpU.1n5G|h]N{{ejBh$@(L|VYC4*T j˴3{a3iZeŹK9\b.X>!wqowKe.8z[-5%@{ d% a..iw_ʦ}?$>-{5zOFJ_6j) BTQZKq+KӸT& wXLF}e{!eeBJW<|661W m<ĉ Ή;Xaj<6^{rg. 0 9[?Ndr]oau-R]K)m@ <[o#n2uGg.̽~ v()ws=ke}s\Fb ~ߺ@՚s} \?vCNƓ-s|B勾% s0i^[(7b>CAGe~8o UO{ۆ)wylOeZu+$cϊʂ%G7^OC eryoy_}$@S !mʈl Je6D϶!>NhqXV [Bɸ"4I$J l{\u!s\>蜹lgh6 o#;%;8aX9.%ۚQ1XD:Ƽ+1f1fL<ݘ<6|p/o Ii S`vHk֔ YB)2A06`٣?POmxZyqU7:Ťd]SdzhcL%kU_X_U3]b-C{򁁹m~g.7BRTE.Xˎ{[%6(n'\JqYPVhLʫ)ת"ns\sVLp񲛨{[ɐ[[g2"-#hcf1L~xJ^m I\B;+\b> o{NUmsZ*m,=z`Rgϡ'UΩd QTdS'|k/u:\Wļmۦ~Cv;Y]}VudRgN.|` ];Xkn达gsݛ** ?I:|P&YhDaܑ|ǘxgwJRm |r픒gpO OP"ɫ*}vZ^7S \ї`Һ1da \V, ~k[ -y=1mƌjYKtU\b-%6쉙(9&2{&:0[,h~{RgT=_#;84RQ` UC3}wr,1yl7ܛUƤ/#s}Gz¿b> ǚ՗i ? Տf 15ySNW9#xaִ/Ԡ*rBgwȮ{b~3c*"ژH&Ij#wt.235>Ji2<]ȴe +io{A]U屼UȽJ~MOBLp+>:߹ u|M04d3qӦ1Zr}EYق(㷭vs|GN_pٸMGb .ӈ˞v$egFbN+p c/kȝJD$6$xx(淗;%62w.G_G=4Z{~^!Y;|K\O8 )dxI A6Un,mZ7/ﶱiq{^l |z&(໡3dCo1s[D^ |쓃 '/*c.1p#W/;1x_;@iS'@v䑆If/iebv0Ξdtf`kYSgFk`U'o3(3T-Sq)rO7\a~7? m 10Zf#f6&hdbRNǹWy6|3!c-VCqN3o9$Ds11W )u+K y6udhhғg9͌i>D(s>e)sE|!j)j1uT_ bqo=Gk7ܟMm M.:).5E=1I&ƸX{#.>ұOdh½MLyYNǁGQ7^oDmYՇGV(sNsSq띸[Gk=yFfج'јQ81vQϔ \3g[e'5qK+!JE1}`Mq&/y!Y\\Z8 ՔR!T#mV7JƤ 0@Ӣ,x.f",.oU\l_,?"wvx2nŭ c?ZCJ=OrZksLQ2ԎF F*JgeKժhJm^m3(x & ƾUn> <<FL'u[G2Eq,xQbEB=v q.T s+)_<|sΧ]ژ4pA?bCJz Z \,T/EmiM >]}r烇0ݙȹ.jCyJZ pkKioL{Yy;2\`p%cꡍ ™Uz,\:n VqeP8׽&V{Tɳܚܻ@b6{٤nmWNƀ0W \ɓ;Hes.>߉&BR[}nZ峧B]jcǁKr|\<}{R18XX0n7+^qXpY6\0@R(}ڇ'7uNOVSLUFft[iId6iqcWȝg蹺'0gR`A$Z&w|p.{θ5pYxq=3'=/X'+%X)6>mqY֌l] # '1_ npoJ{pG=-n dBBKc<`K\JёV8E[Ƚ-#YUdRgzڇɽ(9~'Ԍ SEo1jah~>H]6tn;GZgIw!K|a2C蜺6I븤HS IDATwD$|&0]A!D] _Q7'zp!|HaDjt2e|M޳4/P|SR~n B!j^zC|D( ^vQw,1mM~CSC ;[){6&HXB~kú+!y>T]ɚd mI(7|e*hEO B *^61T/OA%VL(coIXʷ̻R KZht!-@&u {̰Ҽi\*0p$>lsHcuQh2y@&BQbI  l,UV*LӤގoLcf4gL 6 1VsMc cqG1ibp Hj B!B r 9n';vQ?2\ސGVBQ33L_dڍR͚싊ߔBԌ=KWpqYuf j@sL .n!WMlWVj4d4 E1!#! ba}RƷ'&1 !!@0%We֬-U~Qg2Wd!!@RV5=vQ7 !=)R9kBTBԓ *P4e!$@zev:nkN =( B!I,+3_1L?EQ:A!D 2e#4K3\/3!I؋2ٳWX> l Im=+P%N:Y"Bԝ10OOuA[a̟d!u'@,?~u֠erj,!2UT Mz.{(B!U ,QT5QQP6s .HƘgukuڀM@nh9[]!)@ho[ WT`HT/{Bw__ٺ7fj`6ߍ1eW4҄BT0p4aW6JKVnZ-Y Bwl6\`Z|[bVKgȂl?<&{g aBj ֘)Oj-4w0@r}ߒ/cZJgC&~aXhiS߬Y($" O*G!=mP z &fZhǨ01cʅؗܮ[Bg-(+-vI6%'BAVA*Ose^6Xab7d|ojeN|_8qbg$!H^np`ׯ6[zI pxjcp؄oA[Y?+sjo&!/@0I5uEEY>x>yVo u+kI NS@ MSCq62A+_\-״w=ۜB ~YM!*]aU -ppCToe(믽)'Y $r'bYÕOǢgn2BԐ32=unxCkS~CcC\gdݲ෴wݣ;\Nl[!~o`$$@Tz^ ߩ^)g,)YFx ӧOo Tt p(p= `R^WHI/r5 ({㺊Ow t܎ #>*fϭ3 |:;\4Uj@pBQ#U&s2mt.Z|B1{z g]Z>@%G5MqHHDR!jH<0s`nIznoF@x埈/7baҁZgvP%Pӌn)퀝tU !DE`@][7 Hi쑭0|t>%%L'E]8qbgJ")|=:p gFBԐno{R$X\Mo<~VaLU_Ee_]|Y k8/nm2huxZ&Ps|-syVL,n3[gUqj6p qݩ;Bԁq"dU,u'CFX&4SVӆ||Zl,RVU.h9gN^=U5K,B+ BQ?gv:u[="+=~\u>K +l8ŽK4>ƾ{qF$s\Yw7tB: #p)EC&ay}[au[ݮ9Y6`jzE}W p֙s^/΀y:oqIBQov]KĘ+d^.FckW| ea0>?m9:" LUE|c !y=3w7 ,koFuF2c ʒ |LZ,a9vD~:s Ezԋ_T ^9 (gOk !D 7v$~(n^Øe M<ǴtէM"K)fsɡaફj%MfA(<# R!X܎oeUxm̐s1b~SnC/5pQ 7ԿؖB;wX tQ[ Xy5BQ=3'M0/uk~LY`+ MrG%7X!ԯތGPbgXcz>P^E|5Ə#~|T#Ն]i2 T7s2CS̷)0 c(;b7 {E %>=XsB@Qx+{;^wB &e}9izH@ lƬ uf+V1ĥmHU>UՐeQM+MVekX~$tGB2жΞLĺLf_}S~qbTެj]ٓRE jCs_P)`OimoPWֻsA).SO[w T !Ddꥡ=m̜t1pusGs&TT<ʦ[| .>w+$Uknn:FŇ>Ů~lC|H|!Htdɸ31fk4ˣ9d| Ԙ+7`gto[={uUwA!(S{d֫-njhl:wG;~Z[PZOkʴE:CD\m|ծLWyX !'>c$&m"!$j:zcdc9Uޯm0wa)?X`! pUJ~;XzØ~!}L_K)F+p::-uBd9gYhͨ\32 _W'KalK2(~_/^Й] ^ 17ϼ`ӧ7qCI'fΝlYF&Cx8#gW'ɉ$ž?Y><0̺X{`.?BI 5s=vRcq-]WOe`څB8UV_!e`ִM=BtclizB(87(| 8ܐՙe"!(|aibB$N/cG9WGL(>/qoB[Qu^!JsnXO`uY.ymi$:vOelZ7BLm Bk{}%FqɰGɌXE~.BH="q"!3tB0n9s%s~W 1_ B$mDE$BØ}WdpɘZgv].c1YYH#M%PD9"a2=eUc̡M-zU(.oB(\2cs; S֨ &LhiﺶtPϢbܯ#~os=BXLyeT Q̼miFUsLiujJ2߻~uzgw{?&f2,Ez<֞UËF0uee﬇ ݏ7|~0pH!JHϚ+ FYǦ?Wy{$, '>Q`'=" Zft=ܐ11b>ydsΒ'TD{55O20/ӿ5>6t[`+jo󁇀n܎$PCV f q`7ܪhk?p2A`,0f\ұFtݭ| <;6<#'6r["ƫzb)"$NBz/2.;6~#4Rxufד2Gcy u xn1+Ŷ2ypZq +&AkV>4OOC(t"L:M'/&iX^?V[&$fKڶg{[ BE䤯sX) Ҍ1?nn}*Z#y%y>?s-F#7&KjJ h33-;>?1Z sTW_~;g_p4x$Ml\/zR&_.pX0; 2)ѵ1[V6-.X8wǸ{qnhm% εXm_P>u`9k"p*-(l14e2ݫ}<u*dc&ovD_"m}+t~9} qYITwX@q|G?zu\LKMlN!ҏoϫ"qc)o!n3(\BI 蟵6;|r$DWƒј1W5Х]:8W8W>~*d\vZ8"3eJ]J<3.XיA"u}4,8Wr[ @ !D.lSp&ǀM\2C٬RN!o?[Y9>[2W\U$ +d Eq-8A"!WAdQe2p-pC4}%1\yʸ zgWy|־ہxۇq7>$b1\KG7evϢ+qWp8p"E]I 2뜲#cy#p1暖G5l+a\EΤ3 tڟw{ݐ21|&/\&׎^L&Uq 69ƉEr02GfMXk;lIy!^z^0M}9K5jyO#>qnYT/V>};|oBdm{es 'XE1i d |9܁[ ɖq)TgbIDATVca:.V3C(: H8ګ~{+`ߝWɘ7H&څZEԱ@bLfukX}}\<8\1ь nD .Ug?bO`,cx3^Θ-Wccb"+)lVDbba =KcSu~ $M&Ec2.e [Q<dG߬Gis7*t]B.}쑿cr+ObD:R`<9Fs}9W`e+"yoiem2%;_$pOܳ-sK^ј);<금c80Wyx.q)Dc.z (BDŖ8*Vxң=1${g~s}o P?؟הq^{}d~{)!$PtH:cq\|*)-|0<`{S]p|/u̷a#5zzܝ\?q7h-!\(:$PD*y=fڃ.Rb|43Y10R '=ut:%9 7Cγ 0n'Ӂ-z :$Pm|n\*ɧh]*־\d< xOnrQM}ƀ}[եsW.)@,|z :$P+0bC*ɺ 9KzHؘ"Y'>A1{ϋmJǩO!RBw2mJ׿}|BGѡC⤞8\c=(ӞO6!䳟ѷa"޼:'yEׇ? db2 !aCJMXys8c]-ӞP{{Ǯ@c4DQ]5PWlfG.)DcH (j~㒔2۱z{nם RUvo @u֘/"c@;G!:$PꉽI2z4Qq~~$~ nQmoب-!3֭{nĺJE<Sֻ<[(aK\~`_=Bcy"ڊC]q턨┾:'ͣZV2&lC!2ve/9α.˽o&ԗPصcE1?)_%p6\#ֱSN!!UD$Y Sֽ<5t\P28y e(Ik1L3nq{<`4Clੁ>+m}) N/ڷUB&'ԆF'pn w)-0y+ y8^Gu=389mR~d8 ?chCeTrR3i* F

#`=}ĶbvV==MD.Xf{%פ"侵@Ub́]m˳lDv SyG{6:BJs]Ňf4&@mVAw& ;ڬW+I7}7V=kJ80hJan < І .8S"[9O0<}796;&=ʟ'PEm^3+U-[|ֳϤOƽO܆Q:7め 9c<`O}7X`)`=}eq%@/j ;lX#M _?'4F%.?BzܤweJ?܎ǜ+DG<x{mp: Or7;5PxG|+ D ;τQT!x.eש/Ǥ/? ]$pv,pG'8x&.>wDZwy:Cݾ Ň pbpU\r6| qR|Bqq gfC%w-`v=e{>c^. Ĕ~ʺ@y_pY2\l/.BN@5*~Vf['}ި{)o78~c}f>ܭzb0 )@uܓM.A9gP zZ(@$BDK̀NI9<X}濄 u)m\VKl'w{4(.2{qk*-܅ys}B[kc8WBH|]B%WL|nd6 l⩮˸7ʅNNN]K &筕&@ '(Mlpnx* D ĭ+o)Ow>#5'R- s#t"2'Qۄ6M`B/ 8g4XO_8w!߬PwqoW>o gD [!<[Ufۗ\A3Εr. sEJtwzYLB6|sk[M>!E =)DAqo3r@ݸ-v!˂LXj p n IF(@~8 Ռs30nUP3& Rgy.oy>mzn@[\\Y] !$@F!kIfBrs4 -<. Oxfg\Moǽ_9͕Ye`ks#9u߻գ];ڣ=9[BsSj'TF\A&Í8 A[R's ONg8ћ"+.%셔4c*.viM)3Hy݅[m ߛ]. p3Dq{NyB\bKgˌh2 vOq#.hLyȿ3 ]pb=Zc;Bv0œK9\=#EznB}ǭ#̮9³,v[w6n3Wo~kB,8Ƣ̗l8RAoZCI0 :P<ʢ:): *bA*A娑4sa$r[uF};w]~]?́} ])۴)(/jUřlI.ǥX/ʪS]>V#ݍXawbLtW5A"0YSJw\!<3􁷋 [3yҶsJlkޥ[eW$\iHҽҌW&^|ܝUg 廙}Rӗ5_!OU X R]U˳Rnb-snX~m}2>>4^?40K"uz_'Oʴnz<]v5%;n$hh4:1$/3[,`ɔm}?+C(>v$ye徯OrHb%8h'kW/Tnӵczj1\}4n aY}WI^3PH+!LwƔ!+}÷$yQpι7M}~g (@XڗW$icǏ2ǶdrzfSWlsgs^]}!/0+Oa9O mqQefƿqg&|ʷ ,<~ٮ7ҞvP &2FYL9r[]}{ܐ={ƌUsh L)}lFln+I _vX{Khz\s$ϟxlq,?gI$W&0PLǢ $ Eo5Aޛr%m= =V_R?Ixޟr1̯Orvm\eR.rL\,?Tʊu\/Yr-FP)nyٛr~7|w֍Q{R$ݒ=]3.j~mH̶vC.<'e}?jIhcInNueIٷeޕrMKRc%-1Kٮ9ɫ2Sn<]'ɝ)[r)gmHrr-A:*lC})۬P7&yW֑M)8@ּ?lّZH#]a1 wmgw^K͹IUI:HWDLĮ ;#)+;{wrWdH$:j|ߖEy$>T+8㄀eF ,FRf~܂Ke-B AfX af,maa\ &YX$JL`0 s2AUI m΀ X XLVAP  M;`|Ӈ:`ȑlY IrIb*@Niz[.JV0s 4ݱ#[2 NoF3IENDB`scikit-learn-contrib-imbalanced-learn-8504e95/doc/_static/img/logo_wide_dark.png000066400000000000000000000625331521136113700276350ustar00rootroot00000000000000PNG  IHDR 1eiCCPICC profile(};HP>R*vP,:jP! :MGc⬫ >@]sB\s﹀QaT2Ʉͭ WCωb uOTwqg(y>xEAzNT,q.[UK!6g֒E7_3.9BH!*[xtL;detcNB!$@][}/"53XK{׳2B !DY>v?LEjw8}?d ! BwQ [;zH]8ee ! BfǸw eeɭsߐ)BT /D9meNI!D(Q[uN#>Gd ! Q#dw>,"VuR-ZgY$S!(7ڂ%D 9uwćuH$3!"D38{Zi8eb6mmZ S!('ZJ휶po9[C*&3!(7Z v8YCۍf4wD!DP,!vi:?> Yc?-S!(Q%wN[ݯ5D4it?]BQ"D7{$w+!b-lp BD1"S ?d /+!zvs%DuN;(W{&A!DU c6~b!bSO,B6BTB(>:~H HA!D9ж)!*O||A  ZO'S!(%ZYDmîhXB!$@W;~,!J B!$@C:~버(1hgBHQOwNE.=< BDP@Q.'#!G݇doA!.>:o'EeV B!J}Qb:{!*E`-sB!JV@()I|}7A!&>:N!*J B!JEL DIz@B!$@ f~$@!D,!X$Q9m u<39۱7 ?)dχ3YG{ %@:+K*`[A!D)"9& pd! BT)}S%D1Q&B!"Dui/u*ck@!'>6Fxce! BT X_U8w_[fB!"D1Հd Q8l*+!:&n%DudDFB!"D1uG۲$@BHQ|9 !BD+KA>LB!$@p FX{`FA!H_5X+H!Hh(K X, UJܽXsCCfV4m8_<_a+xF` gc~pu>\/_*hU]};`n(<7 z?sƸO >׻B1w?4: <n r c~[ae?_l8x.|$y1u] YB$UTݏ"jK.4?vSW*{ ӋX7}-b(npbt~uغlU߻*`U]}PTPdQ/1k2 pSs7qUb7/ 'Ųp"C1p_dz@D"#/A Qzlis[Do?.~Iu>t(6o'~~Y%dǏ7*o-[1d^HZ,{wǁ?P%@(}Se QXp%1Z Tpl(A \:7U^jl 9F":kP'o v`JBj"Xm@ vu !G}"@._+s/i%/JDqX(>Ǝpl }|l!z3`{f6gCb"&l=u ʴ8f|'6$wvӖ;DZ7lK AtIL(8R|TkNme&p}8 mB1-51-7EQkS_#SA$U VVY9?G+xp[>oN%0]*_w I^aF}05[#-FV%DuL,!j_Κ{jL$`,3K2[ÉX{_0YBiq9ڱ7r>ef̱} EMzf)e(pcydcfs,(Xƈ{fp`l:,X|2;ٿ lhfm}ϨW/5L99N?>e,of)>gfP9?PvLj=QZF J)?f">º=hfU&1jN.|- lofDb!$@M|VYC4^c~f Dr pݓ ,Hsd{ I?*I94; f 3M-x6N6mD@٣ 8&{G>r?UUBKُVN$@(#G sQTY}G٢^O"]N^ N"&ih7s#NepCbhEƖhBO(dbDr,p20Wl2#&dk Q=7l$6#d(4E@"%@B_@afe7F3ظl%3{B-.'y$w3ՙx?um{eg{.gd얛L 3bFgģPwJ o+nJ~f);{45qc=c ye/t睶0yӚi)K!*,;9"&c QKRm\Vu@P[†f+ u"}1Ẽ*Upt8>\挓:V~^[G;`c~!n/j{CȪ|BD+ˎW2U3jȔGࢥ7P]{[) s?>j±2[L+ UD78zY&?~gfV롍 P o+fvVZı̩ʯm>J{-~[b[8=z`rgaz&i;[n񊈒#rcReO8JZ#~w-}GͷDEo۪ܕ sndچ5 0 S ;{&5t3nWutgc=vU|+# ȍ$3oq 4A3p-rCM2o#&*,7=v+n{"sDR⦪H[G)>XFoʃO ^kÇe Q[(+T S=-+E˔ _b6"h)롍IO7f$V!=wL&1}OT!f쉘 kjY/kZfut;gޯ#=84Do UC3]w߂[a4ytq~s56*86"X ;߷ A#ر px _8^,=mݗ[ Տf ǚbzRrF!Yk1^@{Ҡ*زrbı;lx77b[9CCKm>Ir62;Og s"5d? yVhnɜTrw *Lt mݟ"sY&wtٳ޳{N lEOc1EL/rFbx1ZBg|fojPW;q"nwU/M HW.ꡍ @6fHP}Ǡ-nsQ1s!ߒ~/y 8'5?Lb[⣣4_.Hf8Kz&CC:1,51m:Z[N/(+ZmQT&g{\αgb(O#>F#ʜS~Q*ꠍy-8q7 3n3Zmo@2.% - QoLG ;{&&}͔1(+d3Rf廭)ݗl9|ohW7{QI4Sf Ǣ!/ng}zCv6E[+p}/RL?' 6p3W/#CH+}r; n"J;^~;~^iE#S8 *' vcnJZ7N4N!zGǣr^l|zqpz2롍%8e7jw_Wd8pUaN+Tx_,qʪᇉ= ߱%$N=0<<7*#K21=~zrg2E^S3GS57eesR5Q&~}3{%b2*Bt/Qfw < c7Lx\m,%aw9 _3wr(dГv'wO'ɜo9 D8gbT(2U{-suȗכO3$3T #E X S3Cwo‰*/%=r791|l`wum}w?a$6k4Wnq{w+yA-"&ȪaΒcVL21`qwIxm}uw!4:alKXup*wayh~+>g|7CDF=ɏJ/C{Srf,ɝ=v*sQ[V|=Le"21ܶb`p%~8MQ@  $7+FLx'{?"?](+QyF66h%Bw4{< mEqs>pECK=8}ē 3.!Xz>o٩ᶯBw/R)nKjR8]tvqJG0ޞ:.jC"{JZ[BX{++%mfsfv-O-~rٻX&AئA(ňqķ L7P; ]F<_"oX!@~ps2gHhO_2[ r~4j~0ԭup's7V@wA5ɛԛٙ9|D[D rA-솕~ LR7XL> @,~lif'٩>o(n%n7~o2W dl3K$7TEloa77muNGi!Uq/ IDAT*#5 |ZDxR ̼WύNdx_)Ww&><.lKy0l#+DDܞA~5` O9}39 9|k"JO8;VQVmG.l륨_ ;$MiaX7V; %x `k`S!C-$7`L#;ffoUٳ?DD,0hjl>I %@:b\|qr_m>gΚ;IO!+@,SO"G5 * FPVȗDC\T0 . Y'g?iFTD*ejeE<⚼5/P*'/J7a=BԼBO$%A.n%F{m7Yo45Th@#WYЅBԶ3`HAz/V^2=Eb[4#O <2Xň2B EZ'wv'3 x3Jr_iR1M}]I5^[ìAxB!j^k Jƪ`' 4K"S  aTHWS2go !*Ȟ7.iw#qKp#d 0*64ճ! f]\kęm+a=8Yp_R2Y}QV5s[2B \G5evwo}s"aC&BQMdAm뺷v%T!)ʌq .ú2MB kekq {ATA!D}Wr}Rj.roM'ܣ8B!DJ%ZFYA!D= {:{y\Ym (;KBԓ{}B\N'=\FFW]o B!I,ˊͨm `*?ڢ p 8~ƾup/2Bz(]ZeZ3Bԙ1 ^㢣!* A!D 2%{ind!u'@F毀o6}Y5hwh,!2V^d=\Zh{ȍKe!*@ jCKVvuBԥI.(;uJ#׵ΚB -T`c$@EZXNYflj50yiw3(GѠ&*uꂲӄ1mJKVG-Y \m l6Zx[/"V ldAߟh\u0B!D 7J]PSWkJ[eLKalHE6, -7# E7 B!FO{|^q[?W Ք@hnl|"ߗ[tO%I("S &%鬷BQ $xAj}L?͕z1k* ݐ{<:w6qĤ r_n(s]^JaQ_rbZjcpؒo??Υy)7B = (+[['}U[m+kq%ENq)iA%_: DmsB!$@Bz+ʸ:mX8J-0Vi-QM- D͙%~l=BH/e4?ӆ?\-Ba9?YGdZw?nli{&Bd%zۮAѰʆCK}6J$|* ]E{_e(_{&Wqvh&v4f÷uctd!!W2=unxCk+%Fc;'-2u3oimGOExw?@l },%$@ܯE e2}}oje$Bawzճp1J4̘11rݽs?&g8[BQgnuIy|u9z({(u8K20l1T5[gv=eJQwyPc \ "r6N+C!jDI.)v2Tds1.ղzW?|#w? B"~RB a؍Rmij$%ձ,AXjG`Y]/~ Դǹ߀g[Y>IwBԈ _ihHGy Ad N}K%,ün8qb/^.Yl'5$@5T},צ70gJU|?K%'۸Y -ǁ 5UѰHPLuM 'un,V31PE RC,w}C̭-]W}<\v[9H) aYި",0ļ\hnW)gp8~X9nKK 6s~;j!5&@Š,P`hj=9J˘'e[UO4cSӟC-K>%jmgk!5cڡb5w_Eo!1oE%z7hO.ѮVW)^6( FSq_3ݞ3J!Q>E䐞$KuMw1.p_x5VK,STa褟p`ӵ7Κ;\J[ds /'P#/F:̍BQg0Xݕl?}ŔQhjjG=)]`da-K+C QD!T&/6eL" r̼<mL p9V`b@폤Lr(_rK&M[JE/woӥ(~=/rq-Vn C묮ہ+eZ E ɵwts}%b.ˊd>uܡZQ-^{b̿쯹 ըS!@"dUlu]L)#l~TKVSBbY6P]mͷoEpm)*Κ;oKwoSqB3 Bԋ5V-_ 4;p<[vWXgn.|8kIy/:xE#1\u]#ГR!L"(2(inUcHi?dEogdt…VTޕm3uWj2gbƯ,W/ "DBz }?4˹ Z(K-m?fpʩB/P+_+^]Zj$Dm|-WI,7F5yYۗ#7[u֟*ꥫ+}8vBQ{^ٳ.gO&t 'Fͪ'zRE;hnW(k \ONSZԝ߸ۯ -S4HfzI! 'U/ io{gR Y_.Q䜱/D*aJW]|,Ⱦ.HVߋO֨'XV?} GC!$@}!5lQgA3۹r~Rv4۹Inj;E߶)zbY ;e9nۤv{#pL\6qV=x 0I XƚON]~=BϬokH{ȼeC/Kesb*nM {2r]^BRj(B ̞a: kn3hK{2E^dML}̾ǵpx-U/ !DE|,ONQgyS1eJ||KGAC!êIX뜶uf>& T܋fax* 3^$MZAIuCbYRl+Qy4pTEޜDKBQR/AQ5 o:k!-3.9 p]9Y(`f)ٰH!$@*JH!]aQm:2ha<8fciuB 0ٖib6Oꎾ7& R֨)^5O76YY$N( vScd~^Bx H^ZA3Zgy]FM0T*eK{5qX|?':9!HXln$'Pde}za_U?0:sd ^.#cfw{"~:Qۻi'+E֨^-\awexI`` pFr]^B   M_B*gY?n9an̑98/"o4smNydfz !D>^?Zfv=Ԑ)]#eGsyxBZVPsp~ 'kAKSfg6zZH_L6,m X*)slZr|(vX X='zwټg{> |p]M_@7p=Szm ҡGlbl30p?7p2703hfWVZ-Vw=5'$<IS0U+@$BI fLlN&;zR(16V wo:"NV*<~RDQ7jdlmGp`pe+a: @NO> 'x8^HONY cf0 Ʀ0 _V9$>jfH ik`;Y$]2;} 2GY^w{X_,\xX;)l&~ 0U8ZOvlj9֯8 8̉/v7&di70-y"จS.2J< dn??~c]_ V9 `o3{D@)pj"XV:p>);ufl$_}~g_pxh3+^c‰g _%#dž8I,DdfQ rdz9A ,h3,LfyHp4}Ҷ؁`b >LUU'@$BJ髹?Y`UXgJu9+—}|/(#o{8>]:8Xfo+x 5'_'zE!.~g'?&qȅߙ1 j3E ?هc~ x yWjfP=oJ-OH19u|9 8!OUKS_vQ9;wU~3HlafoW6E>p]Z-=6?Pu|,N$Uv h3(:}ȶt}%1tP\W*Y+p&*gsVH ?{==|r(DUFhLU'v)kyM" ȯ1 {_A6: ?2We̺*.v`>jgN"?N[gf0f] |<٘3xVфF⠗`%p5mca3TDDHX\49`*NBRveLEp|}/V';ɯ %yft8>.=Ne˝h%(vfWF‰wQE2p p]8}`uySCξkg)+F ?E>3l7Tܠ5P)DmfSO?S\\ifGX# bOsaJ2gf.u"y6,ƅb,ffׄ 0lFL{cfkY#+r843iBi,Ab,36zX(^)㇚ 'B%>z/bþGjN7_}t GߣgFuF`A/a`53[:w`큛}~fTeDSse?=** IDAT(|㸖eaL#+`\3A.x̶+>l"fX^OhLmۯ(;Qskbe  !qR8K.کi(;D񷱊Gofv[c(jW("&p52gZ!O6ٝ`KPQƵ[d CzCnP8+dWyD fUH@_&X1}H$m5 4(S ,`fK+!hlƙğKm(>vGN21J疔5 *ǂv] ǧͷ0<Ȣ 7e9q3r!ͫy͎~?Fdl? L|̾^f[ܜ$ȒU6#_ w^ dSF`":~Yd]Z(pafwdC :f3ۙz_7X;a}sDVULce@u,P:VuMq6߈`$Ձ1f )v{Ō qxZgV{O\,n61Vdas.m||afz^`KcS~6H&CvENN n$kG9YB;:fv}:.2_oߚٱ9\{J+ON%IZ+b$pT")>@$>Fl$>z rS|lF #qAB/k$hm"bU{䦸G1 nInIB^N%$@b'(B(sJk~Q| G> |G`Y"9!ڤ 1O(.F[RHܚg= X'!c 14pJY  !5BǰE$DĹ*/{/E=߽ǔ߿M&lffU\ W\NX1nGD#߀'O0˼)*9ڭR+eR.@B NDck;rqq"{K@1q%+vbVcbcߊ+'mfqnyu\D->~*Ab $@DlDET@8{H' Vo$:2!༘GHbGp9Ň]ηi;v ,y8Afq&hc7g Nd_x8=ݟ#7K(Oq"R@"5~V涟Oaט1aZ e% a<7)ܿ=3v4tz >8(67P(H y!U?aY%vo>6 BEH-(>He0ep8V,cldҸW?JWY 7 8`[\  %p AެL\ ̊>8vg$@DQ;!"f"J|bfK8f~^ '4qԣدb~C='{q* Il.\ ؓ xQ~Xx|)}C6W~|6Pڻ\p( !(@8Y43{>3_gf1"'O.L8Q>LJ,sU Vf pm'D[}P|D+9oBvXDL>\ b4ʈq"Rv.bfym [DX{3.O|A{K[%Tn:Idfw6KBFyLmIpK[ By@DbDyOD?I̶[~=&L/Ҟ7Y lo`즀m*>cfKKDm3`~s%%k%v(@JFg I8xA _(Ϙ+1ԣIE< |&x1KЕ_0A$c=^Khg vgG%HJpI BٱDy e%l3Vg @3[Tq';D8%r5*`5pe [2mH&\A[\S/MDY&]1>l-(ùt訹>t ٟ+(>$3{:l Sϟ2bE ,'̞.A_@Ϛ٦%Sc ̖D@}3zBm_` aS ofv @l:JR+U,D"US*E||"A8q/drmcʼn B|P%q}+Nu{_PX>p\Hƚ(tOkwJH|$mg QDM̒ Qז8I*!['Xf k1-)`| U!{r}?}/,a_P7=w2c_"HV77we8x O0KK"ʘP݆%@qr~wdM\w\ QG"$)af߮CQoyi)396A%U'PRM3[? Gs8\;IO(iGͬ %jR_-GNx7Z;(kT@@ϖ DAD0q1($Ey1&rqEEqE%# Ƞ"4S38[_s:ުw:w5*urb IbD̗lEBmy6ʝlfkRǭEKV"BiK06Pfr{{G3 "`#i͂ks(Ӈr&RC6$.'y*gY@KD#W":X&ef==$O u"ĔQxV:3[}@iw\laZ'[2Jgo` BSR z#"rDe.,Աs'K,#jˮ T )<" m~4I'g$865 2ƾYP18 &>det[^ ]g8@)KO83K Pl3[P[0>-v_ACY?hg6Hb(>/`@fQ칉DD]J,g_ٳ >{'@/'d+ٿ/{JZ+BJ3{b[Yټ7`Xkbf@v2) B Z iN粏ܿRXhmfN qu+ =*sTLD*nǡeT*`?Jp@bCDSQ@b@d)?%ٗ)jDRbg˞h>>\wBo X?̧A ;7x@D4{Y>g-JOH4;vj%|]SNxBq0(zX@ů? }(|fD{QPi^ioYD7g ;Aɞ8@ )P+3g7-k3xRKH\]|278ό.$=\ 0LD(@ u䪌#pݞ8wx݀UWƚY(+./X幼Sٹ=? +Y (jj6c~X~`Bf:#> ~u u1DP),d c= $mB@C})-<``-c;Ћ\VU=`Dm(_ASB nU NBr3g6fQIvAt^ U]ӝb'[#>)/4|}&ky53ܬTnotIϞ VхedAIeWIѳln1Cc|fvgUuLmP}䱬޾z<)7;EK"[UUUvHAI%x3aɤ[ {-3<S|.F})cG߉nCϞOrh cE$^ LBfݚi2{>kU0G͞>2TwE<.h-SCoB<1Y&veػ 1׋f12<4ҠkL22{)RAO!1H^lĝ8V;#(׏Hn(P$ `y%ۑ1 aϵ)>{=uV /)Ӯb1HbĔNNR_s`(I>Y-cHNOZ-}+2}9Oqv`%ۅe'9sԆ}-b]b!8 ~OH_TQDȸ8^U=`PUՁқ܇"l}ofW$܉+E$ IV$(+]g"ݗ1ʭ%mG6BeLaɾ$xΝ<#}% 4g !$>D'kI$/r#q?0*`xaXHKnю91m>5jj_݉^ݒ"f4}϶.ƴ_@Ř[BD ^ $/U-`g/s=83u0w{mG"}Nrz߿!gmI8$'X6sHTn$1tp~i6A\lCɃH^IrA,&٫_xsǘӋc?bU-t'DFB3Br 5l`C7!A<6$/uKYⲐdIO(׆mHF?X|C E^D]]HqqEϗ$>I=ǘ0Q13K%>|"q %$ϑ-M;R|\hIgNHaN-mITl(oGߌ3H\Qv&m^C}ݍ q7`<ֳ('.bV:"N}2x&8)z0LvA@t#qL0̖`l bx >qCtwGG;"ԯ/C,ηkfKxIr:_U-5OK|FB2f6/p=3vof5 mV$mHrDV~ `g{>u$TIH\E|Hl QVr<t fVۄ-нBٚ1aN?ɐ8>KK[dL3[׈c}Oq 6IWB$_AKDsč?k|)|x]w&>pY!0C ``=Pvćc:y,kyT "I"ܞCW8̮uIY\G4b])l0.41+gffngSzFY)˩|[|@=rӾ`?3lW/"D.dxMp`sCCu^PmfXh7fv~ 3[n`.,m޿5>t1(^ߧwtOIţ심x$@DdY'_IafULh5!ڻ$ 3!.Ft|Ͼ"relpP֍ ; 2OR?xcfYb%ܔ"O"ʎEIt&.$_eO{!!<سڬ䯒~ɕ#{<] I.N/Ԓdk%ڊޏ~`$;oK7ܔNrp¾X#9{\$IAZG:3ţ6 IYѩFsDt;Kf$ N7 `-C1^T` t:X` Y :kj'/AnԣiL r=u`y[VGxOKB-"BQ<DxO P!BDTHR.B!$@DQt/B !R2\EB!"wBP!"hVIf?K!B B!BDG#~QB!$@hP!BDT\Q!B !B!!B!DEJ ABB!DhD!BQ B!% DAGPB!!B!š= B!B !ĿY,!BH!B!fAB!"B! B!B !B!B!BdR3P P!Rlk !B! $W9| Bf)@)j7!BHk.04"!BJ^BOޘ image/svg+xml scikit-learn-contrib-imbalanced-learn-8504e95/doc/_static/index_examples.svg000066400000000000000000000047401521136113700271240ustar00rootroot00000000000000 image/svg+xml scikit-learn-contrib-imbalanced-learn-8504e95/doc/_static/index_getting_started.svg000066400000000000000000000076111521136113700304750ustar00rootroot00000000000000 image/svg+xml scikit-learn-contrib-imbalanced-learn-8504e95/doc/_static/index_user_guide.svg000066400000000000000000000144351521136113700274430ustar00rootroot00000000000000 image/svg+xml scikit-learn-contrib-imbalanced-learn-8504e95/doc/_static/js/000077500000000000000000000000001521136113700240055ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/doc/_static/js/copybutton.js000066400000000000000000000053631521136113700265600ustar00rootroot00000000000000$(document).ready(function() { /* Add a [>>>] button on the top-right corner of code samples to hide * the >>> and ... prompts and the output and thus make the code * copyable. */ var div = $('.highlight-python .highlight,' + '.highlight-python3 .highlight,' + '.highlight-pycon .highlight,' + '.highlight-default .highlight') var pre = div.find('pre'); // get the styles from the current theme pre.parent().parent().css('position', 'relative'); var hide_text = 'Hide the prompts and output'; var show_text = 'Show the prompts and output'; var border_width = pre.css('border-top-width'); var border_style = pre.css('border-top-style'); var border_color = pre.css('border-top-color'); var button_styles = { 'cursor':'pointer', 'position': 'absolute', 'top': '0', 'right': '0', 'border-color': border_color, 'border-style': border_style, 'border-width': border_width, 'color': border_color, 'text-size': '75%', 'font-family': 'monospace', 'padding-left': '0.2em', 'padding-right': '0.2em', 'border-radius': '0 3px 0 0' } // create and add the button to all the code blocks that contain >>> div.each(function(index) { var jthis = $(this); if (jthis.find('.gp').length > 0) { var button = $('>>>'); button.css(button_styles) button.attr('title', hide_text); button.data('hidden', 'false'); jthis.prepend(button); } // tracebacks (.gt) contain bare text elements that need to be // wrapped in a span to work with .nextUntil() (see later) jthis.find('pre:has(.gt)').contents().filter(function() { return ((this.nodeType == 3) && (this.data.trim().length > 0)); }).wrap(''); }); // define the behavior of the button when it's clicked $('.copybutton').click(function(e){ e.preventDefault(); var button = $(this); if (button.data('hidden') === 'false') { // hide the code output button.parent().find('.go, .gp, .gt').hide(); button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'hidden'); button.css('text-decoration', 'line-through'); button.attr('title', show_text); button.data('hidden', 'true'); } else { // show the code output button.parent().find('.go, .gp, .gt').show(); button.next('pre').find('.gt').nextUntil('.gp, .go').css('visibility', 'visible'); button.css('text-decoration', 'none'); button.attr('title', hide_text); button.data('hidden', 'false'); } }); }); scikit-learn-contrib-imbalanced-learn-8504e95/doc/_templates/000077500000000000000000000000001521136113700241005ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/doc/_templates/class.rst000066400000000000000000000007111521136113700257360ustar00rootroot00000000000000{{objname}} {{ underline }}============== .. currentmodule:: {{ module }} .. autoclass:: {{ objname }} {% block methods %} {% if methods %} .. rubric:: Methods .. autosummary:: {% for item in methods %} {% if '__init__' not in item %} ~{{ name }}.{{ item }} {% endif %} {%- endfor %} {% endif %} {% endblock %} .. include:: {{module}}.{{objname}}.examples .. raw:: html

scikit-learn-contrib-imbalanced-learn-8504e95/doc/_templates/function.rst000066400000000000000000000003231521136113700264550ustar00rootroot00000000000000{{objname}} {{ underline }}==================== .. currentmodule:: {{ module }} .. autofunction:: {{ objname }} .. include:: {{module}}.{{objname}}.examples .. raw:: html
scikit-learn-contrib-imbalanced-learn-8504e95/doc/_templates/numpydoc_docstring.rst000066400000000000000000000003261521136113700305450ustar00rootroot00000000000000{{index}} {{summary}} {{extended_summary}} {{parameters}} {{returns}} {{yields}} {{other_parameters}} {{attributes}} {{raises}} {{warns}} {{warnings}} {{see_also}} {{notes}} {{references}} {{examples}} {{methods}} scikit-learn-contrib-imbalanced-learn-8504e95/doc/_templates/sidebar-search-bs.html000066400000000000000000000005631521136113700302500ustar00rootroot00000000000000 scikit-learn-contrib-imbalanced-learn-8504e95/doc/about.rst000066400000000000000000000012131521136113700236040ustar00rootroot00000000000000About us ======== .. include:: ../AUTHORS.rst .. _citing-imbalanced-learn: Citing imbalanced-learn ----------------------- If you use imbalanced-learn in a scientific publication, we would appreciate citations to the following paper:: @article{JMLR:v18:16-365, author = {Guillaume Lema{{\^i}}tre and Fernando Nogueira and Christos K. Aridas}, title = {Imbalanced-learn: A Python Toolbox to Tackle the Curse of Imbalanced Datasets in Machine Learning}, journal = {Journal of Machine Learning Research}, year = {2017}, volume = {18}, number = {17}, pages = {1-5}, url = {http://jmlr.org/papers/v18/16-365.html} } scikit-learn-contrib-imbalanced-learn-8504e95/doc/bibtex/000077500000000000000000000000001521136113700232205ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/doc/bibtex/refs.bib000066400000000000000000000201731521136113700246400ustar00rootroot00000000000000@inproceedings{mani2003knn, title={kNN approach to unbalanced data distributions: a case study involving information extraction}, author={Mani, Inderjeet and Zhang, I}, booktitle={Proceedings of workshop on learning from imbalanced datasets}, volume={126}, year={2003} } @article{batista2004study, title={A study of the behavior of several methods for balancing machine learning training data}, author={Batista, Gustavo EAPA and Prati, Ronaldo C and Monard, Maria Carolina}, journal={ACM SIGKDD explorations newsletter}, volume={6}, number={1}, pages={20--29}, year={2004}, publisher={ACM} } @inproceedings{batista2003balancing, title={Balancing Training Data for Automated Annotation of Keywords: a Case Study.}, author={Batista, Gustavo EAPA and Bazzan, Ana LC and Monard, Maria Carolina}, booktitle={WOB}, pages={10--18}, year={2003} } @article{chen2004using, title={Using random forest to learn imbalanced data}, author={Chen, Chao and Liaw, Andy and Breiman, Leo and others}, journal={University of California, Berkeley}, volume={110}, number={1-12}, pages={24}, year={2004} } @article{liu2008exploratory, title={Exploratory undersampling for class-imbalance learning}, author={Liu, Xu-Ying and Wu, Jianxin and Zhou, Zhi-Hua}, journal={IEEE Transactions on Systems, Man, and Cybernetics, Part B (Cybernetics)}, volume={39}, number={2}, pages={539--550}, year={2008}, publisher={IEEE} } @article{seiffert2009rusboost, title={RUSBoost: A hybrid approach to alleviating class imbalance}, author={Seiffert, Chris and Khoshgoftaar, Taghi M and Van Hulse, Jason and Napolitano, Amri}, journal={IEEE Transactions on Systems, Man, and Cybernetics-Part A: Systems and Humans}, volume={40}, number={1}, pages={185--197}, year={2009}, publisher={IEEE} } @inproceedings{kubat1997addressing, title={Addressing the curse of imbalanced training sets: one-sided selection}, author={Kubat, Miroslav and Matwin, Stan and others}, booktitle={Icml}, volume={97}, pages={179--186}, year={1997}, organization={Nashville, USA} } @article{barandela2003strategies, title={Strategies for learning in class imbalance problems}, author={Barandela, Ricardo and S{\'a}nchez, Jos{\'e} Salvador and Garca, V and Rangel, Edgar}, journal={Pattern Recognition}, volume={36}, number={3}, pages={849--851}, year={2003}, publisher={Elsevier Science Publishing Company, Inc.} } @article{garcia2012effectiveness, title={On the effectiveness of preprocessing methods when dealing with different levels of class imbalance}, author={Garc{\'\i}a, Vicente and S{\'a}nchez, Jos{\'e} Salvador and Mollineda, Ram{\'o}n Alberto}, journal={Knowledge-Based Systems}, volume={25}, number={1}, pages={13--21}, year={2012}, publisher={Elsevier} } @inproceedings{he2008adasyn, title={ADASYN: Adaptive synthetic sampling approach for imbalanced learning}, author={He, Haibo and Bai, Yang and Garcia, Edwardo A and Li, Shutao}, booktitle={2008 IEEE International Joint Conference on Neural Networks (IEEE World Congress on Computational Intelligence)}, pages={1322--1328}, year={2008}, organization={IEEE} } @article{chawla2002smote, title={SMOTE: synthetic minority over-sampling technique}, author={Chawla, Nitesh V and Bowyer, Kevin W and Hall, Lawrence O and Kegelmeyer, W Philip}, journal={Journal of artificial intelligence research}, volume={16}, pages={321--357}, year={2002} } @inproceedings{han2005borderline, title={Borderline-SMOTE: a new over-sampling method in imbalanced data sets learning}, author={Han, Hui and Wang, Wen-Yuan and Mao, Bing-Huan}, booktitle={International conference on intelligent computing}, pages={878--887}, year={2005}, organization={Springer} } @inproceedings{nguyen2009borderline, title={Borderline over-sampling for imbalanced data classification}, author={Nguyen, Hien M and Cooper, Eric W and Kamei, Katsuari}, booktitle={Proceedings: Fifth International Workshop on Computational Intelligence \& Applications}, volume={2009}, number={1}, pages={24--29}, year={2009}, organization={IEEE SMC Hiroshima Chapter} } @article{last2017oversampling, title={Oversampling for Imbalanced Learning Based on K-Means and SMOTE}, author={Last, Felix and Douzas, Georgios and Bacao, Fernando}, journal={arXiv preprint arXiv:1711.00837}, year={2017} } @article{tomek1976two, title={Two modifications of CNN}, author={Tomek, Ivan}, journal={IEEE Trans. Systems, Man and Cybernetics}, volume={6}, pages={769--772}, year={1976} } @article{wilson1972asymptotic, title={Asymptotic properties of nearest neighbor rules using edited data}, author={Wilson, Dennis L}, journal={IEEE Transactions on Systems, Man, and Cybernetics}, number={3}, pages={408--421}, year={1972}, publisher={IEEE} } @article{tomek1976experiment, title={An experiment with the edited nearest-neighbor rule}, author={Tomek, Ivan}, journal={IEEE Transactions on systems, Man, and Cybernetics}, volume={6}, number={6}, pages={448--452}, year={1976} } @article{hart1968condensed, title={The condensed nearest neighbor rule (Corresp.)}, author={Hart, Peter}, journal={IEEE transactions on information theory}, volume={14}, number={3}, pages={515--516}, year={1968}, publisher={Citeseer} } @inproceedings{laurikkala2001improving, title={Improving identification of difficult small classes by balancing class distribution}, author={Laurikkala, Jorma}, booktitle={Conference on Artificial Intelligence in Medicine in Europe}, pages={63--66}, year={2001}, organization={Springer} } @article{smith2014instance, title={An instance level analysis of data complexity}, author={Smith, Michael R and Martinez, Tony and Giraud-Carrier, Christophe}, journal={Machine learning}, volume={95}, number={2}, pages={225--256}, year={2014}, publisher={Springer} } @article{torelli2014rose, author = {Menardi, Giovanna and Torelli, Nicola}, title={Training and assessing classification rules with imbalanced data}, journal={Data Mining and Knowledge Discovery}, volume={28}, pages={92-122}, year={2014}, publisher={Springer}, issue = {1}, issn = {1573-756X}, url = {https://doi.org/10.1007/s10618-012-0295-5}, doi = {10.1007/s10618-012-0295-5} } @article{esuli2009ordinal, author = {A. Esuli and S. Baccianella and F. Sebastiani}, title = {Evaluation Measures for Ordinal Regression}, journal = {Intelligent Systems Design and Applications, International Conference on}, year = {2009}, volume = {1}, issn = {}, pages = {283-287}, keywords = {ordinal regression;ordinal classification;evaluation measures;class imbalance;product reviews}, doi = {10.1109/ISDA.2009.230}, url = {https://doi.ieeecomputersociety.org/10.1109/ISDA.2009.230}, publisher = {IEEE Computer Society}, address = {Los Alamitos, CA, USA}, month = {dec} } @article{stanfill1986toward, title={Toward memory-based reasoning}, author={Stanfill, Craig and Waltz, David}, journal={Communications of the ACM}, volume={29}, number={12}, pages={1213--1228}, year={1986}, publisher={ACM New York, NY, USA} } @article{wilson1997improved, title={Improved heterogeneous distance functions}, author={Wilson, D Randall and Martinez, Tony R}, journal={Journal of artificial intelligence research}, volume={6}, pages={1--34}, year={1997} } @inproceedings{wang2009diversity, title={Diversity analysis on imbalanced data sets by using ensemble models}, author={Wang, Shuo and Yao, Xin}, booktitle={2009 IEEE symposium on computational intelligence and data mining}, pages={324--331}, year={2009}, organization={IEEE} } @article{hido2009roughly, title={Roughly balanced bagging for imbalanced data}, author={Hido, Shohei and Kashima, Hisashi and Takahashi, Yutaka}, journal={Statistical Analysis and Data Mining: The ASA Data Science Journal}, volume={2}, number={5-6}, pages={412--426}, year={2009}, publisher={Wiley Online Library} } @article{maclin1997empirical, title={An empirical evaluation of bagging and boosting}, author={Maclin, Richard and Opitz, David}, journal={AAAI/IAAI}, volume={1997}, pages={546--551}, year={1997} } scikit-learn-contrib-imbalanced-learn-8504e95/doc/combine.rst000066400000000000000000000044411521136113700241140ustar00rootroot00000000000000.. _combine: ======================================= Combination of over- and under-sampling ======================================= .. currentmodule:: imblearn.over_sampling We previously presented :class:`SMOTE` and showed that this method can generate noisy samples by interpolating new points between marginal outliers and inliers. This issue can be solved by cleaning the space resulting from over-sampling. .. currentmodule:: imblearn.combine In this regard, Tomek's link and edited nearest-neighbours are the two cleaning methods that have been added to the pipeline after applying SMOTE over-sampling to obtain a cleaner space. The two ready-to use classes imbalanced-learn implements for combining over- and undersampling methods are: (i) :class:`SMOTETomek` :cite:`batista2004study` and (ii) :class:`SMOTEENN` :cite:`batista2003balancing`. Those two classes can be used like any other sampler with parameters identical to their former samplers:: >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> X, y = make_classification(n_samples=5000, n_features=2, n_informative=2, ... n_redundant=0, n_repeated=0, n_classes=3, ... n_clusters_per_class=1, ... weights=[0.01, 0.05, 0.94], ... class_sep=0.8, random_state=0) >>> print(sorted(Counter(y).items())) [(0, 64), (1, 262), (2, 4674)] >>> from imblearn.combine import SMOTEENN >>> smote_enn = SMOTEENN(random_state=0) >>> X_resampled, y_resampled = smote_enn.fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 4060), (1, 4381), (2, 3502)] >>> from imblearn.combine import SMOTETomek >>> smote_tomek = SMOTETomek(random_state=0) >>> X_resampled, y_resampled = smote_tomek.fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 4499), (1, 4566), (2, 4413)] We can also see in the example below that :class:`SMOTEENN` tends to clean more noisy samples than :class:`SMOTETomek`. .. image:: ./auto_examples/combine/images/sphx_glr_plot_comparison_combine_001.png :target: ./auto_examples/combine/plot_comparison_combine.html :scale: 60 :align: center .. topic:: Examples * :ref:`sphx_glr_auto_examples_combine_plot_comparison_combine.py` scikit-learn-contrib-imbalanced-learn-8504e95/doc/common_pitfalls.rst000066400000000000000000000170421521136113700256670ustar00rootroot00000000000000.. _common_pitfalls: ========================================= Common pitfalls and recommended practices ========================================= This section is a complement to the documentation given `[here] `_ in scikit-learn. Indeed, we will highlight the issue of misusing resampling, leading to a **data leakage**. Due to this leakage, the performance of a model reported will be over-optimistic. Data leakage ============ As mentioned in the scikit-learn documentation, data leakage occurs when information that would not be available at prediction time is used when building the model. In the resampling setting, there is a common pitfall that corresponds to resample the **entire** dataset before splitting it into a train and a test partitions. Note that it would be equivalent to resample the train and test partitions as well. Such of a processing leads to two issues: * the model will not be tested on a dataset with class distribution similar to the real use-case. Indeed, by resampling the entire dataset, both the training and testing set will be potentially balanced while the model should be tested on the natural imbalanced dataset to evaluate the potential bias of the model; * the resampling procedure might use information about samples in the dataset to either generate or select some of the samples. Therefore, we might use information of samples which will be later used as testing samples which is the typical data leakage issue. We will demonstrate the wrong and right ways to do some sampling and emphasize the tools that one should use, avoiding to fall in the trap. We will use the adult census dataset. For the sake of simplicity, we will only use the numerical features. Also, we will make the dataset more imbalanced to increase the effect of the wrongdoings:: >>> from sklearn.datasets import fetch_openml >>> from imblearn.datasets import make_imbalance >>> X, y = fetch_openml( ... data_id=1119, as_frame=True, return_X_y=True ... ) >>> X = X.select_dtypes(include="number") >>> X, y = make_imbalance( ... X, y, sampling_strategy={">50K": 300}, random_state=1 ... ) Let's first check the balancing ratio on this dataset:: >>> from collections import Counter >>> {key: value / len(y) for key, value in Counter(y).items()} {'<=50K': 0.988..., '>50K': 0.011...} To later highlight some of the issue, we will keep aside a left-out set that we will not use for the evaluation of the model:: >>> from sklearn.model_selection import train_test_split >>> X, X_left_out, y, y_left_out = train_test_split( ... X, y, stratify=y, random_state=0 ... ) We will use a :class:`sklearn.ensemble.HistGradientBoostingClassifier` as a baseline classifier. First, we will train and check the performance of this classifier, without any preprocessing to alleviate the bias toward the majority class. We evaluate the generalization performance of the classifier via cross-validation:: >>> from sklearn.ensemble import HistGradientBoostingClassifier >>> from sklearn.model_selection import cross_validate >>> model = HistGradientBoostingClassifier(random_state=0) >>> cv_results = cross_validate( ... model, X, y, scoring="balanced_accuracy", ... return_train_score=True, return_estimator=True, ... n_jobs=-1 ... ) >>> print( ... f"Balanced accuracy mean +/- std. dev.: " ... f"{cv_results['test_score'].mean():.3f} +/- " ... f"{cv_results['test_score'].std():.3f}" ... ) Balanced accuracy mean +/- std. dev.: 0.609 +/- 0.024 We see that the classifier does not give good performance in terms of balanced accuracy mainly due to the class imbalance issue. In the cross-validation, we stored the different classifiers of all folds. We will show that evaluating these classifiers on the left-out data will give close statistical performance:: >>> import numpy as np >>> from sklearn.metrics import balanced_accuracy_score >>> scores = [] >>> for fold_id, cv_model in enumerate(cv_results["estimator"]): ... scores.append( ... balanced_accuracy_score( ... y_left_out, cv_model.predict(X_left_out) ... ) ... ) >>> print( ... f"Balanced accuracy mean +/- std. dev.: " ... f"{np.mean(scores):.3f} +/- {np.std(scores):.3f}" ... ) Balanced accuracy mean +/- std. dev.: 0.628 +/- 0.009 Let's now show the **wrong** pattern to apply when it comes to resampling to alleviate the class imbalance issue. We will use a sampler to balance the **entire** dataset and check the statistical performance of our classifier via cross-validation:: >>> from imblearn.under_sampling import RandomUnderSampler >>> sampler = RandomUnderSampler(random_state=0) >>> X_resampled, y_resampled = sampler.fit_resample(X, y) >>> model = HistGradientBoostingClassifier(random_state=0) >>> cv_results = cross_validate( ... model, X_resampled, y_resampled, scoring="balanced_accuracy", ... return_train_score=True, return_estimator=True, ... n_jobs=-1 ... ) >>> print( ... f"Balanced accuracy mean +/- std. dev.: " ... f"{cv_results['test_score'].mean():.3f} +/- " ... f"{cv_results['test_score'].std():.3f}" ... ) Balanced accuracy mean +/- std. dev.: 0.724 +/- 0.042 The cross-validation performance looks good, but evaluating the classifiers on the left-out data shows a different picture:: >>> scores = [] >>> for fold_id, cv_model in enumerate(cv_results["estimator"]): ... scores.append( ... balanced_accuracy_score( ... y_left_out, cv_model.predict(X_left_out) ... ) ... ) >>> print( ... f"Balanced accuracy mean +/- std. dev.: " ... f"{np.mean(scores):.3f} +/- {np.std(scores):.3f}" ... ) Balanced accuracy mean +/- std. dev.: 0.698 +/- 0.014 We see that the performance is now worse than the cross-validated performance. Indeed, the data leakage gave us too optimistic results due to the reason stated earlier in this section. We will now illustrate the correct pattern to use. Indeed, as in scikit-learn, using a :class:`~imblearn.pipeline.Pipeline` avoids to make any data leakage because the resampling will be delegated to imbalanced-learn and does not require any manual steps:: >>> from imblearn.pipeline import make_pipeline >>> model = make_pipeline( ... RandomUnderSampler(random_state=0), ... HistGradientBoostingClassifier(random_state=0) ... ) >>> cv_results = cross_validate( ... model, X, y, scoring="balanced_accuracy", ... return_train_score=True, return_estimator=True, ... n_jobs=-1 ... ) >>> print( ... f"Balanced accuracy mean +/- std. dev.: " ... f"{cv_results['test_score'].mean():.3f} +/- " ... f"{cv_results['test_score'].std():.3f}" ... ) Balanced accuracy mean +/- std. dev.: 0.732 +/- 0.019 We observe that we get good statistical performance as well. However, now we can check the performance of the model from each cross-validation fold to ensure that we have similar performance:: >>> scores = [] >>> for fold_id, cv_model in enumerate(cv_results["estimator"]): ... scores.append( ... balanced_accuracy_score( ... y_left_out, cv_model.predict(X_left_out) ... ) ... ) >>> print( ... f"Balanced accuracy mean +/- std. dev.: " ... f"{np.mean(scores):.3f} +/- {np.std(scores):.3f}" ... ) Balanced accuracy mean +/- std. dev.: 0.727 +/- 0.008 We see that the statistical performance are very close to the cross-validation study that we perform, without any sign of over-optimistic results. scikit-learn-contrib-imbalanced-learn-8504e95/doc/conf.py000066400000000000000000000240461521136113700232500ustar00rootroot00000000000000# # imbalanced-learn documentation build configuration file, created by # sphinx-quickstart on Mon Jan 18 14:44:12 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import os import sys from datetime import datetime from io import StringIO from pathlib import Path # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath("sphinxext")) from github_link import make_linkcode_resolve # noqa # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ "sphinx.ext.autodoc", "sphinx.ext.autosummary", "sphinx.ext.doctest", "sphinx.ext.intersphinx", "sphinx.ext.linkcode", "sphinxcontrib.bibtex", "numpydoc", "sphinx_issues", "sphinx_gallery.gen_gallery", "sphinx_copybutton", "sphinx_design", ] # Specify how to identify the prompt when copying code snippets copybutton_prompt_text = r">>> |\.\.\. " copybutton_prompt_is_regexp = True # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix of source filenames. source_suffix = ".rst" # The master toctree document. master_doc = "index" # General information about the project. project = "imbalanced-learn" copyright = f"2014-{datetime.now().year}, The imbalanced-learn developers" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. from imblearn import __version__ # noqa version = __version__ # The full version, including alpha/beta/rc tags. release = __version__ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ["_build", "_templates"] # The reST default role (used for this markup: `text`) to use for all # documents. default_role = "literal" # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = "pydata_sphinx_theme" html_title = f"Version {version}" html_favicon = "_static/img/favicon.ico" html_logo = "_static/img/logo_wide.png" html_style = "css/imbalanced-learn.css" html_css_files = [ "css/imbalanced-learn.css", ] html_sidebars = { "changelog": [], } html_theme_options = { "external_links": [], "github_url": "https://github.com/scikit-learn-contrib/imbalanced-learn", "use_edit_page_button": True, "show_toc_level": 1, # "navbar_align": "right", # For testing that the navbar items align properly "logo": { "image_dark": ( "https://imbalanced-learn.org/stable/_static/img/logo_wide_dark.png" ) }, } html_context = { "github_user": "scikit-learn-contrib", "github_repo": "imbalanced-learn", "github_version": "master", "doc_path": "doc", } # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] # Output file base name for HTML help builder. htmlhelp_basename = "imbalanced-learndoc" # -- Options for autodoc ------------------------------------------------------ autodoc_default_options = { "members": True, "inherited-members": True, } # generate autosummary even if no references autosummary_generate = True # -- Options for numpydoc ----------------------------------------------------- # this is needed for some reason... # see https://github.com/numpy/numpydoc/issues/69 numpydoc_show_class_members = False # -- Options for sphinxcontrib-bibtex ----------------------------------------- # bibtex file bibtex_bibfiles = ["bibtex/refs.bib"] # -- Options for intersphinx -------------------------------------------------- # intersphinx configuration intersphinx_mapping = { "python": (f"https://docs.python.org/{sys.version_info.major}", None), "numpy": ("https://numpy.org/doc/stable", None), "scipy": ("https://docs.scipy.org/doc/scipy/reference", None), "matplotlib": ("https://matplotlib.org/", None), "pandas": ("https://pandas.pydata.org/pandas-docs/stable/", None), "joblib": ("https://joblib.readthedocs.io/en/latest/", None), "seaborn": ("https://seaborn.pydata.org/", None), } # -- Options for sphinx-gallery ----------------------------------------------- # Generate the plot for the gallery plot_gallery = True # sphinx-gallery configuration sphinx_gallery_conf = { "doc_module": "imblearn", "backreferences_dir": os.path.join("references/generated"), "show_memory": True, "reference_url": {"imblearn": None}, } # -- Options for github link for what's new ----------------------------------- # Config for sphinx_issues issues_uri = "https://github.com/scikit-learn-contrib/imbalanced-learn/issues/{issue}" issues_github_path = "scikit-learn-contrib/imbalanced-learn" issues_user_uri = "https://github.com/{user}" # The following is used by sphinx.ext.linkcode to provide links to github linkcode_resolve = make_linkcode_resolve( "imblearn", ( "https://github.com/scikit-learn-contrib/" "imbalanced-learn/blob/{revision}/" "{package}/{path}#L{lineno}" ), ) # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ( "index", "imbalanced-learn.tex", "imbalanced-learn Documentation", "The imbalanced-learn developers", "manual", ), ] # -- Options for manual page output --------------------------------------- # If false, no module index is generated. # latex_domain_indices = True # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ( "index", "imbalanced-learn", "imbalanced-learn Documentation", ["The imbalanced-learn developers"], 1, ) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ( "index", "imbalanced-learn", "imbalanced-learn Documentation", "The imbalanced-learn developerss", "imbalanced-learn", "Toolbox for imbalanced dataset in machine learning.", "Miscellaneous", ), ] # -- Dependencies generation ---------------------------------------------- def generate_min_dependency_table(app): """Generate min dependency table for docs.""" from sklearn._min_dependencies import dependent_packages # get length of header package_header_len = max(len(package) for package in dependent_packages) + 4 version_header_len = len("Minimum Version") + 4 tags_header_len = max(len(tags) for _, tags in dependent_packages.values()) + 4 output = StringIO() output.write( " ".join( ["=" * package_header_len, "=" * version_header_len, "=" * tags_header_len] ) ) output.write("\n") dependency_title = "Dependency" version_title = "Minimum Version" tags_title = "Purpose" output.write( f"{dependency_title:<{package_header_len}} " f"{version_title:<{version_header_len}} " f"{tags_title}\n" ) output.write( " ".join( ["=" * package_header_len, "=" * version_header_len, "=" * tags_header_len] ) ) output.write("\n") for package, (version, tags) in dependent_packages.items(): output.write( f"{package:<{package_header_len}} {version:<{version_header_len}} {tags}\n" ) output.write( " ".join( ["=" * package_header_len, "=" * version_header_len, "=" * tags_header_len] ) ) output.write("\n") output = output.getvalue() with (Path(".") / "min_dependency_table.rst").open("w") as f: f.write(output) def generate_min_dependency_substitutions(app): """Generate min dependency substitutions for docs.""" from sklearn._min_dependencies import dependent_packages output = StringIO() for package, (version, _) in dependent_packages.items(): package = package.capitalize() output.write(f".. |{package}MinVersion| replace:: {version}") output.write("\n") output = output.getvalue() with (Path(".") / "min_dependency_substitutions.rst").open("w") as f: f.write(output) # -- Additional temporary hacks ----------------------------------------------- def setup(app): app.connect("builder-inited", generate_min_dependency_table) app.connect("builder-inited", generate_min_dependency_substitutions) scikit-learn-contrib-imbalanced-learn-8504e95/doc/datasets/000077500000000000000000000000001521136113700235535ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/doc/datasets/index.rst000066400000000000000000000176061521136113700254260ustar00rootroot00000000000000.. _datasets: ========================= Dataset loading utilities ========================= .. currentmodule:: imblearn.datasets The :mod:`imblearn.datasets` package is complementing the :mod:`sklearn.datasets` package. The package provides both: (i) a set of imbalanced datasets to perform systematic benchmark and (ii) a utility to create an imbalanced dataset from an original balanced dataset. .. _zenodo: Imbalanced datasets for benchmark ================================= :func:`fetch_datasets` allows to fetch 27 datasets which are imbalanced and binarized. The following data sets are available: +--+--------------+-------------------------------+-------+---------+-----+ |ID|Name | Repository & Target | Ratio | #S | #F | +==+==============+===============================+=======+=========+=====+ |1 |ecoli | UCI, target: imU | 8.6:1 | 336 | 7 | +--+--------------+-------------------------------+-------+---------+-----+ |2 |optical_digits| UCI, target: 8 | 9.1:1 | 5,620 | 64 | +--+--------------+-------------------------------+-------+---------+-----+ |3 |satimage | UCI, target: 4 | 9.3:1 | 6,435 | 36 | +--+--------------+-------------------------------+-------+---------+-----+ |4 |pen_digits | UCI, target: 5 | 9.4:1 | 10,992 | 16 | +--+--------------+-------------------------------+-------+---------+-----+ |5 |abalone | UCI, target: 7 | 9.7:1 | 4,177 | 10 | +--+--------------+-------------------------------+-------+---------+-----+ |6 |sick_euthyroid| UCI, target: sick euthyroid | 9.8:1 | 3,163 | 42 | +--+--------------+-------------------------------+-------+---------+-----+ |7 |spectrometer | UCI, target: >=44 | 11:1 | 531 | 93 | +--+--------------+-------------------------------+-------+---------+-----+ |8 |car_eval_34 | UCI, target: good, v good | 12:1 | 1,728 | 21 | +--+--------------+-------------------------------+-------+---------+-----+ |9 |isolet | UCI, target: A, B | 12:1 | 7,797 | 617 | +--+--------------+-------------------------------+-------+---------+-----+ |10|us_crime | UCI, target: >0.65 | 12:1 | 1,994 | 100 | +--+--------------+-------------------------------+-------+---------+-----+ |11|yeast_ml8 | LIBSVM, target: 8 | 13:1 | 2,417 | 103 | +--+--------------+-------------------------------+-------+---------+-----+ |12|scene | LIBSVM, target: >one label | 13:1 | 2,407 | 294 | +--+--------------+-------------------------------+-------+---------+-----+ |13|libras_move | UCI, target: 1 | 14:1 | 360 | 90 | +--+--------------+-------------------------------+-------+---------+-----+ |14|thyroid_sick | UCI, target: sick | 15:1 | 3,772 | 52 | +--+--------------+-------------------------------+-------+---------+-----+ |15|coil_2000 | KDD, CoIL, target: minority | 16:1 | 9,822 | 85 | +--+--------------+-------------------------------+-------+---------+-----+ |16|arrhythmia | UCI, target: 06 | 17:1 | 452 | 278 | +--+--------------+-------------------------------+-------+---------+-----+ |17|solar_flare_m0| UCI, target: M->0 | 19:1 | 1,389 | 32 | +--+--------------+-------------------------------+-------+---------+-----+ |18|oil | UCI, target: minority | 22:1 | 937 | 49 | +--+--------------+-------------------------------+-------+---------+-----+ |19|car_eval_4 | UCI, target: vgood | 26:1 | 1,728 | 21 | +--+--------------+-------------------------------+-------+---------+-----+ |20|wine_quality | UCI, wine, target: <=4 | 26:1 | 4,898 | 11 | +--+--------------+-------------------------------+-------+---------+-----+ |21|letter_img | UCI, target: Z | 26:1 | 20,000 | 16 | +--+--------------+-------------------------------+-------+---------+-----+ |22|yeast_me2 | UCI, target: ME2 | 28:1 | 1,484 | 8 | +--+--------------+-------------------------------+-------+---------+-----+ |23|webpage | LIBSVM, w7a, target: minority | 33:1 | 34,780 | 300 | +--+--------------+-------------------------------+-------+---------+-----+ |24|ozone_level | UCI, ozone, data | 34:1 | 2,536 | 72 | +--+--------------+-------------------------------+-------+---------+-----+ |25|mammography | UCI, target: minority | 42:1 | 11,183 | 6 | +--+--------------+-------------------------------+-------+---------+-----+ |26|protein_homo | KDD CUP 2004, minority | 11:1 | 145,751 | 74 | +--+--------------+-------------------------------+-------+---------+-----+ |27|abalone_19 | UCI, target: 19 | 130:1 | 4,177 | 10 | +--+--------------+-------------------------------+-------+---------+-----+ A specific data set can be selected as:: >>> from collections import Counter >>> from imblearn.datasets import fetch_datasets >>> ecoli = fetch_datasets()['ecoli'] >>> ecoli.data.shape (336, 7) >>> print(sorted(Counter(ecoli.target).items())) [(-1, 301), (1, 35)] .. _make_imbalanced: Imbalanced generator ==================== :func:`make_imbalance` turns an original dataset into an imbalanced dataset. This behaviour is driven by the parameter ``sampling_strategy`` which behave similarly to other resampling algorithm. ``sampling_strategy`` can be given as a dictionary where the key corresponds to the class and the value is the number of samples in the class:: >>> from sklearn.datasets import load_iris >>> from imblearn.datasets import make_imbalance >>> iris = load_iris() >>> sampling_strategy = {0: 20, 1: 30, 2: 40} >>> X_imb, y_imb = make_imbalance(iris.data, iris.target, ... sampling_strategy=sampling_strategy) >>> sorted(Counter(y_imb).items()) [(0, 20), (1, 30), (2, 40)] Note that all samples of a class are passed-through if the class is not mentioned in the dictionary:: >>> sampling_strategy = {0: 10} >>> X_imb, y_imb = make_imbalance(iris.data, iris.target, ... sampling_strategy=sampling_strategy) >>> sorted(Counter(y_imb).items()) [(0, 10), (1, 50), (2, 50)] Instead of a dictionary, a function can be defined and directly pass to ``sampling_strategy``:: >>> def ratio_multiplier(y): ... multiplier = {0: 0.5, 1: 0.7, 2: 0.95} ... target_stats = Counter(y) ... for key, value in target_stats.items(): ... target_stats[key] = int(value * multiplier[key]) ... return target_stats >>> X_imb, y_imb = make_imbalance(iris.data, iris.target, ... sampling_strategy=ratio_multiplier) >>> sorted(Counter(y_imb).items()) [(0, 25), (1, 35), (2, 47)] It would also work with pandas dataframe:: >>> from sklearn.datasets import fetch_openml >>> df, y = fetch_openml( ... 'iris', version=1, return_X_y=True, as_frame=True) >>> df_resampled, y_resampled = make_imbalance( ... df, y, sampling_strategy={'Iris-setosa': 10, 'Iris-versicolor': 20}, ... random_state=42) >>> df_resampled.head() sepallength sepalwidth petallength petalwidth 13 4.3 3.0 1.1 0.1 39 5.1 3.4 1.5 0.2 30 4.8 3.1 1.6 0.2 45 4.8 3.0 1.4 0.3 17 5.1 3.5 1.4 0.3 >>> Counter(y_resampled) Counter({'Iris-virginica': 50, 'Iris-versicolor': 20, 'Iris-setosa': 10}) See :ref:`sphx_glr_auto_examples_datasets_plot_make_imbalance.py` and :ref:`sphx_glr_auto_examples_api_plot_sampling_strategy_usage.py`. scikit-learn-contrib-imbalanced-learn-8504e95/doc/developers_utils.rst000066400000000000000000000162711521136113700260740ustar00rootroot00000000000000.. _developers-utils: =================== Developer guideline =================== Developer utilities ------------------- Imbalanced-learn contains a number of utilities to help with development. These are located in :mod:`imblearn.utils`, and include tools in a number of categories. All the following functions and classes are in the module :mod:`imblearn.utils`. .. warning :: These utilities are meant to be used internally within the imbalanced-learn package. They are not guaranteed to be stable between versions of imbalanced-learn. Backports, in particular, will be removed as the imbalanced-learn dependencies evolve. Validation Tools ~~~~~~~~~~~~~~~~ .. currentmodule:: imblearn.utils These are tools used to check and validate input. When you write a function which accepts arrays, matrices, or sparse matrices as arguments, the following should be used when applicable. - :func:`check_neighbors_object`: Check the objects is consistent to be a NN. - :func:`check_target_type`: Check the target types to be conform to the current samplers. - :func:`check_sampling_strategy`: Checks that sampling target is consistent with the type and return a dictionary containing each targeted class with its corresponding number of pixel. Deprecation ~~~~~~~~~~~ .. currentmodule:: imblearn.utils.deprecation .. warning :: Apart from :func:`deprecate_parameter` the rest of this section is taken from scikit-learn. Please refer to their original documentation. If any publicly accessible method, function, attribute or parameter is renamed, we still support the old one for two releases and issue a deprecation warning when it is called/passed/accessed. E.g., if the function ``zero_one`` is renamed to ``zero_one_loss``, we add the decorator ``deprecated`` (from ``sklearn.utils``) to ``zero_one`` and call ``zero_one_loss`` from that function:: from ..utils import deprecated def zero_one_loss(y_true, y_pred, normalize=True): # actual implementation pass @deprecated("Function 'zero_one' was renamed to 'zero_one_loss' " "in version 0.13 and will be removed in release 0.15. " "Default behavior is changed from 'normalize=False' to " "'normalize=True'") def zero_one(y_true, y_pred, normalize=False): return zero_one_loss(y_true, y_pred, normalize) If an attribute is to be deprecated, use the decorator ``deprecated`` on a property. E.g., renaming an attribute ``labels_`` to ``classes_`` can be done as:: @property @deprecated("Attribute labels_ was deprecated in version 0.13 and " "will be removed in 0.15. Use 'classes_' instead") def labels_(self): return self.classes_ If a parameter has to be deprecated, use ``FutureWarning`` appropriately. In the following example, k is deprecated and renamed to n_clusters:: import warnings def example_function(n_clusters=8, k=None): if k is not None: warnings.warn("'k' was renamed to n_clusters in version 0.13 and " "will be removed in 0.15.", DeprecationWarning) n_clusters = k As in these examples, the warning message should always give both the version in which the deprecation happened and the version in which the old behavior will be removed. If the deprecation happened in version 0.x-dev, the message should say deprecation occurred in version 0.x and the removal will be in 0.(x+2). For example, if the deprecation happened in version 0.18-dev, the message should say it happened in version 0.18 and the old behavior will be removed in version 0.20. In addition, a deprecation note should be added in the docstring, recalling the same information as the deprecation warning as explained above. Use the ``.. deprecated::`` directive:: .. deprecated:: 0.13 ``k`` was renamed to ``n_clusters`` in version 0.13 and will be removed in 0.15. On the top of all the functionality provided by scikit-learn. imbalanced-learn provides :func:`deprecate_parameter`: which is used to deprecate a sampler's parameter (attribute) by another one. Making a release ---------------- This section document the different steps that are necessary to make a new imbalanced-learn release. Major release ~~~~~~~~~~~~~ * Update the release note `whats_new/v0..rst` by giving a date and removing the status "Under development" from the title. * Run `bumpversion release`. It will remove the `dev0` tag. * Commit the change `git commit -am "bumpversion 0..0"` (e.g., `git commit -am "bumpversion 0.5.0"`). * Create a branch for this version (e.g., `git checkout -b 0..X`). * Push the new branch into the upstream remote imbalanced-learn repository. * Change the `symlink` in the `imbalanced-learn website repository `_ such that stable points to the latest release version, i.e, `0.`. To do this, clone the repository, `run unlink stable`, followed by `ln -s 0. stable`. To check that this was performed correctly, ensure that stable has the new version number using `ls -l`. * Return to your imbalanced-learn repository, in the branch `0..X`. * Create the source distribution and wheel: `python setup.py sdist` and `python setup.py bdist_wheel`. * Upload these file to PyPI using `twine upload dist/*` * Switch to the `master` branch and run `bumpversion minor`, commit and push on upstream. We are officially at `0..0.dev0`. * Create a GitHub release by clicking on "Draft a new release" here. "Tag version" should be the latest version number (e.g., `0..0`), "Target" should be the branch for that the release (e.g., `0..X`) and "Release title" should be "Version ". Add the notes from the release notes there. * Add a new `v0..rst` file in `doc/whats_new/` and `.. include::` this new file in `doc/whats_new.rst`. Mark the version as the version under development. * Finally, go to the `conda-forge feedstock `_ and a new PR will be created when the feedstock will synchronizing with the PyPI repository. Merge this PR such that we have the binary for `conda` available. Bug fix release ~~~~~~~~~~~~~~~ * Find the commit(s) hash of the bug fix commit you wish to back port using `git log`. * Checkout the branch for the lastest release, e.g., `git checkout 0..X`. * Append the bug fix commit(s) to the branch using `git cherry-pick `. Alternatively, you can use interactive rebasing from the `master` branch. * Bump the version number with bumpversion patch. This will bump the patch version, for example from `0.X.0` to `0.X.* dev0`. * Mark the current version as a release version (as opposed to `dev` version) with `bumpversion release --allow-dirty`. It will bump the version, for example from `0.X.* dev0` to `0.X.1`. * Commit the changes with `git commit -am 'bumpversion '`. * Push the changes to the release branch in upstream, e.g. `git push `. * Use the same process as in a major release to upload on PyPI and conda-forge. scikit-learn-contrib-imbalanced-learn-8504e95/doc/ensemble.rst000066400000000000000000000113361521136113700242730ustar00rootroot00000000000000.. _ensemble: ==================== Ensemble of samplers ==================== .. currentmodule:: imblearn.ensemble .. _ensemble_meta_estimators: Classifier including inner balancing samplers ============================================= .. _bagging: Bagging classifier ------------------ In ensemble classifiers, bagging methods build several estimators on different randomly selected subset of data. In scikit-learn, this classifier is named :class:`~sklearn.ensemble.BaggingClassifier`. However, this classifier does not allow each subset of data to be balanced. Therefore, when training on an imbalanced data set, this classifier will favor the majority classes:: >>> from sklearn.datasets import make_classification >>> X, y = make_classification(n_samples=10000, n_features=2, n_informative=2, ... n_redundant=0, n_repeated=0, n_classes=3, ... n_clusters_per_class=1, ... weights=[0.01, 0.05, 0.94], class_sep=0.8, ... random_state=0) >>> from sklearn.model_selection import train_test_split >>> from sklearn.metrics import balanced_accuracy_score >>> from sklearn.ensemble import BaggingClassifier >>> from sklearn.tree import DecisionTreeClassifier >>> X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) >>> bc = BaggingClassifier(DecisionTreeClassifier(), random_state=0) >>> bc.fit(X_train, y_train) #doctest: BaggingClassifier(...) >>> y_pred = bc.predict(X_test) >>> balanced_accuracy_score(y_test, y_pred) 0.77... In :class:`BalancedBaggingClassifier`, each bootstrap sample will be further resampled to achieve the `sampling_strategy` desired. Therefore, :class:`BalancedBaggingClassifier` takes the same parameters as the scikit-learn :class:`~sklearn.ensemble.BaggingClassifier`. In addition, the sampling is controlled by the parameter `sampler` or the two parameters `sampling_strategy` and `replacement`, if one wants to use the :class:`~imblearn.under_sampling.RandomUnderSampler`:: >>> from imblearn.ensemble import BalancedBaggingClassifier >>> bbc = BalancedBaggingClassifier(DecisionTreeClassifier(), ... sampling_strategy='auto', ... replacement=False, ... random_state=0) >>> bbc.fit(X_train, y_train) BalancedBaggingClassifier(...) >>> y_pred = bbc.predict(X_test) >>> balanced_accuracy_score(y_test, y_pred) 0.8... Changing the `sampler` will give rise to different known implementations :cite:`maclin1997empirical`, :cite:`hido2009roughly`, :cite:`wang2009diversity`. You can refer to the following example which shows these different methods in practice: :ref:`sphx_glr_auto_examples_ensemble_plot_bagging_classifier.py` .. _forest: Forest of randomized trees -------------------------- :class:`BalancedRandomForestClassifier` is another ensemble method in which each tree of the forest will be provided a balanced bootstrap sample :cite:`chen2004using`. This class provides all functionality of the :class:`~sklearn.ensemble.RandomForestClassifier`:: >>> from imblearn.ensemble import BalancedRandomForestClassifier >>> brf = BalancedRandomForestClassifier( ... n_estimators=100, random_state=0, sampling_strategy="all", replacement=True, ... bootstrap=False, ... ) >>> brf.fit(X_train, y_train) BalancedRandomForestClassifier(...) >>> y_pred = brf.predict(X_test) >>> balanced_accuracy_score(y_test, y_pred) 0.8... .. _boosting: Boosting -------- Several methods taking advantage of boosting have been designed. :class:`RUSBoostClassifier` randomly under-samples the dataset before performing a boosting iteration :cite:`seiffert2009rusboost`:: >>> from imblearn.ensemble import RUSBoostClassifier >>> rusboost = RUSBoostClassifier(n_estimators=200, random_state=0) >>> rusboost.fit(X_train, y_train) RUSBoostClassifier(...) >>> y_pred = rusboost.predict(X_test) >>> balanced_accuracy_score(y_test, y_pred) 0... A specific method which uses :class:`~sklearn.ensemble.AdaBoostClassifier` as learners in the bagging classifier is called "EasyEnsemble". The :class:`EasyEnsembleClassifier` allows bagging AdaBoost learners which are trained on balanced bootstrap samples :cite:`liu2008exploratory`. Similarly to the :class:`BalancedBaggingClassifier` API, one can construct the ensemble as:: >>> from imblearn.ensemble import EasyEnsembleClassifier >>> eec = EasyEnsembleClassifier(random_state=0) >>> eec.fit(X_train, y_train) EasyEnsembleClassifier(...) >>> y_pred = eec.predict(X_test) >>> balanced_accuracy_score(y_test, y_pred) 0.6... .. topic:: Examples * :ref:`sphx_glr_auto_examples_ensemble_plot_comparison_ensemble_classifier.py` scikit-learn-contrib-imbalanced-learn-8504e95/doc/index.rst000066400000000000000000000060011521136113700236010ustar00rootroot00000000000000.. project-template documentation master file, created by sphinx-quickstart on Mon Jan 18 14:44:12 2016. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. :notoc: ############################## imbalanced-learn documentation ############################## **Date**: |today| **Version**: |version| **Useful links**: `Binary Installers `__ | `Source Repository `__ | `Issues & Ideas `__ | `Q&A Support `__ Imbalanced-learn (imported as :mod:`imblearn`) is an open source, MIT-licensed library relying on scikit-learn (imported as :mod:`sklearn`) and provides tools when dealing with classification with imbalanced classes. .. grid:: 1 2 2 2 :gutter: 4 :padding: 2 2 0 0 :class-container: sd-text-center .. grid-item-card:: Getting started :img-top: _static/index_getting_started.svg :class-card: intro-card :shadow: md Check out the getting started guides to install `imbalanced-learn`. Some extra information to get started with a new contribution is also provided. +++ .. button-ref:: getting_started :ref-type: ref :click-parent: :color: secondary :expand: To the installation guideline .. grid-item-card:: User guide :img-top: _static/index_user_guide.svg :class-card: intro-card :shadow: md The user guide provides in-depth information on the key concepts of `imbalanced-learn` with useful background information and explanation. +++ .. button-ref:: user_guide :ref-type: ref :click-parent: :color: secondary :expand: To the user guide .. grid-item-card:: API reference :img-top: _static/index_api.svg :class-card: intro-card :shadow: md The reference guide contains a detailed description of the `imbalanced-learn` API. To known more about methods parameters. +++ .. button-ref:: api :ref-type: ref :click-parent: :color: secondary :expand: To the reference guide .. grid-item-card:: Examples :img-top: _static/index_examples.svg :class-card: intro-card :shadow: md The gallery of examples is a good place to see `imbalanced-learn` in action. Select an example and dive in. +++ .. button-ref:: general_examples :ref-type: ref :click-parent: :color: secondary :expand: To the gallery of examples .. toctree:: :maxdepth: 3 :hidden: :titlesonly: install user_guide references/index auto_examples/index whats_new about scikit-learn-contrib-imbalanced-learn-8504e95/doc/install.rst000066400000000000000000000064261521136113700241530ustar00rootroot00000000000000.. _getting_started: ############### Getting Started ############### Prerequisites ============= .. |PythonMinVersion| replace:: 3.10 .. |NumPyMinVersion| replace:: 1.25.2 .. |SciPyMinVersion| replace:: 1.11.4 .. |ScikitLearnMinVersion| replace:: 1.4.2 .. |MatplotlibMinVersion| replace:: 3.7.3 .. |PandasMinVersion| replace:: 2.0.3 .. |TensorflowMinVersion| replace:: 2.16.1 .. |KerasMinVersion| replace:: 3.3.3 .. |SeabornMinVersion| replace:: 0.12.2 .. |PytestMinVersion| replace:: 7.2.2 `imbalanced-learn` requires the following dependencies: - Python (>= |PythonMinVersion|) - NumPy (>= |NumPyMinVersion|) - SciPy (>= |SciPyMinVersion|) - Scikit-learn (>= |ScikitLearnMinVersion|) - Pytest (>= |PytestMinVersion|) Additionally, `imbalanced-learn` requires the following optional dependencies: - Pandas (>= |PandasMinVersion|) for dealing with dataframes - Tensorflow (>= |TensorflowMinVersion|) for dealing with TensorFlow models - Keras (>= |KerasMinVersion|) for dealing with Keras models The examples will requires the following additional dependencies: - Matplotlib (>= |MatplotlibMinVersion|) - Seaborn (>= |SeabornMinVersion|) Install ======= From PyPi or conda-forge repositories ------------------------------------- imbalanced-learn is currently available on the PyPi's repositories and you can install it via `pip`:: pip install imbalanced-learn The package is released also on the conda-forge repositories and you can install it with `conda` (or `mamba`):: conda install -c conda-forge imbalanced-learn Intel optimizations via scikit-learn-intelex -------------------------------------------- Imbalanced-learn relies entirely on scikit-learn algorithms. Intel provides an optimized version of scikit-learn for Intel hardwares, called scikit-learn-intelex. Installing scikit-learn-intelex and patching scikit-learn will activate the Intel optimizations. You can refer to the following `blog post `_ for some benchmarks. Refer to the following documentation for instructions: - `Installation guide `_. - `Patching guide `_. From source available on GitHub ------------------------------- If you prefer, you can clone it and run the setup.py file. Use the following commands to get a copy from Github and install all dependencies:: git clone https://github.com/scikit-learn-contrib/imbalanced-learn.git cd imbalanced-learn pip install . Be aware that you can install in developer mode with:: pip install --no-build-isolation --editable . If you wish to make pull-requests on GitHub, we advise you to install pre-commit:: pip install pre-commit pre-commit install Test and coverage ================= You want to test the code before to install:: $ make test You wish to test the coverage of your version:: $ make coverage You can also use `pytest`:: $ pytest imblearn -v Contribute ========== You can contribute to this code through Pull Request on GitHub_. Please, make sure that your code is coming with unit tests to ensure full coverage and continuous integration in the API. .. _GitHub: https://github.com/scikit-learn-contrib/imbalanced-learn/pulls scikit-learn-contrib-imbalanced-learn-8504e95/doc/introduction.rst000066400000000000000000000061741521136113700252260ustar00rootroot00000000000000.. _introduction: ============ Introduction ============ .. _api_imblearn: API's of imbalanced-learn samplers ---------------------------------- The available samplers follow the `scikit-learn API `_ using the base estimator and incorporating a sampling functionality via the ``sample`` method: :Estimator: The base object, implements a ``fit`` method to learn from data:: estimator = obj.fit(data, targets) :Resampler: To resample a data sets, each sampler implements a ``fit_resample`` method:: data_resampled, targets_resampled = obj.fit_resample(data, targets) Imbalanced-learn samplers accept the same inputs as scikit-learn estimators: * `data`, 2-dimensional array-like structures, such as: * Python's list of lists :class:`list`, * Numpy arrays :class:`numpy.ndarray`, * Panda dataframes :class:`pandas.DataFrame`, * Scipy sparse matrices :class:`scipy.sparse.csr_matrix` or :class:`scipy.sparse.csc_matrix`; * `targets`, 1-dimensional array-like structures, such as: * Numpy arrays :class:`numpy.ndarray`, * Pandas series :class:`pandas.Series`. The output will be of the following type: * `data_resampled`, 2-dimensional aray-like structures, such as: * Numpy arrays :class:`numpy.ndarray`, * Pandas dataframes :class:`pandas.DataFrame`, * Scipy sparse matrices :class:`scipy.sparse.csr_matrix` or :class:`scipy.sparse.csc_matrix`; * `targets_resampled`, 1-dimensional array-like structures, such as: * Numpy arrays :class:`numpy.ndarray`, * Pandas series :class:`pandas.Series`. .. topic:: Pandas in/out Unlike scikit-learn, imbalanced-learn provides support for pandas in/out. Therefore providing a dataframe, will output as well a dataframe. .. topic:: Sparse input For sparse input the data is **converted to the Compressed Sparse Rows representation** (see ``scipy.sparse.csr_matrix``) before being fed to the sampler. To avoid unnecessary memory copies, it is recommended to choose the CSR representation upstream. .. _problem_statement: Problem statement regarding imbalanced data sets ------------------------------------------------ The learning and prediction phrases of machine learning algorithms can be impacted by the issue of **imbalanced datasets**. This imbalance refers to the difference in the number of samples across different classes. We demonstrate the effect of training a `Logistic Regression classifier `_ with varying levels of class balancing by adjusting their weights. .. image:: ./auto_examples/over-sampling/images/sphx_glr_plot_comparison_over_sampling_001.png :target: ./auto_examples/over-sampling/plot_comparison_over_sampling.html :scale: 60 :align: center As expected, the decision function of the Logistic Regression classifier varies significantly depending on how imbalanced the data is. With a greater imbalance ratio, the decision function tends to favour the class with the larger number of samples, usually referred to as the **majority class**. scikit-learn-contrib-imbalanced-learn-8504e95/doc/make.bat000066400000000000000000000151011521136113700233460ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\imbalanced-learn.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\imbalanced-learn.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end scikit-learn-contrib-imbalanced-learn-8504e95/doc/metrics.rst000066400000000000000000000132101521136113700241400ustar00rootroot00000000000000.. _metrics: ======= Metrics ======= .. currentmodule:: imblearn.metrics Classification metrics ---------------------- Currently, scikit-learn only offers the ``sklearn.metrics.balanced_accuracy_score`` (in 0.20) as metric to deal with imbalanced datasets. The module :mod:`imblearn.metrics` offers a couple of other metrics which are used in the literature to evaluate the quality of classifiers. .. _sensitivity_specificity: Sensitivity and specificity metrics ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sensitivity and specificity are metrics which are well known in medical imaging. Sensitivity (also called true positive rate or recall) is the proportion of the positive samples which is well classified while specificity (also called true negative rate) is the proportion of the negative samples which are well classified. Therefore, depending of the field of application, either the sensitivity/specificity or the precision/recall pair of metrics are used. Currently, only the `precision and recall metrics `_ are implemented in scikit-learn. :func:`sensitivity_specificity_support`, :func:`sensitivity_score`, and :func:`specificity_score` add the possibility to use those metrics. .. _imbalanced_metrics: Additional metrics specific to imbalanced datasets ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The :func:`geometric_mean_score` :cite:`barandela2003strategies,kubat1997addressing` is the root of the product of class-wise sensitivity. This measure tries to maximize the accuracy on each of the classes while keeping these accuracies balanced. The :func:`make_index_balanced_accuracy` :cite:`garcia2012effectiveness` can wrap any metric and give more importance to a specific class using the parameter ``alpha``. .. _macro_averaged_mean_absolute_error: Macro-Averaged Mean Absolute Error (MA-MAE) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Ordinal classification is used when there is a rank among classes, for example levels of functionality or movie ratings. The :func:`macro_averaged_mean_absolute_error` :cite:`esuli2009ordinal` is used for imbalanced ordinal classification. The mean absolute error is computed for each class and averaged over classes, giving an equal weight to each class. .. _classification_report: Summary of important metrics ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The :func:`classification_report_imbalanced` will compute a set of metrics per class and summarize it in a table. The parameter `output_dict` allows to get a string or a Python dictionary. This dictionary can be reused to create a Pandas dataframe for instance. The bottom row (i.e "avg/total") contains the weighted average by the support (i.e column "sup") of each column. Note that the weighted average of the class recalls is also known as the classification accuracy. .. _pairwise_metrics: Pairwise metrics ---------------- The :mod:`imblearn.metrics.pairwise` submodule implements pairwise distances that are available in scikit-learn while used in some of the methods in imbalanced-learn. .. _vdm: Value Difference Metric ~~~~~~~~~~~~~~~~~~~~~~~ The class :class:`~imblearn.metrics.pairwise.ValueDifferenceMetric` is implementing the Value Difference Metric proposed in :cite:`stanfill1986toward`. This measure is used to compute the proximity of two samples composed of only categorical values. Given a single feature, categories with similar correlation with the target vector will be considered closer. Let's give an example to illustrate this behaviour as given in :cite:`wilson1997improved`. `X` will be represented by a single feature which will be some color and the target will be if a sample is whether or not an apple:: >>> import numpy as np >>> X = np.array(["green"] * 10 + ["red"] * 10 + ["blue"] * 10).reshape(-1, 1) >>> y = ["apple"] * 8 + ["not apple"] * 5 + ["apple"] * 7 + ["not apple"] * 9 + ["apple"] In this dataset, the categories "red" and "green" are more correlated to the target `y` and should have a smaller distance than with the category "blue". We should this behaviour. Be aware that we need to encode the `X` to work with numerical values:: >>> from sklearn.preprocessing import OrdinalEncoder >>> encoder = OrdinalEncoder(dtype=np.int32) >>> X_encoded = encoder.fit_transform(X) Now, we can compute the distance between three different samples representing the different categories:: >>> from imblearn.metrics.pairwise import ValueDifferenceMetric >>> vdm = ValueDifferenceMetric().fit(X_encoded, y) >>> X_test = np.array(["green", "red", "blue"]).reshape(-1, 1) >>> X_test_encoded = encoder.transform(X_test) >>> vdm.pairwise(X_test_encoded) array([[0. , 0.04, 1.96], [0.04, 0. , 1.44], [1.96, 1.44, 0. ]]) We see that the minimum distance happen when the categories "red" and "green" are compared. Whenever comparing with "blue", the distance is much larger. **Mathematical formulation** The distance between feature values of two samples is defined as: .. math:: \delta(x, y) = \sum_{c=1}^{C} |p(c|x_{f}) - p(c|y_{f})|^{k} \ , where :math:`x` and :math:`y` are two samples and :math:`f` a given feature, :math:`C` is the number of classes, :math:`p(c|x_{f})` is the conditional probability that the output class is :math:`c` given that the feature value :math:`f` has the value :math:`x` and :math:`k` an exponent usually defined to 1 or 2. The distance for the feature vectors :math:`X` and :math:`Y` is subsequently defined as: .. math:: \Delta(X, Y) = \sum_{f=1}^{F} \delta(X_{f}, Y_{f})^{r} \ , where :math:`F` is the number of feature and :math:`r` an exponent usually defined equal to 1 or 2. scikit-learn-contrib-imbalanced-learn-8504e95/doc/miscellaneous.rst000066400000000000000000000165211521136113700253450ustar00rootroot00000000000000.. _miscellaneous: ====================== Miscellaneous samplers ====================== .. currentmodule:: imblearn .. _function_sampler: Custom samplers --------------- A fully customized sampler, :class:`FunctionSampler`, is available in imbalanced-learn such that you can fast prototype your own sampler by defining a single function. Additional parameters can be added using the attribute ``kw_args`` which accepts a dictionary. The following example illustrates how to retain the 10 first elements of the array ``X`` and ``y``:: >>> import numpy as np >>> from imblearn import FunctionSampler >>> from sklearn.datasets import make_classification >>> X, y = make_classification(n_samples=5000, n_features=2, n_informative=2, ... n_redundant=0, n_repeated=0, n_classes=3, ... n_clusters_per_class=1, ... weights=[0.01, 0.05, 0.94], ... class_sep=0.8, random_state=0) >>> def func(X, y): ... return X[:10], y[:10] >>> sampler = FunctionSampler(func=func) >>> X_res, y_res = sampler.fit_resample(X, y) >>> np.all(X_res == X[:10]) True >>> np.all(y_res == y[:10]) True In addition, the parameter ``validate`` controls input checking. For instance, turning ``validate=False`` allows to pass any type of target ``y`` and do some sampling for regression targets:: >>> from sklearn.datasets import make_regression >>> X_reg, y_reg = make_regression(n_samples=100, random_state=42) >>> rng = np.random.RandomState(42) >>> def dummy_sampler(X, y): ... indices = rng.choice(np.arange(X.shape[0]), size=10) ... return X[indices], y[indices] >>> sampler = FunctionSampler(func=dummy_sampler, validate=False) >>> X_res, y_res = sampler.fit_resample(X_reg, y_reg) >>> y_res array([ 41.49112498, -142.78526195, 85.55095317, 141.43321419, 75.46571114, -67.49177372, 159.72700509, -169.80498923, 211.95889757, 211.95889757]) We illustrated the use of such sampler to implement an outlier rejection estimator which can be easily used within a :class:`~imblearn.pipeline.Pipeline`: :ref:`sphx_glr_auto_examples_applications_plot_outlier_rejections.py` .. _generators: Custom generators ----------------- Imbalanced-learn provides specific generators for TensorFlow and Keras which will generate balanced mini-batches. .. _tensorflow_generator: TensorFlow generator ~~~~~~~~~~~~~~~~~~~~ The :func:`~imblearn.tensorflow.balanced_batch_generator` allows to generate balanced mini-batches using an imbalanced-learn sampler which returns indices. Let's first generate some data:: >>> n_features, n_classes = 10, 2 >>> X, y = make_classification( ... n_samples=10_000, n_features=n_features, n_informative=2, ... n_redundant=0, n_repeated=0, n_classes=n_classes, ... n_clusters_per_class=1, weights=[0.1, 0.9], ... class_sep=0.8, random_state=0 ... ) >>> X = X.astype(np.float32) Then, we can create the generator that will yield mini-batches that will be balanced:: >>> from imblearn.under_sampling import RandomUnderSampler >>> from imblearn.tensorflow import balanced_batch_generator >>> training_generator, steps_per_epoch = balanced_batch_generator( ... X, ... y, ... sample_weight=None, ... sampler=RandomUnderSampler(), ... batch_size=32, ... random_state=42, ... ) The ``generator`` and ``steps_per_epoch`` are used during the training of a Tensorflow model. We will illustrate how to use this generator. First, we can define a logistic regression model which will be optimized by a gradient descent:: >>> import tensorflow as tf >>> # initialize the weights and intercept >>> normal_initializer = tf.random_normal_initializer(mean=0, stddev=0.01) >>> coef = tf.Variable(normal_initializer( ... shape=[n_features, n_classes]), dtype="float32" ... ) >>> intercept = tf.Variable( ... normal_initializer(shape=[n_classes]), dtype="float32" ... ) >>> # define the model >>> def logistic_regression(X): ... return tf.nn.softmax(tf.matmul(X, coef) + intercept) >>> # define the loss function >>> def cross_entropy(y_true, y_pred): ... y_true = tf.one_hot(y_true, depth=n_classes) ... y_pred = tf.clip_by_value(y_pred, 1e-9, 1.) ... return tf.reduce_mean(-tf.reduce_sum(y_true * tf.math.log(y_pred))) >>> # define our metric >>> def balanced_accuracy(y_true, y_pred): ... cm = tf.math.confusion_matrix(tf.cast(y_true, tf.int64), tf.argmax(y_pred, 1)) ... per_class = np.diag(cm) / tf.math.reduce_sum(cm, axis=1) ... return np.mean(per_class) >>> # define the optimizer >>> optimizer = tf.optimizers.SGD(learning_rate=0.01) >>> # define the optimization step >>> def run_optimization(X, y): ... with tf.GradientTape() as g: ... y_pred = logistic_regression(X) ... loss = cross_entropy(y, y_pred) ... gradients = g.gradient(loss, [coef, intercept]) ... optimizer.apply_gradients(zip(gradients, [coef, intercept])) Once initialized, the model is trained by iterating on balanced mini-batches of data and minimizing the loss previously defined:: >>> epochs = 10 >>> for e in range(epochs): ... y_pred = logistic_regression(X) ... loss = cross_entropy(y, y_pred) ... bal_acc = balanced_accuracy(y, y_pred) ... print(f"epoch: {e}, loss: {loss:.3f}, accuracy: {bal_acc}") ... for i in range(steps_per_epoch): ... X_batch, y_batch = next(training_generator) ... run_optimization(X_batch, y_batch) epoch: 0, ... .. _keras_generator: Keras generator ~~~~~~~~~~~~~~~ Keras provides an higher level API in which a model can be defined and train by calling ``fit_generator`` method to train the model. To illustrate, we will define a logistic regression model:: >>> from tensorflow import keras >>> y = keras.utils.to_categorical(y, 3) >>> model = keras.Sequential() >>> model.add( ... keras.layers.Dense( ... y.shape[1], input_dim=X.shape[1], activation='softmax' ... ) ... ) >>> model.compile( ... optimizer='sgd', loss='categorical_crossentropy', metrics=['accuracy'] ... ) :func:`~imblearn.keras.balanced_batch_generator` creates a balanced mini-batches generator with the associated number of mini-batches which will be generated:: >>> from imblearn.keras import balanced_batch_generator >>> training_generator, steps_per_epoch = balanced_batch_generator( ... X, y, sampler=RandomUnderSampler(), batch_size=10, random_state=42 ... ) Then, ``fit`` can be called passing the generator and the step:: >>> callback_history = model.fit( ... training_generator, ... steps_per_epoch=steps_per_epoch, ... epochs=10, ... verbose=1, ... ) Epoch 1/10 ... The second possibility is to use :class:`~imblearn.keras.BalancedBatchGenerator`. Only an instance of this class will be passed to ``fit``:: >>> from imblearn.keras import BalancedBatchGenerator >>> training_generator = BalancedBatchGenerator( ... X, y, sampler=RandomUnderSampler(), batch_size=10, random_state=42 ... ) >>> callback_history = model.fit( ... training_generator, ... steps_per_epoch=steps_per_epoch, ... epochs=10, ... verbose=1, ... ) Epoch 1/10 ... .. topic:: References * :ref:`sphx_glr_auto_examples_applications_porto_seguro_keras_under_sampling.py` scikit-learn-contrib-imbalanced-learn-8504e95/doc/model_selection.rst000066400000000000000000000141701521136113700256450ustar00rootroot00000000000000.. _cross_validation: ================ Cross validation ================ .. currentmodule:: imblearn.model_selection .. _instance_hardness_threshold_cv: The term instance hardness is used in literature to express the difficulty to correctly classify an instance. An instance for which the predicted probability of the true class is low, has large instance hardness. The way these hard-to-classify instances are distributed over train and test sets in cross validation, has significant effect on the test set performance metrics. The :class:`~imblearn.model_selection.InstanceHardnessCV` splitter distributes samples with large instance hardness equally over the folds, resulting in more robust cross validation. We will discuss instance hardness in this document and explain how to use the :class:`~imblearn.model_selection.InstanceHardnessCV` splitter. Instance hardness and average precision ======================================= Instance hardness is defined as 1 minus the probability of the most probable class: .. math:: H(x) = 1 - P(\hat{y}|x) In this equation :math:`H(x)` is the instance hardness for a sample with features :math:`x` and :math:`P(\hat{y}|x)` the probability of predicted label :math:`\hat{y}` given the features. If the model predicts label 0 and gives a `predict_proba` output of [0.9, 0.1], the probability of the most probable class (0) is 0.9 and the instance hardness is `1-0.9=0.1`. Samples with large instance hardness have significant effect on the area under precision-recall curve, or average precision. Especially samples with label 0 with large instance hardness (so the model predicts label 1) reduce the average precision a lot as these points affect the precision-recall curve in the left where the area is largest; the precision is lowered in the range of low recall and high thresholds. When doing cross validation, e.g. in case of hyperparameter tuning or recursive feature elimination, random gathering of these points in some folds introduce variance in CV results that deteriorates robustness of the cross validation task. The :class:`~imblearn.model_selection.InstanceHardnessCV` splitter aims to distribute the samples with large instance hardness over the folds in order to reduce undesired variance. Note that one should use this splitter to make model *selection* tasks robust like hyperparameter tuning and feature selection but not for model *performance estimation* for which you also want to know the variance of performance to be expected in production. Create imbalanced dataset with samples with large instance hardness =================================================================== Let's start by creating a dataset to work with. We create a dataset with 5% class imbalance using scikit-learn's :func:`~sklearn.datasets.make_blobs` function. >>> import numpy as np >>> from matplotlib import pyplot as plt >>> from sklearn.datasets import make_blobs >>> from imblearn.datasets import make_imbalance >>> random_state = 10 >>> X, y = make_blobs(n_samples=[950, 50], centers=((-3, 0), (3, 0)), ... random_state=random_state) >>> plt.scatter(X[:, 0], X[:, 1], c=y) >>> plt.show() .. image:: ./auto_examples/model_selection/images/sphx_glr_plot_instance_hardness_cv_001.png :target: ./auto_examples/model_selection/plot_instance_hardness_cv.html :align: center Now we add some samples with large instance hardness >>> X_hard, y_hard = make_blobs(n_samples=10, centers=((3, 0), (-3, 0)), ... cluster_std=1, ... random_state=random_state) >>> X = np.vstack((X, X_hard)) >>> y = np.hstack((y, y_hard)) >>> plt.scatter(X[:, 0], X[:, 1], c=y) >>> plt.show() .. image:: ./auto_examples/model_selection/images/sphx_glr_plot_instance_hardness_cv_002.png :target: ./auto_examples/model_selection/plot_instance_hardness_cv.html :align: center Assess cross validation performance variance using `InstanceHardnessCV` splitter ================================================================================ Then we take a :class:`~sklearn.linear_model.LogisticRegression` and assess the cross validation performance using a :class:`~sklearn.model_selection.StratifiedKFold` cv splitter and the :func:`~sklearn.model_selection.cross_validate` function. >>> from sklearn.ensemble import LogisticRegressionClassifier >>> clf = LogisticRegressionClassifier(random_state=random_state) >>> skf_cv = StratifiedKFold(n_splits=5, shuffle=True, ... random_state=random_state) >>> skf_result = cross_validate(clf, X, y, cv=skf_cv, scoring="average_precision") Now, we do the same using an :class:`~imblearn.model_selection.InstanceHardnessCV` splitter. We use provide our classifier to the splitter to calculate instance hardness and distribute samples with large instance hardness equally over the folds. >>> ih_cv = InstanceHardnessCV(estimator=clf, n_splits=5, ... random_state=random_state) >>> ih_result = cross_validate(clf, X, y, cv=ih_cv, scoring="average_precision") When we plot the test scores for both cv splitters, we see that the variance using the :class:`~imblearn.model_selection.InstanceHardnessCV` splitter is lower than for the :class:`~sklearn.model_selection.StratifiedKFold` splitter. >>> plt.boxplot([skf_result['test_score'], ih_result['test_score']], ... tick_labels=["StratifiedKFold", "InstanceHardnessCV"], ... vert=False) >>> plt.xlabel('Average precision') >>> plt.tight_layout() .. image:: ./auto_examples/model_selection/images/sphx_glr_plot_instance_hardness_cv_003.png :target: ./auto_examples/model_selection/plot_instance_hardness_cv.html :align: center Be aware that the most important part of cross-validation splitters is to simulate the conditions that one will encounter in production. Therefore, if it is likely to get difficult samples in production, one should use a cross-validation splitter that emulates this situation. In our case, the :class:`~sklearn.model_selection.StratifiedKFold` splitter did not allow to distribute the difficult samples over the folds and thus it was likely a problem for our use case. scikit-learn-contrib-imbalanced-learn-8504e95/doc/over_sampling.rst000066400000000000000000000343241521136113700253500ustar00rootroot00000000000000.. _over-sampling: ============= Over-sampling ============= .. currentmodule:: imblearn.over_sampling A practical guide ================= You can refer to :ref:`sphx_glr_auto_examples_over-sampling_plot_comparison_over_sampling.py`. .. _random_over_sampler: Naive random over-sampling -------------------------- One way to fight this issue is to generate new samples in the classes which are under-represented. The most naive strategy is to generate new samples by randomly sampling with replacement the current available samples. The :class:`RandomOverSampler` offers such scheme:: >>> from sklearn.datasets import make_classification >>> X, y = make_classification(n_samples=5000, n_features=2, n_informative=2, ... n_redundant=0, n_repeated=0, n_classes=3, ... n_clusters_per_class=1, ... weights=[0.01, 0.05, 0.94], ... class_sep=0.8, random_state=0) >>> from imblearn.over_sampling import RandomOverSampler >>> ros = RandomOverSampler(random_state=0) >>> X_resampled, y_resampled = ros.fit_resample(X, y) >>> from collections import Counter >>> print(sorted(Counter(y_resampled).items())) [(0, 4674), (1, 4674), (2, 4674)] The augmented data set should be used instead of the original data set to train a classifier:: >>> from sklearn.linear_model import LogisticRegression >>> clf = LogisticRegression() >>> clf.fit(X_resampled, y_resampled) LogisticRegression(...) In the figure below, we compare the decision functions of a classifier trained using the over-sampled data set and the original data set. .. image:: ./auto_examples/over-sampling/images/sphx_glr_plot_comparison_over_sampling_002.png :target: ./auto_examples/over-sampling/plot_comparison_over_sampling.html :scale: 60 :align: center As a result, the majority class does not take over the other classes during the training process. Consequently, all classes are represented by the decision function. In addition, :class:`RandomOverSampler` allows to sample heterogeneous data (e.g. containing some strings):: >>> import numpy as np >>> X_hetero = np.array([['xxx', 1, 1.0], ['yyy', 2, 2.0], ['zzz', 3, 3.0]], ... dtype=object) >>> y_hetero = np.array([0, 0, 1]) >>> X_resampled, y_resampled = ros.fit_resample(X_hetero, y_hetero) >>> print(X_resampled) [['xxx' 1 1.0] ['yyy' 2 2.0] ['zzz' 3 3.0] ['zzz' 3 3.0]] >>> print(y_resampled) [0 0 1 1] It would also work with pandas dataframe:: >>> from sklearn.datasets import fetch_openml >>> df_adult, y_adult = fetch_openml( ... 'adult', version=2, as_frame=True, return_X_y=True) >>> df_adult.head() # doctest: +SKIP >>> df_resampled, y_resampled = ros.fit_resample(df_adult, y_adult) >>> df_resampled.head() # doctest: +SKIP If repeating samples is an issue, the parameter `shrinkage` allows to create a smoothed bootstrap. However, the original data needs to be numerical. The `shrinkage` parameter controls the dispersion of the new generated samples. We show an example illustrate that the new samples are not overlapping anymore once using a smoothed bootstrap. This ways of generating smoothed bootstrap is also known a Random Over-Sampling Examples (ROSE) :cite:`torelli2014rose`. .. image:: ./auto_examples/over-sampling/images/sphx_glr_plot_comparison_over_sampling_003.png :target: ./auto_examples/over-sampling/plot_comparison_over_sampling.html :scale: 60 :align: center .. _smote_adasyn: From random over-sampling to SMOTE and ADASYN --------------------------------------------- Apart from the random sampling with replacement, there are two popular methods to over-sample minority classes: (i) the Synthetic Minority Oversampling Technique (SMOTE) :cite:`chawla2002smote` and (ii) the Adaptive Synthetic (ADASYN) :cite:`he2008adasyn` sampling method. These algorithms can be used in the same manner:: >>> from imblearn.over_sampling import SMOTE, ADASYN >>> X_resampled, y_resampled = SMOTE().fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 4674), (1, 4674), (2, 4674)] >>> clf_smote = LogisticRegression().fit(X_resampled, y_resampled) >>> X_resampled, y_resampled = ADASYN().fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 4673), (1, 4662), (2, 4674)] >>> clf_adasyn = LogisticRegression().fit(X_resampled, y_resampled) The figure below illustrates the major difference of the different over-sampling methods. .. image:: ./auto_examples/over-sampling/images/sphx_glr_plot_comparison_over_sampling_004.png :target: ./auto_examples/over-sampling/plot_comparison_over_sampling.html :scale: 60 :align: center Ill-posed examples ------------------ While the :class:`RandomOverSampler` is over-sampling by duplicating some of the original samples of the minority class, :class:`SMOTE` and :class:`ADASYN` generate new samples in by interpolation. However, the samples used to interpolate/generate new synthetic samples differ. In fact, :class:`ADASYN` focuses on generating samples next to the original samples which are wrongly classified using a k-Nearest Neighbors classifier while the basic implementation of :class:`SMOTE` will not make any distinction between easy and hard samples to be classified using the nearest neighbors rule. Therefore, the decision function found during training will be different among the algorithms. .. image:: ./auto_examples/over-sampling/images/sphx_glr_plot_comparison_over_sampling_005.png :target: ./auto_examples/over-sampling/plot_comparison_over_sampling.html :align: center The sampling particularities of these two algorithms can lead to some peculiar behavior as shown below. .. image:: ./auto_examples/over-sampling/images/sphx_glr_plot_comparison_over_sampling_006.png :target: ./auto_examples/over-sampling/plot_comparison_over_sampling.html :scale: 60 :align: center SMOTE variants -------------- SMOTE might connect inliers and outliers while ADASYN might focus solely on outliers which, in both cases, might lead to a sub-optimal decision function. In this regard, SMOTE offers three additional options to generate samples. Those methods focus on samples near the border of the optimal decision function and will generate samples in the opposite direction of the nearest neighbors class. Those variants are presented in the figure below. .. image:: ./auto_examples/over-sampling/images/sphx_glr_plot_comparison_over_sampling_007.png :target: ./auto_examples/over-sampling/plot_comparison_over_sampling.html :scale: 60 :align: center The :class:`BorderlineSMOTE` :cite:`han2005borderline`, :class:`SVMSMOTE` :cite:`nguyen2009borderline`, and :class:`KMeansSMOTE` :cite:`last2017oversampling` offer some variant of the SMOTE algorithm:: >>> from imblearn.over_sampling import BorderlineSMOTE >>> X_resampled, y_resampled = BorderlineSMOTE().fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 4674), (1, 4674), (2, 4674)] When dealing with mixed data type such as continuous and categorical features, none of the presented methods (apart of the class :class:`RandomOverSampler`) can deal with the categorical features. The :class:`SMOTENC` :cite:`chawla2002smote` is an extension of the :class:`SMOTE` algorithm for which categorical data are treated differently:: >>> # create a synthetic data set with continuous and categorical features >>> rng = np.random.RandomState(42) >>> n_samples = 50 >>> X = np.empty((n_samples, 3), dtype=object) >>> X[:, 0] = rng.choice(['A', 'B', 'C'], size=n_samples).astype(object) >>> X[:, 1] = rng.randn(n_samples) >>> X[:, 2] = rng.randint(3, size=n_samples) >>> y = np.array([0] * 20 + [1] * 30) >>> print(sorted(Counter(y).items())) [(0, 20), (1, 30)] In this data set, the first and last features are considered as categorical features. One needs to provide this information to :class:`SMOTENC` via the parameters ``categorical_features`` either by passing the indices, the feature names when `X` is a pandas DataFrame, a boolean mask marking these features, or relying on `dtype` inference if the columns are using the :class:`pandas.CategoricalDtype`:: >>> from imblearn.over_sampling import SMOTENC >>> smote_nc = SMOTENC(categorical_features=[0, 2], random_state=0) >>> X_resampled, y_resampled = smote_nc.fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 30), (1, 30)] >>> print(X_resampled[-5:]) [['A' 0.19... 2] ['B' -0.36... 2] ['B' 0.87... 2] ['B' 0.37... 2] ['B' 0.33... 2]] Therefore, it can be seen that the samples generated in the first and last columns are belonging to the same categories originally presented without any other extra interpolation. However, :class:`SMOTENC` is only working when data is a mixed of numerical and categorical features. If data are made of only categorical data, one can use the :class:`SMOTEN` variant :cite:`chawla2002smote`. The algorithm changes in two ways: * the nearest neighbors search does not rely on the Euclidean distance. Indeed, the value difference metric (VDM) also implemented in the class :class:`~imblearn.metrics.ValueDifferenceMetric` is used. * a new sample is generated where each feature value corresponds to the most common category seen in the neighbors samples belonging to the same class. Let's take the following example:: >>> import numpy as np >>> X = np.array(["green"] * 5 + ["red"] * 10 + ["blue"] * 7, ... dtype=object).reshape(-1, 1) >>> y = np.array(["apple"] * 5 + ["not apple"] * 3 + ["apple"] * 7 + ... ["not apple"] * 5 + ["apple"] * 2, dtype=object) We generate a dataset associating a color to being an apple or not an apple. We strongly associated "green" and "red" to being an apple. The minority class being "not apple", we expect new data generated belonging to the category "blue":: >>> from imblearn.over_sampling import SMOTEN >>> sampler = SMOTEN(random_state=0) >>> X_res, y_res = sampler.fit_resample(X, y) >>> X_res[y.size:] array([['blue'], ['blue'], ['blue'], ['blue'], ['blue'], ['blue']], dtype=object) >>> y_res[y.size:] array(['not apple', 'not apple', 'not apple', 'not apple', 'not apple', 'not apple'], dtype=object) Mathematical formulation ======================== Sample generation ----------------- Both :class:`SMOTE` and :class:`ADASYN` use the same algorithm to generate new samples. Considering a sample :math:`x_i`, a new sample :math:`x_{new}` will be generated considering its k neareast-neighbors (corresponding to ``k_neighbors``). For instance, the 3 nearest-neighbors are included in the blue circle as illustrated in the figure below. Then, one of these nearest-neighbors :math:`x_{zi}` is selected and a sample is generated as follows: .. math:: x_{new} = x_i + \lambda \times (x_{zi} - x_i) where :math:`\lambda` is a random number in the range :math:`[0, 1]`. This interpolation will create a sample on the line between :math:`x_{i}` and :math:`x_{zi}` as illustrated in the image below: .. image:: ./auto_examples/over-sampling/images/sphx_glr_plot_illustration_generation_sample_001.png :target: ./auto_examples/over-sampling/plot_illustration_generation_sample.html :scale: 60 :align: center SMOTE-NC slightly change the way a new sample is generated by performing something specific for the categorical features. In fact, the categories of a new generated sample are decided by picking the most frequent category of the nearest neighbors present during the generation. .. warning:: Be aware that SMOTE-NC is not designed to work with only categorical data. The other SMOTE variants and ADASYN differ from each other by selecting the samples :math:`x_i` ahead of generating the new samples. The **regular** SMOTE algorithm --- cf. to the :class:`SMOTE` object --- does not impose any rule and will randomly pick-up all possible :math:`x_i` available. The **borderline** SMOTE --- cf. to the :class:`BorderlineSMOTE` with the parameters ``kind='borderline-1'`` and ``kind='borderline-2'`` --- will classify each sample :math:`x_i` to be (i) noise (i.e. all nearest-neighbors are from a different class than the one of :math:`x_i`), (ii) in danger (i.e. at least half of the nearest neighbors are from the same class than :math:`x_i`, or (iii) safe (i.e. all nearest neighbors are from the same class than :math:`x_i`). **Borderline-1** and **Borderline-2** SMOTE will use the samples *in danger* to generate new samples. In **Borderline-1** SMOTE, :math:`x_{zi}` will belong to the same class than the one of the sample :math:`x_i`. On the contrary, **Borderline-2** SMOTE will consider :math:`x_{zi}` which can be from any class. **SVM** SMOTE --- cf. to :class:`SVMSMOTE` --- uses an SVM classifier to find support vectors and generate samples considering them. Note that the ``C`` parameter of the SVM classifier allows to select more or less support vectors. For both borderline and SVM SMOTE, a neighborhood is defined using the parameter ``m_neighbors`` to decide if a sample is in danger, safe, or noise. **KMeans** SMOTE --- cf. to :class:`KMeansSMOTE` --- uses a KMeans clustering method before to apply SMOTE. The clustering will group samples together and generate new samples depending of the cluster density. ADASYN works similarly to the regular SMOTE. However, the number of samples generated for each :math:`x_i` is proportional to the number of samples which are not from the same class than :math:`x_i` in a given neighborhood. Therefore, more samples will be generated in the area that the nearest neighbor rule is not respected. The parameter ``m_neighbors`` is equivalent to ``k_neighbors`` in :class:`SMOTE`. Multi-class management ---------------------- All algorithms can be used with multiple classes as well as binary classes classification. :class:`RandomOverSampler` does not require any inter-class information during the sample generation. Therefore, each targeted class is resampled independently. In the contrary, both :class:`ADASYN` and :class:`SMOTE` need information regarding the neighbourhood of each sample used for sample generation. They are using a one-vs-rest approach by selecting each targeted class and computing the necessary statistics against the rest of the data set which are grouped in a single class. scikit-learn-contrib-imbalanced-learn-8504e95/doc/references/000077500000000000000000000000001521136113700240645ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/doc/references/combine.rst000066400000000000000000000005001521136113700262250ustar00rootroot00000000000000.. _combine_ref: Combination of over- and under-sampling methods =============================================== .. automodule:: imblearn.combine :no-members: :no-inherited-members: .. currentmodule:: imblearn.combine .. autosummary:: :toctree: generated/ :template: class.rst SMOTEENN SMOTETomek scikit-learn-contrib-imbalanced-learn-8504e95/doc/references/datasets.rst000066400000000000000000000004041521136113700264240ustar00rootroot00000000000000.. _datasets_ref: Datasets ======== .. automodule:: imblearn.datasets :no-members: :no-inherited-members: .. currentmodule:: imblearn.datasets .. autosummary:: :toctree: generated/ :template: function.rst make_imbalance fetch_datasets scikit-learn-contrib-imbalanced-learn-8504e95/doc/references/ensemble.rst000066400000000000000000000007571521136113700264210ustar00rootroot00000000000000.. _ensemble_ref: Ensemble methods ================ .. automodule:: imblearn.ensemble :no-members: :no-inherited-members: .. currentmodule:: imblearn.ensemble Boosting algorithms ------------------- .. autosummary:: :toctree: generated/ :template: class.rst EasyEnsembleClassifier RUSBoostClassifier Bagging algorithms ------------------ .. autosummary:: :toctree: generated/ :template: class.rst BalancedBaggingClassifier BalancedRandomForestClassifier scikit-learn-contrib-imbalanced-learn-8504e95/doc/references/index.rst000066400000000000000000000004731521136113700257310ustar00rootroot00000000000000.. _api: ############# API reference ############# This is the full API documentation of the `imbalanced-learn` toolbox. .. toctree:: :maxdepth: 3 under_sampling over_sampling combine ensemble keras tensorflow miscellaneous pipeline metrics model_selection datasets utils scikit-learn-contrib-imbalanced-learn-8504e95/doc/references/keras.rst000066400000000000000000000005701521136113700257250ustar00rootroot00000000000000.. _keras_ref: Batch generator for Keras ========================= .. automodule:: imblearn.keras :no-members: :no-inherited-members: .. currentmodule:: imblearn .. autosummary:: :toctree: generated/ :template: class.rst keras.BalancedBatchGenerator .. autosummary:: :toctree: generated/ :template: function.rst keras.balanced_batch_generator scikit-learn-contrib-imbalanced-learn-8504e95/doc/references/metrics.rst000066400000000000000000000015561521136113700262730ustar00rootroot00000000000000.. _metrics_ref: Metrics ======= .. automodule:: imblearn.metrics :no-members: :no-inherited-members: Classification metrics ---------------------- See the :ref:`metrics` section of the user guide for further details. .. currentmodule:: imblearn.metrics .. autosummary:: :toctree: generated/ :template: function.rst classification_report_imbalanced sensitivity_specificity_support sensitivity_score specificity_score geometric_mean_score macro_averaged_mean_absolute_error make_index_balanced_accuracy Pairwise metrics ---------------- See the :ref:`pairwise_metrics` section of the user guide for further details. .. automodule:: imblearn.metrics.pairwise :no-members: :no-inherited-members: .. currentmodule:: imblearn.metrics.pairwise .. autosummary:: :toctree: generated/ :template: class.rst ValueDifferenceMetric scikit-learn-contrib-imbalanced-learn-8504e95/doc/references/miscellaneous.rst000066400000000000000000000003251521136113700274610ustar00rootroot00000000000000.. _misc_ref: Miscellaneous ============= Imbalance-learn provides some fast-prototyping tools. .. currentmodule:: imblearn .. autosummary:: :toctree: generated/ :template: class.rst FunctionSampler scikit-learn-contrib-imbalanced-learn-8504e95/doc/references/model_selection.rst000066400000000000000000000006701521136113700277660ustar00rootroot00000000000000.. _model_selection_ref: Model selection methods ======================= .. automodule:: imblearn.model_selection :no-members: :no-inherited-members: Cross-validation splitters -------------------------- .. automodule:: imblearn.model_selection._split :no-members: :no-inherited-members: .. currentmodule:: imblearn.model_selection .. autosummary:: :toctree: generated/ :template: class.rst InstanceHardnessCV scikit-learn-contrib-imbalanced-learn-8504e95/doc/references/over_sampling.rst000066400000000000000000000010001521136113700274520ustar00rootroot00000000000000.. _over_sampling_ref: Over-sampling methods ===================== .. automodule:: imblearn.over_sampling :no-members: :no-inherited-members: .. currentmodule:: imblearn.over_sampling Basic over-sampling ------------------- .. autosummary:: :toctree: generated/ :template: class.rst RandomOverSampler SMOTE algorithms ---------------- .. autosummary:: :toctree: generated/ :template: class.rst SMOTE SMOTENC SMOTEN ADASYN BorderlineSMOTE KMeansSMOTE SVMSMOTE scikit-learn-contrib-imbalanced-learn-8504e95/doc/references/pipeline.rst000066400000000000000000000005001521136113700264160ustar00rootroot00000000000000.. _pipeline_ref: Pipeline ======== .. automodule:: imblearn.pipeline :no-members: :no-inherited-members: .. currentmodule:: imblearn.pipeline .. autosummary:: :toctree: generated/ :template: class.rst Pipeline .. autosummary:: :toctree: generated/ :template: function.rst make_pipeline scikit-learn-contrib-imbalanced-learn-8504e95/doc/references/tensorflow.rst000066400000000000000000000004561521136113700270250ustar00rootroot00000000000000.. _tensorflow_ref: Batch generator for TensorFlow ============================== .. automodule:: imblearn.tensorflow :no-members: :no-inherited-members: .. currentmodule:: imblearn .. autosummary:: :toctree: generated/ :template: function.rst tensorflow.balanced_batch_generator scikit-learn-contrib-imbalanced-learn-8504e95/doc/references/under_sampling.rst000066400000000000000000000016271521136113700276330ustar00rootroot00000000000000.. _under_sampling_ref: Under-sampling methods ====================== .. automodule:: imblearn.under_sampling :no-members: :no-inherited-members: Prototype generation -------------------- .. automodule:: imblearn.under_sampling._prototype_generation :no-members: :no-inherited-members: .. currentmodule:: imblearn.under_sampling .. autosummary:: :toctree: generated/ :template: class.rst ClusterCentroids Prototype selection ------------------- .. automodule:: imblearn.under_sampling._prototype_selection :no-members: :no-inherited-members: .. currentmodule:: imblearn.under_sampling .. autosummary:: :toctree: generated/ :template: class.rst CondensedNearestNeighbour EditedNearestNeighbours RepeatedEditedNearestNeighbours AllKNN InstanceHardnessThreshold NearMiss NeighbourhoodCleaningRule OneSidedSelection RandomUnderSampler TomekLinks scikit-learn-contrib-imbalanced-learn-8504e95/doc/references/utils.rst000066400000000000000000000013151521136113700257560ustar00rootroot00000000000000Utilities ========= .. automodule:: imblearn.utils :no-members: :no-inherited-members: .. currentmodule:: imblearn.utils Validation checks used in samplers ---------------------------------- .. autosummary:: :toctree: generated/ :template: function.rst estimator_checks.parametrize_with_checks check_neighbors_object check_sampling_strategy check_target_type Testing compatibility of your own sampler ----------------------------------------- .. automodule:: imblearn.utils.estimator_checks :no-members: :no-inherited-members: .. currentmodule:: imblearn.utils.estimator_checks .. autosummary:: :toctree: generated/ :template: function.rst parametrize_with_checks scikit-learn-contrib-imbalanced-learn-8504e95/doc/sphinxext/000077500000000000000000000000001521136113700237755ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/doc/sphinxext/LICENSE.txt000066400000000000000000000136221521136113700256240ustar00rootroot00000000000000------------------------------------------------------------------------------- The files - numpydoc.py - autosummary.py - autosummary_generate.py - docscrape.py - docscrape_sphinx.py - phantom_import.py have the following license: Copyright (C) 2008 Stefan van der Walt , Pauli Virtanen Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------- The files - compiler_unparse.py - comment_eater.py - traitsdoc.py have the following license: This software is OSI Certified Open Source Software. OSI Certified is a certification mark of the Open Source Initiative. Copyright (c) 2006, Enthought, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Enthought, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------- The files - only_directives.py - plot_directive.py originate from Matplotlib (http://matplotlib.sf.net/) which has the following license: Copyright (c) 2002-2008 John D. Hunter; All Rights Reserved. 1. This LICENSE AGREEMENT is between John D. Hunter (“JDH”), and the Individual or Organization (“Licensee”) accessing and otherwise using matplotlib software in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, JDH hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use matplotlib 0.98.3 alone or in any derivative version, provided, however, that JDH’s License Agreement and JDH’s notice of copyright, i.e., “Copyright (c) 2002-2008 John D. Hunter; All Rights Reserved” are retained in matplotlib 0.98.3 alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates matplotlib 0.98.3 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to matplotlib 0.98.3. 4. JDH is making matplotlib 0.98.3 available to Licensee on an “AS IS” basis. JDH MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, JDH MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF MATPLOTLIB 0.98.3 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. JDH SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF MATPLOTLIB 0.98.3 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING MATPLOTLIB 0.98.3, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between JDH and Licensee. This License Agreement does not grant permission to use JDH trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By copying, installing or otherwise using matplotlib 0.98.3, Licensee agrees to be bound by the terms and conditions of this License Agreement. scikit-learn-contrib-imbalanced-learn-8504e95/doc/sphinxext/MANIFEST.in000066400000000000000000000000531521136113700255310ustar00rootroot00000000000000recursive-include tests *.py include *.txt scikit-learn-contrib-imbalanced-learn-8504e95/doc/sphinxext/README.txt000066400000000000000000000032401521136113700254720ustar00rootroot00000000000000===================================== numpydoc -- Numpy's Sphinx extensions ===================================== Numpy's documentation uses several custom extensions to Sphinx. These are shipped in this ``numpydoc`` package, in case you want to make use of them in third-party projects. The following extensions are available: - ``numpydoc``: support for the Numpy docstring format in Sphinx, and add the code description directives ``np-function``, ``np-cfunction``, etc. that support the Numpy docstring syntax. - ``numpydoc.traitsdoc``: For gathering documentation about Traits attributes. - ``numpydoc.plot_directives``: Adaptation of Matplotlib's ``plot::`` directive. Note that this implementation may still undergo severe changes or eventually be deprecated. - ``numpydoc.only_directives``: (DEPRECATED) - ``numpydoc.autosummary``: (DEPRECATED) An ``autosummary::`` directive. Available in Sphinx 0.6.2 and (to-be) 1.0 as ``sphinx.ext.autosummary``, and it the Sphinx 1.0 version is recommended over that included in Numpydoc. numpydoc ======== Numpydoc inserts a hook into Sphinx's autodoc that converts docstrings following the Numpy/Scipy format to a form palatable to Sphinx. Options ------- The following options can be set in conf.py: - numpydoc_use_plots: bool Whether to produce ``plot::`` directives for Examples sections that contain ``import matplotlib``. - numpydoc_show_class_members: bool Whether to show all members of a class in the Methods and Attributes sections automatically. - numpydoc_edit_link: bool (DEPRECATED -- edit your HTML template instead) Whether to insert an edit link after docstrings. scikit-learn-contrib-imbalanced-learn-8504e95/doc/sphinxext/github_link.py000066400000000000000000000051251521136113700266510ustar00rootroot00000000000000import inspect import os import subprocess import sys from functools import partial from operator import attrgetter REVISION_CMD = "git rev-parse --short HEAD" def _get_git_revision(): try: revision = subprocess.check_output(REVISION_CMD.split()).strip() except (subprocess.CalledProcessError, OSError): print("Failed to execute git to get revision") return None return revision.decode("utf-8") def _linkcode_resolve(domain, info, package, url_fmt, revision): """Determine a link to online source for a class/method/function This is called by sphinx.ext.linkcode An example with a long-untouched module that everyone has >>> _linkcode_resolve('py', {'module': 'tty', ... 'fullname': 'setraw'}, ... package='tty', ... url_fmt='https://hg.python.org/cpython/file/' ... '{revision}/Lib/{package}/{path}#L{lineno}', ... revision='xxxx') 'https://hg.python.org/cpython/file/xxxx/Lib/tty/tty.py#L18' """ if revision is None: return if domain not in ("py", "pyx"): return if not info.get("module") or not info.get("fullname"): return class_name = info["fullname"].split(".")[0] module = __import__(info["module"], fromlist=[class_name]) obj = attrgetter(info["fullname"])(module) # Unwrap the object to get the correct source # file in case that is wrapped by a decorator obj = inspect.unwrap(obj) try: fn = inspect.getsourcefile(obj) except Exception: fn = None if not fn: try: fn = inspect.getsourcefile(sys.modules[obj.__module__]) except Exception: fn = None if not fn: return fn = os.path.relpath(fn, start=os.path.dirname(__import__(package).__file__)) try: lineno = inspect.getsourcelines(obj)[1] except Exception: lineno = "" return url_fmt.format(revision=revision, package=package, path=fn, lineno=lineno) def make_linkcode_resolve(package, url_fmt): """Returns a linkcode_resolve function for the given URL format revision is a git commit reference (hash or name) package is the name of the root module of the package url_fmt is along the lines of ('https://github.com/USER/PROJECT/' 'blob/{revision}/{package}/' '{path}#L{lineno}') """ revision = _get_git_revision() return partial( _linkcode_resolve, revision=revision, package=package, url_fmt=url_fmt ) scikit-learn-contrib-imbalanced-learn-8504e95/doc/sphinxext/sphinx_issues.py000066400000000000000000000175341521136113700272650ustar00rootroot00000000000000"""A Sphinx extension for linking to your project's issue tracker. Copyright 2014 Steven Loria Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import re from docutils import nodes, utils from sphinx.util.nodes import split_explicit_title __version__ = "1.2.0" __author__ = "Steven Loria" __license__ = "MIT" def user_role(name, rawtext, text, lineno, inliner, options=None, content=None): """Sphinx role for linking to a user profile. Defaults to linking to Github profiles, but the profile URIS can be configured via the ``issues_user_uri`` config value. Examples: :: :user:`sloria` Anchor text also works: :: :user:`Steven Loria ` """ options = options or {} content = content or [] has_explicit_title, title, target = split_explicit_title(text) target = utils.unescape(target).strip() title = utils.unescape(title).strip() config = inliner.document.settings.env.app.config if config.issues_user_uri: ref = config.issues_user_uri.format(user=target) else: ref = f"https://github.com/{target}" if has_explicit_title: text = title else: text = f"@{target}" link = nodes.reference(text=text, refuri=ref, **options) return [link], [] def cve_role(name, rawtext, text, lineno, inliner, options=None, content=None): """Sphinx role for linking to a CVE on https://cve.mitre.org. Examples: :: :cve:`CVE-2018-17175` """ options = options or {} content = content or [] has_explicit_title, title, target = split_explicit_title(text) target = utils.unescape(target).strip() title = utils.unescape(title).strip() ref = f"https://cve.mitre.org/cgi-bin/cvename.cgi?name={target}" text = title if has_explicit_title else target link = nodes.reference(text=text, refuri=ref, **options) return [link], [] class IssueRole: EXTERNAL_REPO_REGEX = re.compile(r"^(\w+)/(.+)([#@])([\w]+)$") def __init__( self, uri_config_option, format_kwarg, github_uri_template, format_text=None, ): self.uri_config_option = uri_config_option self.format_kwarg = format_kwarg self.github_uri_template = github_uri_template self.format_text = format_text or self.default_format_text @staticmethod def default_format_text(issue_no): return f"#{issue_no}" def make_node(self, name, issue_no, config, options=None): name_map = {"pr": "pull", "issue": "issues", "commit": "commit"} options = options or {} repo_match = self.EXTERNAL_REPO_REGEX.match(issue_no) if repo_match: # External repo username, repo, symbol, issue = repo_match.groups() if name not in name_map: raise ValueError(f"External repo linking not supported for :{name}:") path = name_map.get(name) ref = "https://github.com/{issues_github_path}/{path}/{n}".format( issues_github_path=f"{username}/{repo}", path=path, n=issue, ) formatted_issue = self.format_text(issue).lstrip("#") text = "{username}/{repo}{symbol}{formatted_issue}".format(**locals()) link = nodes.reference(text=text, refuri=ref, **options) return link if issue_no not in ("-", "0"): uri_template = getattr(config, self.uri_config_option, None) if uri_template: ref = uri_template.format(**{self.format_kwarg: issue_no}) elif config.issues_github_path: ref = self.github_uri_template.format( issues_github_path=config.issues_github_path, n=issue_no ) else: raise ValueError( f"Neither {self.uri_config_option} nor issues_github_path is set" ) issue_text = self.format_text(issue_no) link = nodes.reference(text=issue_text, refuri=ref, **options) else: link = None return link def __call__( self, name, rawtext, text, lineno, inliner, options=None, content=None ): options = options or {} content = content or [] issue_nos = [each.strip() for each in utils.unescape(text).split(",")] config = inliner.document.settings.env.app.config ret = [] for i, issue_no in enumerate(issue_nos): node = self.make_node(name, issue_no, config, options=options) ret.append(node) if i != len(issue_nos) - 1: sep = nodes.raw(text=", ", format="html") ret.append(sep) return ret, [] """Sphinx role for linking to an issue. Must have `issues_uri` or `issues_github_path` configured in ``conf.py``. Examples: :: :issue:`123` :issue:`42,45` :issue:`sloria/konch#123` """ issue_role = IssueRole( uri_config_option="issues_uri", format_kwarg="issue", github_uri_template="https://github.com/{issues_github_path}/issues/{n}", ) """Sphinx role for linking to a pull request. Must have `issues_pr_uri` or `issues_github_path` configured in ``conf.py``. Examples: :: :pr:`123` :pr:`42,45` :pr:`sloria/konch#43` """ pr_role = IssueRole( uri_config_option="issues_pr_uri", format_kwarg="pr", github_uri_template="https://github.com/{issues_github_path}/pull/{n}", ) def format_commit_text(sha): return sha[:7] """Sphinx role for linking to a commit. Must have `issues_pr_uri` or `issues_github_path` configured in ``conf.py``. Examples: :: :commit:`123abc456def` :commit:`sloria/konch@123abc456def` """ commit_role = IssueRole( uri_config_option="issues_commit_uri", format_kwarg="commit", github_uri_template="https://github.com/{issues_github_path}/commit/{n}", format_text=format_commit_text, ) def setup(app): # Format template for issues URI # e.g. 'https://github.com/sloria/marshmallow/issues/{issue} app.add_config_value("issues_uri", default=None, rebuild="html") # Format template for PR URI # e.g. 'https://github.com/sloria/marshmallow/pull/{issue} app.add_config_value("issues_pr_uri", default=None, rebuild="html") # Format template for commit URI # e.g. 'https://github.com/sloria/marshmallow/commits/{commit} app.add_config_value("issues_commit_uri", default=None, rebuild="html") # Shortcut for Github, e.g. 'sloria/marshmallow' app.add_config_value("issues_github_path", default=None, rebuild="html") # Format template for user profile URI # e.g. 'https://github.com/{user}' app.add_config_value("issues_user_uri", default=None, rebuild="html") app.add_role("issue", issue_role) app.add_role("pr", pr_role) app.add_role("user", user_role) app.add_role("commit", commit_role) app.add_role("cve", cve_role) return { "version": __version__, "parallel_read_safe": True, "parallel_write_safe": True, } scikit-learn-contrib-imbalanced-learn-8504e95/doc/under_sampling.rst000066400000000000000000000537071521136113700255200ustar00rootroot00000000000000.. _under-sampling: ============== Under-sampling ============== .. currentmodule:: imblearn.under_sampling One way of handling imbalanced datasets is to reduce the number of observations from all classes but the minority class. The minority class is that with the least number of observations. The most well known algorithm in this group is random undersampling, where samples from the targeted classes are removed at random. But there are many other algorithms to help us reduce the number of observations in the dataset. These algorithms can be grouped based on their undersampling strategy into: - Prototype generation methods. - Prototype selection methods. And within the latter, we find: - Controlled undersampling - Cleaning methods We will discuss the different algorithms throughout this document. Check also :ref:`sphx_glr_auto_examples_under-sampling_plot_comparison_under_sampling.py`. .. _cluster_centroids: Prototype generation ==================== Given an original data set :math:`S`, prototype generation algorithms will generate a new set :math:`S'` where :math:`|S'| < |S|` and :math:`S' \not\subset S`. In other words, prototype generation techniques will reduce the number of samples in the targeted classes but the remaining samples are generated --- and not selected --- from the original set. :class:`ClusterCentroids` makes use of K-means to reduce the number of samples. Therefore, each class will be synthesized with the centroids of the K-means method instead of the original samples:: >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> X, y = make_classification(n_samples=5000, n_features=2, n_informative=2, ... n_redundant=0, n_repeated=0, n_classes=3, ... n_clusters_per_class=1, ... weights=[0.01, 0.05, 0.94], ... class_sep=0.8, random_state=0) >>> print(sorted(Counter(y).items())) [(0, 64), (1, 262), (2, 4674)] >>> from imblearn.under_sampling import ClusterCentroids >>> cc = ClusterCentroids(random_state=0) >>> X_resampled, y_resampled = cc.fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 64), (1, 64), (2, 64)] The figure below illustrates such under-sampling. .. image:: ./auto_examples/under-sampling/images/sphx_glr_plot_comparison_under_sampling_001.png :target: ./auto_examples/under-sampling/plot_comparison_under_sampling.html :scale: 60 :align: center :class:`ClusterCentroids` offers an efficient way to represent the data cluster with a reduced number of samples. Keep in mind that this method requires that your data are grouped into clusters. In addition, the number of centroids should be set such that the under-sampled clusters are representative of the original one. .. warning:: :class:`ClusterCentroids` supports sparse matrices. However, the new samples generated are not specifically sparse. Therefore, even if the resulting matrix will be sparse, the algorithm will be inefficient in this regard. Prototype selection =================== Prototype selection algorithms will select samples from the original set :math:`S`, generating a dataset :math:`S'`, where :math:`|S'| < |S|` and :math:`S' \subset S`. In other words, :math:`S'` is a subset of :math:`S`. Prototype selection algorithms can be divided into two groups: (i) controlled under-sampling techniques and (ii) cleaning under-sampling techniques. Controlled under-sampling methods reduce the number of observations in the majority class or classes to an arbitrary number of samples specified by the user. Typically, they reduce the number of observations to the number of samples observed in the minority class. In contrast, cleaning under-sampling techniques "clean" the feature space by removing either "noisy" or "too easy to classify" observations, depending on the method. The final number of observations in each class varies with the cleaning method and can't be specified by the user. .. _controlled_under_sampling: Controlled under-sampling techniques ------------------------------------ Controlled under-sampling techniques reduce the number of observations from the targeted classes to a number specified by the user. Random under-sampling ^^^^^^^^^^^^^^^^^^^^^ :class:`RandomUnderSampler` is a fast and easy way to balance the data by randomly selecting a subset of data for the targeted classes:: >>> from imblearn.under_sampling import RandomUnderSampler >>> rus = RandomUnderSampler(random_state=0) >>> X_resampled, y_resampled = rus.fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 64), (1, 64), (2, 64)] .. image:: ./auto_examples/under-sampling/images/sphx_glr_plot_comparison_under_sampling_002.png :target: ./auto_examples/under-sampling/plot_comparison_under_sampling.html :scale: 60 :align: center :class:`RandomUnderSampler` allows bootstrapping the data by setting ``replacement`` to ``True``. When there are multiple classes, each targeted class is under-sampled independently:: >>> import numpy as np >>> print(np.vstack([tuple(row) for row in X_resampled]).shape) (192, 2) >>> rus = RandomUnderSampler(random_state=0, replacement=True) >>> X_resampled, y_resampled = rus.fit_resample(X, y) >>> print(np.vstack(np.unique([tuple(row) for row in X_resampled], axis=0)).shape) (181, 2) :class:`RandomUnderSampler` handles heterogeneous data types, i.e. numerical, categorical, dates, etc.:: >>> X_hetero = np.array([['xxx', 1, 1.0], ['yyy', 2, 2.0], ['zzz', 3, 3.0]], ... dtype=object) >>> y_hetero = np.array([0, 0, 1]) >>> X_resampled, y_resampled = rus.fit_resample(X_hetero, y_hetero) >>> print(X_resampled) [['xxx' 1 1.0] ['zzz' 3 3.0]] >>> print(y_resampled) [0 1] :class:`RandomUnderSampler` also supports pandas dataframes as input for undersampling:: >>> from sklearn.datasets import fetch_openml >>> df_adult, y_adult = fetch_openml( ... 'adult', version=2, as_frame=True, return_X_y=True) >>> df_adult.head() # doctest: +SKIP >>> df_resampled, y_resampled = rus.fit_resample(df_adult, y_adult) >>> df_resampled.head() # doctest: +SKIP :class:`NearMiss` adds some heuristic rules to select samples :cite:`mani2003knn`. :class:`NearMiss` implements 3 different types of heuristic which can be selected with the parameter ``version``:: >>> from imblearn.under_sampling import NearMiss >>> nm1 = NearMiss(version=1) >>> X_resampled_nm1, y_resampled = nm1.fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 64), (1, 64), (2, 64)] As later stated in the next section, :class:`NearMiss` heuristic rules are based on nearest neighbors algorithm. Therefore, the parameters ``n_neighbors`` and ``n_neighbors_ver3`` accept classifier derived from ``KNeighborsMixin`` from scikit-learn. The former parameter is used to compute the average distance to the neighbors while the latter is used for the pre-selection of the samples of interest. Mathematical formulation ^^^^^^^^^^^^^^^^^^^^^^^^ Let *positive samples* be the samples belonging to the targeted class to be under-sampled. *Negative sample* refers to the samples from the minority class (i.e., the most under-represented class). NearMiss-1 selects the positive samples for which the average distance to the :math:`N` closest samples of the negative class is the smallest. .. image:: ./auto_examples/under-sampling/images/sphx_glr_plot_illustration_nearmiss_001.png :target: ./auto_examples/under-sampling/plot_illustration_nearmiss.html :scale: 60 :align: center NearMiss-2 selects the positive samples for which the average distance to the :math:`N` farthest samples of the negative class is the smallest. .. image:: ./auto_examples/under-sampling/images/sphx_glr_plot_illustration_nearmiss_002.png :target: ./auto_examples/under-sampling/plot_illustration_nearmiss.html :scale: 60 :align: center NearMiss-3 is a 2-steps algorithm. First, for each negative sample, their :math:`M` nearest-neighbors will be kept. Then, the positive samples selected are the one for which the average distance to the :math:`N` nearest-neighbors is the largest. .. image:: ./auto_examples/under-sampling/images/sphx_glr_plot_illustration_nearmiss_003.png :target: ./auto_examples/under-sampling/plot_illustration_nearmiss.html :scale: 60 :align: center In the next example, the different :class:`NearMiss` variant are applied on the previous toy example. It can be seen that the decision functions obtained in each case are different. When under-sampling a specific class, NearMiss-1 can be altered by the presence of noise. In fact, it will implied that samples of the targeted class will be selected around these samples as it is the case in the illustration below for the yellow class. However, in the normal case, samples next to the boundaries will be selected. NearMiss-2 will not have this effect since it does not focus on the nearest samples but rather on the farthest samples. We can imagine that the presence of noise can also altered the sampling mainly in the presence of marginal outliers. NearMiss-3 is probably the version which will be less affected by noise due to the first step sample selection. .. image:: ./auto_examples/under-sampling/images/sphx_glr_plot_comparison_under_sampling_003.png :target: ./auto_examples/under-sampling/plot_comparison_under_sampling.html :scale: 60 :align: center Cleaning under-sampling techniques ---------------------------------- Cleaning under-sampling methods "clean" the feature space by removing either "noisy" observations or observations that are "too easy to classify", depending on the method. The final number of observations in each targeted class varies with the cleaning method and cannot be specified by the user. .. _tomek_links: Tomek's links ^^^^^^^^^^^^^ A Tomek's link exists when two samples from different classes are closest neighbors to each other. Mathematically, a Tomek's link between two samples from different classes :math:`x` and :math:`y` is defined such that for any sample :math:`z`: .. math:: d(x, y) < d(x, z) \text{ and } d(x, y) < d(y, z) where :math:`d(.)` is the distance between the two samples. :class:`TomekLinks` detects and removes Tomek's links :cite:`tomek1976two`. The underlying idea is that Tomek's links are noisy or hard to classify observations and would not help the algorithm find a suitable discrimination boundary. In the following figure, a Tomek's link between an observation of class :math:`+` and class :math:`-` is highlighted in green: .. image:: ./auto_examples/under-sampling/images/sphx_glr_plot_illustration_tomek_links_001.png :target: ./auto_examples/under-sampling/plot_illustration_tomek_links.html :scale: 60 :align: center When :class:`TomekLinks` finds a Tomek's link, it can either remove the sample of the majority class, or both. The parameter ``sampling_strategy`` controls which samples from the link will be removed. By default (i.e., ``sampling_strategy='auto'``), it will remove the sample from the majority class. Both samples, that is that from the majority and the one from the minority class, can be removed by setting ``sampling_strategy`` to ``'all'``. The following figure illustrates this behaviour: on the left, only the sample from the majority class is removed, whereas on the right, the entire Tomek's link is removed. .. image:: ./auto_examples/under-sampling/images/sphx_glr_plot_illustration_tomek_links_002.png :target: ./auto_examples/under-sampling/plot_illustration_tomek_links.html :scale: 60 :align: center .. _edited_nearest_neighbors: Editing data using nearest neighbours ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Edited nearest neighbours ~~~~~~~~~~~~~~~~~~~~~~~~~ The edited nearest neighbours methodology uses K-Nearest Neighbours to identify the neighbours of the targeted class samples, and then removes observations if any or most of their neighbours are from a different class :cite:`wilson1972asymptotic`. :class:`EditedNearestNeighbours` carries out the following steps: 1. Train a K-Nearest neighbours using the entire dataset. 2. Find each observations' K closest neighbours (only for the targeted classes). 3. Remove observations if any or most of its neighbours belong to a different class. Below the code implementation:: >>> sorted(Counter(y).items()) [(0, 64), (1, 262), (2, 4674)] >>> from imblearn.under_sampling import EditedNearestNeighbours >>> enn = EditedNearestNeighbours() >>> X_resampled, y_resampled = enn.fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 64), (1, 213), (2, 4568)] To paraphrase step 3, :class:`EditedNearestNeighbours` will retain observations from the majority class when **most**, or **all** of its neighbours are from the same class. To control this behaviour we set ``kind_sel='mode'`` or ``kind_sel='all'``, respectively. Hence, `kind_sel='all'` is less conservative than `kind_sel='mode'`, resulting in the removal of more samples:: >>> enn = EditedNearestNeighbours(kind_sel="all") >>> X_resampled, y_resampled = enn.fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 64), (1, 213), (2, 4568)] >>> enn = EditedNearestNeighbours(kind_sel="mode") >>> X_resampled, y_resampled = enn.fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 64), (1, 234), (2, 4666)] The parameter ``n_neighbors`` accepts integers. The integer refers to the number of neighbours to examine for each sample. It can also take a classifier subclassed from ``KNeighborsMixin`` from scikit-learn. When passing a classifier, note that, if you pass a 3-Nearest Neighbors classifier, only 2 neighbours will be examined for the cleaning, as the third sample is the one being examined for undersampling since it is part of the samples provided at `fit`. Repeated Edited Nearest Neighbours ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :class:`RepeatedEditedNearestNeighbours` extends :class:`EditedNearestNeighbours` by repeating the algorithm multiple times :cite:`tomek1976experiment`. Generally, repeating the algorithm will delete more data:: >>> from imblearn.under_sampling import RepeatedEditedNearestNeighbours >>> renn = RepeatedEditedNearestNeighbours() >>> X_resampled, y_resampled = renn.fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 64), (1, 208), (2, 4551)] The user can set up the number of times the edited nearest neighbours method should be repeated through the parameter `max_iter`. The repetitions will stop when: 1. the maximum number of iterations is reached, or 2. no more observations are removed, or 3. one of the majority classes becomes a minority class, or 4. one of the majority classes disappears during the undersampling. All KNN ~~~~~~~ :class:`AllKNN` is a variation of the :class:`RepeatedEditedNearestNeighbours` where the number of neighbours evaluated at each round of :class:`EditedNearestNeighbours` increases. It starts by editing based on 1-Nearest Neighbour, and it increases the neighbourhood by 1 at each iteration :cite:`tomek1976experiment`:: >>> from imblearn.under_sampling import AllKNN >>> allknn = AllKNN() >>> X_resampled, y_resampled = allknn.fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 64), (1, 220), (2, 4601)] :class:`AllKNN` stops cleaning when the maximum number of neighbours to examine, which is determined by the user through the parameter `n_neighbors` is reached, or when the majority class becomes the minority class. In the example below, we see that :class:`EditedNearestNeighbours`, :class:`RepeatedEditedNearestNeighbours` and :class:`AllKNN` have similar impact when cleaning "noisy" samples at the boundaries between classes. .. image:: ./auto_examples/under-sampling/images/sphx_glr_plot_comparison_under_sampling_004.png :target: ./auto_examples/under-sampling/plot_comparison_under_sampling.html :scale: 60 :align: center .. _condensed_nearest_neighbors: Condensed nearest neighbors ^^^^^^^^^^^^^^^^^^^^^^^^^^^ :class:`CondensedNearestNeighbour` uses a 1 nearest neighbor rule to iteratively decide if a sample should be removed :cite:`hart1968condensed`. The algorithm runs as follows: 1. Get all minority samples in a set :math:`C`. 2. Add a sample from the targeted class (class to be under-sampled) in :math:`C` and all other samples of this class in a set :math:`S`. 3. Train a 1-Nearest Neigbhour on :math:`C`. 4. Go through the samples in set :math:`S`, sample by sample, and classify each one using a 1 nearest neighbor rule (trained in 3). 5. If the sample is misclassified, add it to :math:`C`, and go to step 6. 6. Repeat steps 3 to 5 until all observations in :math:`S` have been examined. The final dataset is :math:`S`, containing all observations from the minority class and those from the majority that were miss-classified by the successive 1-Nearest Neigbhour algorithms. The :class:`CondensedNearestNeighbour` can be used in the following manner:: >>> from imblearn.under_sampling import CondensedNearestNeighbour >>> cnn = CondensedNearestNeighbour(random_state=0) >>> X_resampled, y_resampled = cnn.fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 64), (1, 24), (2, 115)] :class:`CondensedNearestNeighbour` is sensitive to noise and may add noisy samples (see figure later on). One Sided Selection ~~~~~~~~~~~~~~~~~~~ In an attempt to remove the noisy observations introduced by :class:`CondensedNearestNeighbour`, :class:`OneSidedSelection` will first find the observations that are hard to classify, and then will use :class:`TomekLinks` to remove noisy samples :cite:`hart1968condensed`. :class:`OneSidedSelection` runs as follows: 1. Get all minority samples in a set :math:`C`. 2. Add a sample from the targeted class (class to be under-sampled) in :math:`C` and all other samples of this class in a set :math:`S`. 3. Train a 1-Nearest Neighbors on :math:`C`. 4. Using a 1 nearest neighbor rule trained in 3, classify all samples in set :math:`S`. 5. Add all misclassified samples to :math:`C`. 6. Remove Tomek Links from :math:`C`. The final dataset is :math:`S`, containing all observations from the minority class, plus the observations from the majority that were added at random, plus all those from the majority that were miss-classified by the 1-Nearest Neighbors algorithms. Note that differently from :class:`CondensedNearestNeighbour`, :class:`OneSidedSelection` does not train a K-Nearest Neighbors after each sample is misclassified. It uses the 1-Nearest Neighbors from step 3 to classify all samples from the majority in 1 pass. The class can be used as:: >>> from imblearn.under_sampling import OneSidedSelection >>> oss = OneSidedSelection(random_state=0) >>> X_resampled, y_resampled = oss.fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 64), (1, 174), (2, 4404)] Our implementation offers the possibility to set the number of observations to put at random in the set :math:`C` through the parameter ``n_seeds_S``. :class:`NeighbourhoodCleaningRule` will focus on cleaning the data than condensing them :cite:`laurikkala2001improving`. Therefore, it will used the union of samples to be rejected between the :class:`EditedNearestNeighbours` and the output a 3 nearest neighbors classifier. The class can be used as:: >>> from imblearn.under_sampling import NeighbourhoodCleaningRule >>> ncr = NeighbourhoodCleaningRule(n_neighbors=11) >>> X_resampled, y_resampled = ncr.fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 64), (1, 193), (2, 4535)] .. image:: ./auto_examples/under-sampling/images/sphx_glr_plot_comparison_under_sampling_005.png :target: ./auto_examples/under-sampling/plot_comparison_under_sampling.html :scale: 60 :align: center .. _instance_hardness_threshold: Additional undersampling techniques ----------------------------------- Instance hardness threshold ^^^^^^^^^^^^^^^^^^^^^^^^^^^ **Instance Hardness** is a measure of how difficult it is to classify an instance or observation correctly. In other words, hard instances are observations that are hard to classify correctly. Fundamentally, instances that are hard to classify correctly are those for which the learning algorithm or classifier produces a low probability of predicting the correct class label. If we removed these hard instances from the dataset, the logic goes, we would help the classifier better identify the different classes :cite:`smith2014instance`. :class:`InstanceHardnessThreshold` trains a classifier on the data and then removes the samples with lower probabilities :cite:`smith2014instance`. Or in other words, it retains the observations with the higher class probabilities. In our implementation, :class:`InstanceHardnessThreshold` is (almost) a controlled under-sampling method: it will retain a specific number of observations of the target class(es), which is specified by the user (see caveat below). The class can be used as:: >>> from sklearn.linear_model import LogisticRegression >>> from imblearn.under_sampling import InstanceHardnessThreshold >>> iht = InstanceHardnessThreshold(random_state=0, ... estimator=LogisticRegression()) >>> X_resampled, y_resampled = iht.fit_resample(X, y) >>> print(sorted(Counter(y_resampled).items())) [(0, 64), (1, 64), (2, 64)] :class:`InstanceHardnessThreshold` has 2 important parameters. The parameter ``estimator`` accepts any scikit-learn classifier with a method ``predict_proba``. This classifier will be used to identify the hard instances. The training is performed with cross-validation which can be specified through the parameter ``cv`. .. note:: :class:`InstanceHardnessThreshold` could almost be considered as a controlled under-sampling method. However, due to the probability outputs, it is not always possible to get the specified number of samples. The figure below shows examples of instance hardness undersampling on a toy dataset. .. image:: ./auto_examples/under-sampling/images/sphx_glr_plot_comparison_under_sampling_006.png :target: ./auto_examples/under-sampling/plot_comparison_under_sampling.html :scale: 60 :align: center scikit-learn-contrib-imbalanced-learn-8504e95/doc/user_guide.rst000066400000000000000000000010151521136113700246250ustar00rootroot00000000000000.. title:: User guide: contents .. _user_guide: ========== User Guide ========== .. Ensure that the references will be alphabetically collected last .. Check https://github.com/mcmtroffaes/sphinxcontrib-bibtex/issues/113 .. toctree:: :numbered: introduction.rst over_sampling.rst under_sampling.rst combine.rst ensemble.rst miscellaneous.rst metrics.rst model_selection.rst common_pitfalls.rst Dataset loading utilities developers_utils.rst zzz_references.rst scikit-learn-contrib-imbalanced-learn-8504e95/doc/whats_new.rst000066400000000000000000000011021521136113700244660ustar00rootroot00000000000000.. currentmodule:: imblearn =============== Release history =============== .. include:: whats_new/v0.15.rst .. include:: whats_new/v0.14.rst .. include:: whats_new/v0.13.rst .. include:: whats_new/v0.12.rst .. include:: whats_new/v0.11.rst .. include:: whats_new/v0.10.rst .. include:: whats_new/v0.9.rst .. include:: whats_new/v0.8.rst .. include:: whats_new/v0.7.rst .. include:: whats_new/v0.6.rst .. include:: whats_new/v0.5.rst .. include:: whats_new/v0.4.rst .. include:: whats_new/v0.3.rst .. include:: whats_new/v0.2.rst .. include:: whats_new/v0.1.rst scikit-learn-contrib-imbalanced-learn-8504e95/doc/whats_new/000077500000000000000000000000001521136113700237425ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/doc/whats_new/v0.1.rst000066400000000000000000000022221521136113700251560ustar00rootroot00000000000000.. _changes_0_1: Version 0.1 =========== **December 26, 2016** Changelog --------- API ~~~ - First release of the stable API. By :user;`Fernando Nogueira `, :user:`Guillaume Lemaitre `, :user:`Christos Aridas `, and :user:`Dayvid Oliveira `. New methods ~~~~~~~~~~~ * Under-sampling 1. Random majority under-sampling with replacement 2. Extraction of majority-minority Tomek links 3. Under-sampling with Cluster Centroids 4. NearMiss-(1 & 2 & 3) 5. Condensend Nearest Neighbour 6. One-Sided Selection 7. Neighboorhood Cleaning Rule 8. Edited Nearest Neighbours 9. Instance Hardness Threshold 10. Repeated Edited Nearest Neighbours * Over-sampling 1. Random minority over-sampling with replacement 2. SMOTE - Synthetic Minority Over-sampling Technique 3. bSMOTE(1 & 2) - Borderline SMOTE of types 1 and 2 4. SVM SMOTE - Support Vectors SMOTE 5. ADASYN - Adaptive synthetic sampling approach for imbalanced learning * Over-sampling followed by under-sampling 1. SMOTE + Tomek links 2. SMOTE + ENN * Ensemble sampling 1. EasyEnsemble 2. BalanceCascade scikit-learn-contrib-imbalanced-learn-8504e95/doc/whats_new/v0.10.rst000066400000000000000000000040611521136113700252410ustar00rootroot00000000000000.. _changes_0_10: Version 0.10.1 ============== **December 28, 2022** Changelog --------- Bug fixes ......... - Fix a regression in over-sampler where the string `minority` was rejected as an unvalid sampling strategy. :pr:`964` by :user:`Prakhyath Bhandary `. Version 0.10.0 ============== **December 9, 2022** Changelog --------- Bug fixes ......... - Make sure that :class:`~imblearn.utils._docstring.Substitution` is working with `python -OO` that replace `__doc__` by `None`. :pr:`953` bu :user:`Guillaume Lemaitre `. Compatibility ............. - Maintenance release for be compatible with scikit-learn >= 1.0.2. :pr:`946`, :pr:`947`, :pr:`949` by :user:`Guillaume Lemaitre `. - Add support for automatic parameters validation as in scikit-learn >= 1.2. :pr:`955` by :user:`Guillaume Lemaitre `. - Add support for `feature_names_in_` as well as `get_feature_names_out` for all samplers. :pr:`959` by :user:`Guillaume Lemaitre `. Deprecation ........... - The parameter `n_jobs` has been deprecated from the classes :class:`~imblearn.over_sampling.ADASYN`, :class:`~imblearn.over_sampling.BorderlineSMOTE`, :class:`~imblearn.over_sampling.SMOTE`, :class:`~imblearn.over_sampling.SMOTENC`, :class:`~imblearn.over_sampling.SMOTEN`, and :class:`~imblearn.over_sampling.SVMSMOTE`. Instead, pass a nearest neighbors estimator where `n_jobs` is set. :pr:`887` by :user:`Guillaume Lemaitre `. - The parameter `base_estimator` is deprecated and will be removed in version 0.12. It is impacted the following classes: :class:`~imblearn.ensemble.BalancedBaggingClassifier`, :class:`~imblearn.ensemble.EasyEnsembleClassifier`, :class:`~imblearn.ensemble.RUSBoostClassifier`. :pr:`946` by :user:`Guillaume Lemaitre `. Enhancements ............ - Add support to accept compatible `NearestNeighbors` objects by only duck-typing. For instance, it allows to accept cuML instances. :pr:`858` by :user:`NV-jpt ` and :user:`Guillaume Lemaitre `. scikit-learn-contrib-imbalanced-learn-8504e95/doc/whats_new/v0.11.rst000066400000000000000000000055121521136113700252440ustar00rootroot00000000000000.. _changes_0_11: Version 0.11.0 ============== **July 8, 2023** Changelog --------- Bug fixes ......... - Fix a bug in :func:`~imblearn.metrics.classification_report_imbalanced` where the parameter `target_names` was not taken into account when `output_dict=True`. :pr:`989` by :user:`AYY7 `. - :class:`~imblearn.over_sampling.SMOTENC` now handles mix types of data type such as `bool` and `pd.category` by delegating the conversion to scikit-learn encoder. :pr:`1002` by :user:`Guillaume Lemaitre `. - Handle sparse matrices in :class:`~imblearn.over_sampling.SMOTEN` and raise a warning since it requires a conversion to dense matrices. :pr:`1003` by :user:`Guillaume Lemaitre `. - Remove spurious warning raised when minority class get over-sampled more than the number of sample in the majority class. :pr:`1007` by :user:`Guillaume Lemaitre `. Compatibility ............. - Maintenance release for being compatible with scikit-learn >= 1.3.0. :pr:`999` by :user:`Guillaume Lemaitre `. Deprecation ........... - The fitted attribute `ohe_` in :class:`~imblearn.over_sampling.SMOTENC` is deprecated and will be removed in version 0.13. Use `categorical_encoder_` instead. :pr:`1000` by :user:`Guillaume Lemaitre `. - The default of the parameters `sampling_strategy`, `bootstrap` and `replacement` will change in :class:`~imblearn.ensemble.BalancedRandomForestClassifier` to follow the implementation of the original paper. This changes will take effect in version 0.13. :pr:`1006` by :user:`Guillaume Lemaitre `. Enhancements ............ - :class:`~imblearn.over_sampling.SMOTENC` now accepts a parameter `categorical_encoder` allowing to specify a :class:`~sklearn.preprocessing.OneHotEncoder` with custom parameters. :pr:`1000` by :user:`Guillaume Lemaitre `. - :class:`~imblearn.over_sampling.SMOTEN` now accepts a parameter `categorical_encoder` allowing to specify a :class:`~sklearn.preprocessing.OrdinalEncoder` with custom parameters. A new fitted parameter `categorical_encoder_` is exposed to access the fitted encoder. :pr:`1001` by :user:`Guillaume Lemaitre `. - :class:`~imblearn.under_sampling.RandomUnderSampler` and :class:`~imblearn.over_sampling.RandomOverSampler` (when `shrinkage is not None`) now accept any data types and will not attempt any data conversion. :pr:`1004` by :user:`Guillaume Lemaitre `. - :class:`~imblearn.over_sampling.SMOTENC` now support passing array-like of `str` when passing the `categorical_features` parameter. :pr:`1008` by :user`Guillaume Lemaitre `. - :class:`~imblearn.over_sampling.SMOTENC` now support automatic categorical inference when `categorical_features` is set to `"auto"`. :pr:`1009` by :user`Guillaume Lemaitre `. scikit-learn-contrib-imbalanced-learn-8504e95/doc/whats_new/v0.12.rst000066400000000000000000000105311521136113700252420ustar00rootroot00000000000000.. _changes_0_12: Version 0.12.4 ============== **October 4, 2024** Changelog --------- Compatibility ............. - Compatibility with NumPy 2.0+ :pr:`1097` by :user:`Guillaume Lemaitre `. Version 0.12.3 ============== **May 28, 2024** Changelog --------- Compatibility ............. - Compatibility with scikit-learn 1.5 :pr:`1074` and :pr:`1084` by :user:`Guillaume Lemaitre `. Version 0.12.2 ============== **March 31, 2024** Changelog --------- Bug fixes ......... - Fix the way we check for a specific Python version in the test suite. :pr:`1075` by :user:`Guillaume Lemaitre `. Version 0.12.1 ============== **March 31, 2024** Changelog --------- Bug fixes ......... - Fix a bug in :class:`~imblearn.under_sampling.InstanceHardnessThreshold` where `estimator` could not be a :class:`~sklearn.pipeline.Pipeline` object. :pr:`1049` by :user:`Gonenc Mogol `. Compatibility ............. - Do not use `distutils` in tests due to deprecation. :pr:`1065` by :user:`Michael R. Crusoe `. - Fix the scikit-learn import in tests to be compatible with version 1.4.1.post1. :pr:`1073` by :user:`Guillaume Lemaitre `. - Fix test to be compatible with Python 3.13. :pr:`1073` by :user:`Guillaume Lemaitre `. Version 0.12.0 ============== **January 24, 2024** Changelog --------- Bug fixes ......... - Fix a bug in :class:`~imblearn.over_sampling.SMOTENC` where the entries of the one-hot encoding should be divided by `sqrt(2)` and not `2`, taking into account that they are plugged into an Euclidean distance computation. :pr:`1014` by :user:`Guillaume Lemaitre `. - Raise an informative error message when all support vectors are tagged as noise in :class:`~imblearn.over_sampling.SVMSMOTE`. :pr:`1016` by :user:`Guillaume Lemaitre `. - Fix a bug in :class:`~imblearn.over_sampling.SMOTENC` where the median of standard deviation of the continuous features was only computed on the minority class. Now, we are computing this statistic for each class that is up-sampled. :pr:`1015` by :user:`Guillaume Lemaitre `. - Fix a bug in :class:`~imblearn.over_sampling.SMOTENC` such that the case where the median of standard deviation of the continuous features is null is handled in the multiclass case as well. :pr:`1015` by :user:`Guillaume Lemaitre `. - Fix a bug in :class:`~imblearn.over_sampling.BorderlineSMOTE` version 2 where samples should be generated from the whole dataset and not only from the minority class. :pr:`1023` by :user:`Guillaume Lemaitre `. - Fix a bug in :class:`~imblearn.under_sampling.NeighbourhoodCleaningRule` where the `kind_sel="all"` was not working as explained in the literature. :pr:`1012` by :user:`Guillaume Lemaitre `. - Fix a bug in :class:`~imblearn.under_sampling.NeighbourhoodCleaningRule` where the `threshold_cleaning` ratio was multiplied on the total number of samples instead of the number of samples in the minority class. :pr:`1012` by :user:`Guillaume Lemaitre `. - Fix a bug in :class:`~imblearn.under_sampling.RandomUnderSampler` and :class:`~imblearn.over_sampling.RandomOverSampler` where a column containing only NaT was not handled correctly. :pr:`1059` by :user:`Guillaume Lemaitre `. Compatibility ............. - :class:`~imblearn.ensemble.BalancedRandomForestClassifier` now support missing values and monotonic constraints if scikit-learn >= 1.4 is installed. - :class:`~imblearn.pipeline.Pipeline` support metadata routing if scikit-learn >= 1.4 is installed. - Compatibility with scikit-learn 1.4. :pr:`1058` by :user:`Guillaume Lemaitre `. Deprecations ............ - Deprecate `estimator_` argument in favor of `estimators_` for the classes :class:`~imblearn.under_sampling.CondensedNearestNeighbour` and :class:`~imblearn.under_sampling.OneSidedSelection`. `estimator_` will be removed in 0.14. :pr:`1011` by :user:`Guillaume Lemaitre `. - Deprecate `kind_sel` in :class:`~imblearn.under_sampling.NeighbourhoodCleaningRule. It will be removed in 0.14. The parameter does not have any effect. :pr:`1012` by :user:`Guillaume Lemaitre `. Enhancements ............ - Allows to output dataframe with sparse format if provided as input. :pr:`1059` by :user:`ts2095 `. scikit-learn-contrib-imbalanced-learn-8504e95/doc/whats_new/v0.13.rst000066400000000000000000000016231521136113700252450ustar00rootroot00000000000000.. _changes_0_13: Version 0.13.0 ============== **December 20, 2024** Changelog --------- Bug fixes ......... - Fix `get_metadata_routing` in :class:`~imblearn.pipeline.Pipeline` such that one can use a sampler with metadata routing. :pr:`1115` by :user:`Guillaume Lemaitre `. Compatibility ............. - Compatibility with scikit-learn 1.6 :pr:`1109` by :user:`Guillaume Lemaitre `. Deprecations ............ - :class:`~imblearn.pipeline.Pipeline` now uses :func:`~sklearn.utils.check_is_fitted` instead of :func:`~sklearn.utils.check_fitted` to check if the pipeline is fitted. In 0.15, it will raise an error instead of a warning. :pr:`1109` by :user:`Guillaume Lemaitre `. - `algorithm` parameter in :class:`~imblearn.ensemble.RUSBoostClassifier` is now deprecated and will be removed in 0.14. :pr:`1109` by :user:`Guillaume Lemaitre `. scikit-learn-contrib-imbalanced-learn-8504e95/doc/whats_new/v0.14.rst000066400000000000000000000024431521136113700252470ustar00rootroot00000000000000.. _changes_0_14: Version 0.14.2 ============== **June 7, 2026** Changelog --------- Maintenance ........... - Compatibility with scikit-learn 1.9 :pr:`1178` by :user:`Guillaume Lemaitre `. Bug fixes ......... - Raise an informative error in :class:`~imblearn.over_sampling.SMOTENC` when the provided `categorical_encoder` does not keep one column per category (e.g. `OneHotEncoder(drop=...)` or an encoder merging infrequent categories), instead of failing with an opaque ``zero-size array`` NumPy error. :issue:`1035` by :user:`Imran Ahamed `. - Compatibility Version 0.14.1 ============== **December 21, 2025** Changelog --------- Maintenance ........... - Compatibility with scikit-learn 1.8 :pr:`1158` by :user:`Guillaume Lemaitre ` and :user:`stratakis `. Version 0.14.0 ============== **August 14, 2025** Changelog --------- Bug fixes ......... Enhancements ............ - Add :class:`~imblearn.model_selection.InstanceHardnessCV` to split data and ensure that samples are distributed in folds based on their instance hardness. :pr:`1125` by :user:`Frits Hermans `. Compatibility ............. - Compatibility with scikit-learn 1.7 :pr:`1137`, :pr:`1145`, :pr:`1146` by :user:`Guillaume Lemaitre `. scikit-learn-contrib-imbalanced-learn-8504e95/doc/whats_new/v0.15.rst000066400000000000000000000003371521136113700252500ustar00rootroot00000000000000.. _changes_0_15: Version 0.15.dev0 (In development) ================================== **TBD** Changelog --------- Bug fixes ......... Enhancements ............ Compatibility ............. Deprecations ............ scikit-learn-contrib-imbalanced-learn-8504e95/doc/whats_new/v0.2.rst000066400000000000000000000136641521136113700251730ustar00rootroot00000000000000.. _changes_0_2: Version 0.2 =========== **January 1, 2017** Changelog --------- Bug fixes ~~~~~~~~~ - Fixed a bug in :class:`under_sampling.NearMiss` which was not picking the right samples during under sampling for the method 3. By :user:`Guillaume Lemaitre `. - Fixed a bug in :class:`ensemble.EasyEnsemble`, correction of the `random_state` generation. By :user:`Guillaume Lemaitre ` and :user:`Christos Aridas `. - Fixed a bug in :class:`under_sampling.RepeatedEditedNearestNeighbours`, add additional stopping criterion to avoid that the minority class become a majority class or that a class disappear. By :user:`Guillaume Lemaitre `. - Fixed a bug in :class:`under_sampling.AllKNN`, add stopping criteria to avoid that the minority class become a majority class or that a class disappear. By :user:`Guillaume Lemaitre `. - Fixed a bug in :class:`under_sampling.CondensedNeareastNeigbour`, correction of the list of indices returned. By :user:`Guillaume Lemaitre `. - Fixed a bug in :class:`ensemble.BalanceCascade`, solve the issue to obtain a single array if desired. By :user:`Guillaume Lemaitre `. - Fixed a bug in :class:`pipeline.Pipeline`, solve to embed `Pipeline` in other `Pipeline`. :issue:`231` by :user:`Christos Aridas `. - Fixed a bug in :class:`pipeline.Pipeline`, solve the issue to put to sampler in the same `Pipeline`. :issue:`188` by :user:`Christos Aridas `. - Fixed a bug in :class:`under_sampling.CondensedNeareastNeigbour`, correction of the shape of `sel_x` when only one sample is selected. By :user:`Aliaksei Halachkin `. - Fixed a bug in :class:`under_sampling.NeighbourhoodCleaningRule`, selecting neighbours instead of minority class misclassified samples. :issue:`230` by :user:`Aleksandr Loskutov `. - Fixed a bug in :class:`over_sampling.ADASYN`, correction of the creation of a new sample so that the new sample lies between the minority sample and the nearest neighbour. :issue:`235` by :user:`Rafael Wampfler `. New features ~~~~~~~~~~~~ - Added AllKNN under sampling technique. By :user:`Dayvid Oliveira `. - Added a module `metrics` implementing some specific scoring function for the problem of balancing. :issue:`204` by :user:`Guillaume Lemaitre ` and :user:`Christos Aridas `. Enhancement ~~~~~~~~~~~ - Added support for bumpversion. By :user:`Guillaume Lemaitre `. - Validate the type of target in binary samplers. A warning is raised for the moment. By :user:`Guillaume Lemaitre ` and :user:`Christos Aridas `. - Change from `cross_validation` module to `model_selection` module for `sklearn` deprecation cycle. By :user:`Dayvid Oliveira ` and :user:`Christos Aridas `. API changes summary ~~~~~~~~~~~~~~~~~~~ - `size_ngh` has been deprecated in :class:`combine.SMOTEENN`. Use `n_neighbors` instead. By :user:`Guillaume Lemaitre `, :user:`Christos Aridas `, and :user:`Dayvid Oliveira `. - `size_ngh` has been deprecated in :class:`under_sampling.EditedNearestNeighbors`. Use `n_neighbors` instead. By :user:`Guillaume Lemaitre `, :user:`Christos Aridas `, and :user:`Dayvid Oliveira `. - `size_ngh` has been deprecated in :class:`under_sampling.CondensedNeareastNeigbour`. Use `n_neighbors` instead. By :user:`Guillaume Lemaitre `, :user:`Christos Aridas `, and :user:`Dayvid Oliveira `. - `size_ngh` has been deprecated in :class:`under_sampling.OneSidedSelection`. Use `n_neighbors` instead. By :user:`Guillaume Lemaitre `, :user:`Christos Aridas `, and :user:`Dayvid Oliveira `. - `size_ngh` has been deprecated in :class:`under_sampling.NeighbourhoodCleaningRule`. Use `n_neighbors` instead. By :user:`Guillaume Lemaitre `, :user:`Christos Aridas `, and :user:`Dayvid Oliveira `. - `size_ngh` has been deprecated in :class:`under_sampling.RepeatedEditedNearestNeighbours`. Use `n_neighbors` instead. By :user:`Guillaume Lemaitre `, :user:`Christos Aridas `, and :user:`Dayvid Oliveira `. - `size_ngh` has been deprecated in :class:`under_sampling.AllKNN`. Use `n_neighbors` instead. By :user:`Guillaume Lemaitre `, :user:`Christos Aridas `, and :user:`Dayvid Oliveira `. - Two base classes :class:`BaseBinaryclassSampler` and :class:`BaseMulticlassSampler` have been created to handle the target type and raise warning in case of abnormality. By :user:`Guillaume Lemaitre ` and :user:`Christos Aridas `. - Move `random_state` to be assigned in the :class:`SamplerMixin` initialization. By :user:`Guillaume Lemaitre `. - Provide estimators instead of parameters in :class:`combine.SMOTEENN` and :class:`combine.SMOTETomek`. Therefore, the list of parameters have been deprecated. By :user:`Guillaume Lemaitre ` and :user:`Christos Aridas `. - `k` has been deprecated in :class:`over_sampling.ADASYN`. Use `n_neighbors` instead. :issue:`183` by :user:`Guillaume Lemaitre `. - `k` and `m` have been deprecated in :class:`over_sampling.SMOTE`. Use `k_neighbors` and `m_neighbors` instead. :issue:`182` by :user:`Guillaume Lemaitre `. - `n_neighbors` accept `KNeighborsMixin` based object for :class:`under_sampling.EditedNearestNeighbors`, :class:`under_sampling.CondensedNeareastNeigbour`, :class:`under_sampling.NeighbourhoodCleaningRule`, :class:`under_sampling.RepeatedEditedNearestNeighbours`, and :class:`under_sampling.AllKNN`. :issue:`109` by :user:`Guillaume Lemaitre `. Documentation changes ~~~~~~~~~~~~~~~~~~~~~ - Replace some remaining `UnbalancedDataset` occurences. By :user:`Francois Magimel `. - Added doctest in the documentation. By :user:`Guillaume Lemaitre `. scikit-learn-contrib-imbalanced-learn-8504e95/doc/whats_new/v0.3.rst000066400000000000000000000064741521136113700251750ustar00rootroot00000000000000.. _changes_0_3: Version 0.3 =========== **February 22, 2018** Changelog --------- Testing ~~~~~~~ - Pytest is used instead of nosetests. :issue:`321` by :user:`Joan Massich `. Documentation ~~~~~~~~~~~~~ - Added a User Guide and extended some examples. :issue:`295` by :user:`Guillaume Lemaitre `. Bug fixes ~~~~~~~~~ - Fixed a bug in :func:`utils.check_ratio` such that an error is raised when the number of samples required is negative. :issue:`312` by :user:`Guillaume Lemaitre `. - Fixed a bug in :class:`under_sampling.NearMiss` version 3. The indices returned were wrong. :issue:`312` by :user:`Guillaume Lemaitre `. - Fixed bug for :class:`ensemble.BalanceCascade` and :class:`combine.SMOTEENN` and :class:`SMOTETomek`. :issue:`295` by :user:`Guillaume Lemaitre `. - Fixed bug for `check_ratio` to be able to pass arguments when `ratio` is a callable. :issue:`307` by :user:`Guillaume Lemaitre `. New features ~~~~~~~~~~~~ - Turn off steps in :class:`pipeline.Pipeline` using the `None` object. By :user:`Christos Aridas `. - Add a fetching function :func:`datasets.fetch_datasets` in order to get some imbalanced datasets useful for benchmarking. :issue:`249` by :user:`Guillaume Lemaitre `. Enhancement ~~~~~~~~~~~ - All samplers accepts sparse matrices with defaulting on CSR type. :issue:`316` by :user:`Guillaume Lemaitre `. - :func:`datasets.make_imbalance` take a ratio similarly to other samplers. It supports multiclass. :issue:`312` by :user:`Guillaume Lemaitre `. - All the unit tests have been factorized and a :func:`utils.check_estimators` has been derived from scikit-learn. By :user:`Guillaume Lemaitre `. - Script for automatic build of conda packages and uploading. :issue:`242` by :user:`Guillaume Lemaitre ` - Remove seaborn dependence and improve the examples. :issue:`264` by :user:`Guillaume Lemaitre `. - adapt all classes to multi-class resampling. :issue:`290` by :user:`Guillaume Lemaitre ` API changes summary ~~~~~~~~~~~~~~~~~~~ - `__init__` has been removed from the :class:`base.SamplerMixin` to create a real mixin class. :issue:`242` by :user:`Guillaume Lemaitre `. - creation of a module :mod:`exceptions` to handle consistant raising of errors. :issue:`242` by :user:`Guillaume Lemaitre `. - creation of a module ``utils.validation`` to make checking of recurrent patterns. :issue:`242` by :user:`Guillaume Lemaitre `. - move the under-sampling methods in ``prototype_selection`` and ``prototype_generation`` submodule to make a clearer dinstinction. :issue:`277` by :user:`Guillaume Lemaitre `. - change ``ratio`` such that it can adapt to multiple class problems. :issue:`290` by :user:`Guillaume Lemaitre `. Deprecation ~~~~~~~~~~~ - Deprecation of the use of ``min_c_`` in :func:`datasets.make_imbalance`. :issue:`312` by :user:`Guillaume Lemaitre ` - Deprecation of the use of float in :func:`datasets.make_imbalance` for the ratio parameter. :issue:`290` by :user:`Guillaume Lemaitre `. - deprecate the use of float as ratio in favor of dictionary, string, or callable. :issue:`290` by :user:`Guillaume Lemaitre `. scikit-learn-contrib-imbalanced-learn-8504e95/doc/whats_new/v0.4.rst000066400000000000000000000206611521136113700251700ustar00rootroot00000000000000.. _changes_0_4: Version 0.4.2 ============= **October 21, 2018** Changelog --------- Bug fixes ......... - Fix a bug in :class:`imblearn.over_sampling.SMOTENC` in which the the median of the standard deviation instead of half of the median of the standard deviation. By :user:`Guillaume Lemaitre ` in :issue:`491`. - Raise an error when passing target which is not supported, i.e. regression target or multilabel targets. Imbalanced-learn does not support this case. By :user:`Guillaume Lemaitre ` in :issue:`490`. - Fix a bug in :class:`imblearn.over_sampling.SMOTENC` in which a sparse matrices were densify during ``inverse_transform``. By :user:`Guillaume Lemaitre ` in :issue:`495`. - Fix a bug in :class:`imblearn.over_sampling.SMOTE_NC` in which a the tie breaking was wrongly sampling. By :user:`Guillaume Lemaitre ` in :issue:`497`. Version 0.4 =========== **October 12, 2018** .. warning:: Version 0.4 is the last version of imbalanced-learn to support Python 2.7 and Python 3.4. Imbalanced-learn 0.5 will require Python 3.5 or higher. Highlights ---------- This release brings its set of new feature as well as some API changes to strengthen the foundation of imbalanced-learn. As new feature, 2 new modules :mod:`imblearn.keras` and :mod:`imblearn.tensorflow` have been added in which imbalanced-learn samplers can be used to generate balanced mini-batches. The module :mod:`imblearn.ensemble` has been consolidated with new classifier: :class:`imblearn.ensemble.BalancedRandomForestClassifier`, :class:`imblearn.ensemble.EasyEnsembleClassifier`, :class:`imblearn.ensemble.RUSBoostClassifier`. Support for string has been added in :class:`imblearn.over_sampling.RandomOverSampler` and :class:`imblearn.under_sampling.RandomUnderSampler`. In addition, a new class :class:`imblearn.over_sampling.SMOTENC` allows to generate sample with data sets containing both continuous and categorical features. The :class:`imblearn.over_sampling.SMOTE` has been simplified and break down to 2 additional classes: :class:`imblearn.over_sampling.SVMSMOTE` and :class:`imblearn.over_sampling.BorderlineSMOTE`. There is also some changes regarding the API: the parameter ``sampling_strategy`` has been introduced to replace the ``ratio`` parameter. In addition, the ``return_indices`` argument has been deprecated and all samplers will exposed a ``sample_indices_`` whenever this is possible. Changelog --------- API ... - Replace the parameter ``ratio`` by ``sampling_strategy``. :issue:`411` by :user:`Guillaume Lemaitre `. - Enable to use a ``float`` with binary classification for ``sampling_strategy``. :issue:`411` by :user:`Guillaume Lemaitre `. - Enable to use a ``list`` for the cleaning methods to specify the class to sample. :issue:`411` by :user:`Guillaume Lemaitre `. - Replace ``fit_sample`` by ``fit_resample``. An alias is still available for backward compatibility. In addition, ``sample`` has been removed to avoid resampling on different set of data. :issue:`462` by :user:`Guillaume Lemaitre `. New features ............ - Add a :mod:`keras` and :mod:`tensorflow` modules to create balanced mini-batches generator. :issue:`409` by :user:`Guillaume Lemaitre `. - Add :class:`imblearn.ensemble.EasyEnsembleClassifier` which create a bag of AdaBoost classifier trained on balanced bootstrap samples. :issue:`455` by :user:`Guillaume Lemaitre `. - Add :class:`imblearn.ensemble.BalancedRandomForestClassifier` which balanced each bootstrap provided to each tree of the forest. :issue:`459` by :user:`Guillaume Lemaitre `. - Add :class:`imblearn.ensemble.RUSBoostClassifier` which applied a random under-sampling stage before each boosting iteration of AdaBoost. :issue:`469` by :user:`Guillaume Lemaitre `. - Add :class:`imblern.over_sampling.SMOTENC` which generate synthetic samples on data set with heterogeneous data type (continuous and categorical features). :issue:`412` by :user:`Denis Dudnik ` and :user:`Guillaume Lemaitre `. Enhancement ........... - Add a documentation node to create a balanced random forest from a balanced bagging classifier. :issue:`372` by :user:`Guillaume Lemaitre `. - Document the metrics to evaluate models on imbalanced dataset. :issue:`367` by :user:`Guillaume Lemaitre `. - Add support for one-vs-all encoded target to support keras. :issue:`409` by :user:`Guillaume Lemaitre `. - Adding specific class for borderline and SVM SMOTE using :class:`BorderlineSMOTE` and :class:`SVMSMOTE`. :issue:`440` by :user:`Guillaume Lemaitre `. - Allow :class:`imblearn.over_sampling.RandomOverSampler` can return indices using the attributes ``return_indices``. :issue:`439` by :user:`Hugo Gascon` and :user:`Guillaume Lemaitre `. - Allow :class:`imblearn.under_sampling.RandomUnderSampler` and :class:`imblearn.over_sampling.RandomOverSampler` to sample object array containing strings. :issue:`451` by :user:`Guillaume Lemaitre `. Bug fixes ......... - Fix bug in :func:`metrics.classification_report_imbalanced` for which `y_pred` and `y_true` where inversed. :issue:`394` by :user:`Ole Silvig .` - Fix bug in ADASYN to consider only samples from the current class when generating new samples. :issue:`354` by :user:`Guillaume Lemaitre `. - Fix bug which allow for sorted behavior of ``sampling_strategy`` dictionary and thus to obtain a deterministic results when using the same random state. :issue:`447` by :user:`Guillaume Lemaitre `. - Force to clone scikit-learn estimator passed as attributes to samplers. :issue:`446` by :user:`Guillaume Lemaitre `. - Fix bug which was not preserving the dtype of X and y when generating samples. :issue:`450` by :user:`Guillaume Lemaitre `. - Add the option to pass a ``Memory`` object to :func:`make_pipeline` like in :class:`pipeline.Pipeline` class. :issue:`458` by :user:`Christos Aridas `. Maintenance ........... - Remove deprecated parameters in 0.2 - :issue:`331` by :user:`Guillaume Lemaitre `. - Make some modules private. :issue:`452` by :user:`Guillaume Lemaitre `. - Upgrade requirements to scikit-learn 0.20. :issue:`379` by :user:`Guillaume Lemaitre `. - Catch deprecation warning in testing. :issue:`441` by :user:`Guillaume Lemaitre `. - Refactor and impose `pytest` style tests. :issue:`470` by :user:`Guillaume Lemaitre `. Documentation ............. - Remove some docstring which are not necessary. :issue:`454` by :user:`Guillaume Lemaitre `. - Fix the documentation of the ``sampling_strategy`` parameters when used as a float. :issue:`480` by :user:`Guillaume Lemaitre `. Deprecation ........... - Deprecate ``ratio`` in favor of ``sampling_strategy``. :issue:`411` by :user:`Guillaume Lemaitre `. - Deprecate the use of a ``dict`` for cleaning methods. a ``list`` should be used. :issue:`411` by :user:`Guillaume Lemaitre `. - Deprecate ``random_state`` in :class:`imblearn.under_sampling.NearMiss`, :class:`imblearn.under_sampling.EditedNearestNeighbors`, :class:`imblearn.under_sampling.RepeatedEditedNearestNeighbors`, :class:`imblearn.under_sampling.AllKNN`, :class:`imblearn.under_sampling.NeighbourhoodCleaningRule`, :class:`imblearn.under_sampling.InstanceHardnessThreshold`, :class:`imblearn.under_sampling.CondensedNearestNeighbours`. - Deprecate ``kind``, ``out_step``, ``svm_estimator``, ``m_neighbors`` in :class:`imblearn.over_sampling.SMOTE`. User should use :class:`imblearn.over_sampling.SVMSMOTE` and :class:`imblearn.over_sampling.BorderlineSMOTE`. :issue:`440` by :user:`Guillaume Lemaitre `. - Deprecate :class:`imblearn.ensemble.EasyEnsemble` in favor of meta-estimator :class:`imblearn.ensemble.EasyEnsembleClassifier` which follow the exact algorithm described in the literature. :issue:`455` by :user:`Guillaume Lemaitre `. - Deprecate :class:`imblearn.ensemble.BalanceCascade`. :issue:`472` by :user:`Guillaume Lemaitre `. - Deprecate ``return_indices`` in all samplers. Instead, an attribute ``sample_indices_`` is created whenever the sampler is selecting a subset of the original samples. :issue:`474` by :user:`Guillaume Lemaitre `. - Add :class:`imblearn.over_sampling.BorderlineSMOTE` and :class:`imblearn.over_sampling.SVMSMOTE` in the API documenation. :issue:`530` by :user:`Guillaume Lemaitre `. Enhancement ........... - Add Parallelisation for SMOTEENN and SMOTETomek. :pr:`547` by :user:`Michael Hsieh `. - Add :class:`imblearn.utils._show_versions`. Updated the contribution guide and issue template showing how to print system and dependency information from the command line. :pr:`557` by :user:`Alexander L. Hayes `. - Add :class:`imblearn.over_sampling.KMeansSMOTE` which is an over-sampler clustering points before to apply SMOTE. :pr:`435` by :user:`Stephan Heijl `. Maintenance ........... - Make it possible to ``import imblearn`` and access submodule. :pr:`500` by :user:`Guillaume Lemaitre `. - Remove support for Python 2, remove deprecation warning from scikit-learn 0.21. :pr:`576` by :user:`Guillaume Lemaitre `. Bug ... - Fix wrong usage of :class:`keras.layers.BatchNormalization` in ``porto_seguro_keras_under_sampling.py`` example. The batch normalization was moved before the activation function and the bias was removed from the dense layer. :pr:`531` by :user:`Guillaume Lemaitre `. - Fix bug which converting to COO format sparse when stacking the matrices in :class:`imblearn.over_sampling.SMOTENC`. This bug was only old scipy version. :pr:`539` by :user:`Guillaume Lemaitre `. - Fix bug in :class:`imblearn.pipeline.Pipeline` where None could be the final estimator. :pr:`554` by :user:`Oliver Rausch `. - Fix bug in :class:`imblearn.over_sampling.SVMSMOTE` and :class:`imblearn.over_sampling.BorderlineSMOTE` where the default parameter of ``n_neighbors`` was not set properly. :pr:`578` by :user:`Guillaume Lemaitre `. - Fix bug by changing the default depth in :class:`imblearn.ensemble.RUSBoostClassifier` to get a decision stump as a weak learner as in the original paper. :pr:`545` by :user:`Christos Aridas `. - Allow to import ``keras`` directly from ``tensorflow`` in the :mod:`imblearn.keras`. :pr:`531` by :user:`Guillaume Lemaitre `. scikit-learn-contrib-imbalanced-learn-8504e95/doc/whats_new/v0.6.rst000066400000000000000000000115471521136113700251750ustar00rootroot00000000000000.. _changes_0_6_2: Version 0.6.2 ============== **February 16, 2020** This is a bug-fix release to resolve some issues regarding the handling the input and the output format of the arrays. Changelog --------- - Allow column vectors to be passed as targets. :pr:`673` by :user:`Christos Aridas `. - Better input/output handling for pandas, numpy and plain lists. :pr:`681` by :user:`Christos Aridas `. .. _changes_0_6_1: Version 0.6.1 ============== **December 7, 2019** This is a bug-fix release to primarily resolve some packaging issues in version 0.6.0. It also includes minor documentation improvements and some bug fixes. Changelog --------- Bug fixes ......... - Fix a bug in :class:`imblearn.ensemble.BalancedRandomForestClassifier` leading to a wrong number of samples used during fitting due `max_samples` and therefore a bad computation of the OOB score. :pr:`656` by :user:`Guillaume Lemaitre `. .. _changes_0_6: Version 0.6.0 ============= **December 5, 2019** Changelog --------- Changed models .............. The following models might give some different sampling due to changes in scikit-learn: - :class:`imblearn.under_sampling.ClusterCentroids` - :class:`imblearn.under_sampling.InstanceHardnessThreshold` The following samplers will give different results due to change linked to the random state internal usage: - :class:`imblearn.over_sampling.ADASYN` - :class:`imblearn.over_sampling.SMOTENC` Bug fixes ......... - :class:`imblearn.under_sampling.InstanceHardnessThreshold` now take into account the `random_state` and will give deterministic results. In addition, `cross_val_predict` is used to take advantage of the parallelism. :pr:`599` by :user:`Shihab Shahriar Khan `. - Fix a bug in :class:`imblearn.ensemble.BalancedRandomForestClassifier` leading to a wrong computation of the OOB score. :pr:`656` by :user:`Guillaume Lemaitre `. Maintenance ........... - Update imports from scikit-learn after that some modules have been privatize. The following import have been changed: :class:`sklearn.ensemble._base._set_random_states`, :class:`sklearn.ensemble._forest._parallel_build_trees`, :class:`sklearn.metrics._classification._check_targets`, :class:`sklearn.metrics._classification._prf_divide`, :class:`sklearn.utils.Bunch`, :class:`sklearn.utils._safe_indexing`, :class:`sklearn.utils._testing.assert_allclose`, :class:`sklearn.utils._testing.assert_array_equal`, :class:`sklearn.utils._testing.SkipTest`. :pr:`617` by :user:`Guillaume Lemaitre `. - Synchronize :mod:`imblearn.pipeline` with :mod:`sklearn.pipeline`. :pr:`620` by :user:`Guillaume Lemaitre `. - Synchronize :class:`imblearn.ensemble.BalancedRandomForestClassifier` and add parameters `max_samples` and `ccp_alpha`. :pr:`621` by :user:`Guillaume Lemaitre `. Enhancement ........... - :class:`imblearn.under_sampling.RandomUnderSampling`, :class:`imblearn.over_sampling.RandomOverSampling`, :class:`imblearn.datasets.make_imbalance` accepts Pandas DataFrame in and will output Pandas DataFrame. Similarly, it will accepts Pandas Series in and will output Pandas Series. :pr:`636` by :user:`Guillaume Lemaitre `. - :class:`imblearn.FunctionSampler` accepts a parameter ``validate`` allowing to check or not the input ``X`` and ``y``. :pr:`637` by :user:`Guillaume Lemaitre `. - :class:`imblearn.under_sampling.RandomUnderSampler`, :class:`imblearn.over_sampling.RandomOverSampler` can resample when non finite values are present in ``X``. :pr:`643` by :user:`Guillaume Lemaitre `. - All samplers will output a Pandas DataFrame if a Pandas DataFrame was given as an input. :pr:`644` by :user:`Guillaume Lemaitre `. - The samples generation in :class:`imblearn.over_sampling.ADASYN`, :class:`imblearn.over_sampling.SMOTE`, :class:`imblearn.over_sampling.BorderlineSMOTE`, :class:`imblearn.over_sampling.SVMSMOTE`, :class:`imblearn.over_sampling.KMeansSMOTE`, :class:`imblearn.over_sampling.SMOTENC` is now vectorize with giving an additional speed-up when `X` in sparse. :pr:`596` and :pr:`649` by :user:`Matt Eding `. Deprecation ........... - The following classes have been removed after 2 deprecation cycles: `ensemble.BalanceCascade` and `ensemble.EasyEnsemble`. :pr:`617` by :user:`Guillaume Lemaitre `. - The following functions have been removed after 2 deprecation cycles: `utils.check_ratio`. :pr:`617` by :user:`Guillaume Lemaitre `. - The parameter `ratio` and `return_indices` has been removed from all samplers. :pr:`617` by :user:`Guillaume Lemaitre `. - The parameters `m_neighbors`, `out_step`, `kind`, `svm_estimator` have been removed from the :class:`imblearn.over_sampling.SMOTE`. :pr:`617` by :user:`Guillaume Lemaitre `. scikit-learn-contrib-imbalanced-learn-8504e95/doc/whats_new/v0.7.rst000066400000000000000000000046041521136113700251720ustar00rootroot00000000000000.. _changes_0_7: Version 0.7.0 ============= **June 9, 2020** Changelog --------- Maintenance ........... - Ensure that :class:`imblearn.pipeline.Pipeline` is working when `memory` is activated and `joblib==0.11`. :pr:`687` by :user:`Christos Aridas `. - Refactor common test to use the dev tools from `scikit-learn` 0.23. :pr:`710` by :user:`Guillaume Lemaitre `. - Remove `FutureWarning` issued by `scikit-learn` 0.23. :pr:`710` by :user:`Guillaume Lemaitre `. - Impose keywords only argument as in `scikit-learn`. :pr:`721` by :user:`Guillaume Lemaitre `. Changed models .............. The following models might give some different results due to changes: - :class:`imblearn.ensemble.BalancedRandomForestClassifier` Bug fixes ......... - Change the default value `min_samples_leaf` to be consistent with scikit-learn. :pr:`711` by :user:`zerolfx `. - Fix a bug due to change in `scikit-learn` 0.23 in :class:`imblearn.metrics.make_index_balanced_accuracy`. The function was unusable. :pr:`710` by :user:`Guillaume Lemaitre `. - Raise a proper error message when only numerical or categorical features are given in :class:`imblearn.over_sampling.SMOTENC`. :pr:`720` by :user:`Guillaume Lemaitre `. - Fix a bug when the median of the standard deviation is null in :class:`imblearn.over_sampling.SMOTENC`. :pr:`675` by :user:`bganglia `. Enhancements ............ - The classifier implemented in imbalanced-learn, :class:`imblearn.ensemble.BalancedBaggingClassifier`, :class:`imblearn.ensemble.BalancedRandomForestClassifier`, :class:`imblearn.ensemble.EasyEnsembleClassifier`, and :class:`imblearn.ensemble.RUSBoostClassifier`, accept `sampling_strategy` with the same key than in `y` without the need of encoding `y` in advance. :pr:`718` by :user:`Guillaume Lemaitre `. - Lazy import `keras` module when importing `imblearn.keras` :pr:`719` by :user:`Guillaume Lemaitre `. Deprecation ........... - Deprecation of the parameters `n_jobs` in :class:`imblearn.under_sampling.ClusterCentroids` since it was used by :class:`sklearn.cluster.KMeans` which deprecated it. :pr:`710` by :user:`Guillaume Lemaitre `. - Deprecation of passing keyword argument by position similarly to `scikit-learn`. :pr:`721` by :user:`Guillaume lemaitre `. scikit-learn-contrib-imbalanced-learn-8504e95/doc/whats_new/v0.8.rst000066400000000000000000000051641521136113700251750ustar00rootroot00000000000000.. _changes_0_8: Version 0.8.1 ============= **September 29, 2020** Changelog --------- Maintenance ........... - Make `imbalanced-learn` compatible with `scikit-learn` 1.0. :pr:`864` by :user:`Guillaume Lemaitre `. Version 0.8.0 ============= **February 18, 2021** Changelog --------- New features ............ - Add the the function :func:`imblearn.metrics.macro_averaged_mean_absolute_error` returning the average across class of the MAE. This metric is used in ordinal classification. :pr:`780` by :user:`Aurélien Massiot `. - Add the class :class:`imblearn.metrics.pairwise.ValueDifferenceMetric` to compute pairwise distances between samples containing only categorical values. :pr:`796` by :user:`Guillaume Lemaitre `. - Add the class :class:`imblearn.over_sampling.SMOTEN` to over-sample data only containing categorical features. :pr:`802` by :user:`Guillaume Lemaitre `. - Add the possibility to pass any type of samplers in :class:`imblearn.ensemble.BalancedBaggingClassifier` unlocking the implementation of methods based on resampled bagging. :pr:`808` by :user:`Guillaume Lemaitre `. Enhancements ............ - Add option `output_dict` in :func:`imblearn.metrics.classification_report_imbalanced` to return a dictionary instead of a string. :pr:`770` by :user:`Guillaume Lemaitre `. - Added an option to generate smoothed bootstrap in :class:`imblearn.over_sampling.RandomOverSampler`. It is controls by the parameter `shrinkage`. This method is also known as Random Over-Sampling Examples (ROSE). :pr:`754` by :user:`Andrea Lorenzon ` and :user:`Guillaume Lemaitre `. Bug fixes ......... - Fix a bug in :class:`imblearn.under_sampling.ClusterCentroids` where `voting="hard"` could have lead to select a sample from any class instead of the targeted class. :pr:`769` by :user:`Guillaume Lemaitre `. - Fix a bug in :class:`imblearn.FunctionSampler` where validation was performed even with `validate=False` when calling `fit`. :pr:`790` by :user:`Guillaume Lemaitre `. Maintenance ........... - Remove requirements files in favour of adding the packages in the `extras_require` within the `setup.py` file. :pr:`816` by :user:`Guillaume Lemaitre `. - Change the website template to use `pydata-sphinx-theme`. :pr:`801` by :user:`Guillaume Lemaitre `. Deprecation ........... - The context manager :func:`imblearn.utils.testing.warns` is deprecated in 0.8 and will be removed 1.0. :pr:`815` by :user:`Guillaume Lemaitre `. scikit-learn-contrib-imbalanced-learn-8504e95/doc/whats_new/v0.9.rst000066400000000000000000000006221521136113700251700ustar00rootroot00000000000000.. _changes_0_9: Version 0.9.1 ============= **May 16, 2022** Changelog --------- This release provides fixes that make `imbalanced-learn` works with the latest release (`1.1.0`) of `scikit-learn`. Version 0.9.0 ============= **January 11, 2022** Changelog --------- This release is mainly providing fixes that make `imbalanced-learn` works with the latest release (`1.0.2`) of `scikit-learn`. scikit-learn-contrib-imbalanced-learn-8504e95/doc/zzz_references.rst000066400000000000000000000001041521136113700255260ustar00rootroot00000000000000========== References ========== .. bibliography:: bibtex/refs.bib scikit-learn-contrib-imbalanced-learn-8504e95/examples/000077500000000000000000000000001521136113700230145ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/README.txt000066400000000000000000000001701521136113700245100ustar00rootroot00000000000000.. _general_examples: Examples -------- General-purpose and introductory examples for the `imbalanced-learn` toolbox. scikit-learn-contrib-imbalanced-learn-8504e95/examples/api/000077500000000000000000000000001521136113700235655ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/api/README.txt000066400000000000000000000002601521136113700252610ustar00rootroot00000000000000.. _api_usage: Examples showing API imbalanced-learn usage ------------------------------------------- Examples that show some details regarding the API of imbalanced-learn. scikit-learn-contrib-imbalanced-learn-8504e95/examples/api/plot_sampling_strategy_usage.py000066400000000000000000000137241521136113700321240ustar00rootroot00000000000000""" ==================================================== How to use ``sampling_strategy`` in imbalanced-learn ==================================================== This example shows the different usage of the parameter ``sampling_strategy`` for the different family of samplers (i.e. over-sampling, under-sampling. or cleaning methods). """ # Authors: Guillaume Lemaitre # License: MIT # %% print(__doc__) import seaborn as sns sns.set_context("poster") # %% [markdown] # Create an imbalanced dataset # ---------------------------- # # First, we will create an imbalanced data set from a the iris data set. # %% from sklearn.datasets import load_iris from imblearn.datasets import make_imbalance iris = load_iris(as_frame=True) sampling_strategy = {0: 10, 1: 20, 2: 47} X, y = make_imbalance(iris.data, iris.target, sampling_strategy=sampling_strategy) # %% import matplotlib.pyplot as plt fig, axs = plt.subplots(ncols=2, figsize=(10, 5)) autopct = "%.2f" iris.target.value_counts().plot.pie(autopct=autopct, ax=axs[0]) axs[0].set_title("Original") y.value_counts().plot.pie(autopct=autopct, ax=axs[1]) axs[1].set_title("Imbalanced") fig.tight_layout() # %% [markdown] # Using ``sampling_strategy`` in resampling algorithms # ==================================================== # # `sampling_strategy` as a `float` # -------------------------------- # # `sampling_strategy` can be given a `float`. For **under-sampling # methods**, it corresponds to the ratio :math:`\alpha_{us}` defined by # :math:`N_{rM} = \alpha_{us} \times N_{m}` where :math:`N_{rM}` and # :math:`N_{m}` are the number of samples in the majority class after # resampling and the number of samples in the minority class, respectively. # %% # select only 2 classes since the ratio make sense in this case binary_mask = y.isin([0, 1]) binary_y = y[binary_mask] binary_X = X[binary_mask] # %% from imblearn.under_sampling import RandomUnderSampler sampling_strategy = 0.8 rus = RandomUnderSampler(sampling_strategy=sampling_strategy) X_res, y_res = rus.fit_resample(binary_X, binary_y) ax = y_res.value_counts().plot.pie(autopct=autopct) _ = ax.set_title("Under-sampling") # %% [markdown] # For **over-sampling methods**, it correspond to the ratio # :math:`\alpha_{os}` defined by :math:`N_{rm} = \alpha_{os} \times N_{M}` # where :math:`N_{rm}` and :math:`N_{M}` are the number of samples in the # minority class after resampling and the number of samples in the majority # class, respectively. # %% from imblearn.over_sampling import RandomOverSampler ros = RandomOverSampler(sampling_strategy=sampling_strategy) X_res, y_res = ros.fit_resample(binary_X, binary_y) ax = y_res.value_counts().plot.pie(autopct=autopct) _ = ax.set_title("Over-sampling") # %% [markdown] # `sampling_strategy` as a `str` # ------------------------------- # # `sampling_strategy` can be given as a string which specify the class # targeted by the resampling. With under- and over-sampling, the number of # samples will be equalized. # # Note that we are using multiple classes from now on. # %% sampling_strategy = "not minority" fig, axs = plt.subplots(ncols=2, figsize=(10, 5)) rus = RandomUnderSampler(sampling_strategy=sampling_strategy) X_res, y_res = rus.fit_resample(X, y) y_res.value_counts().plot.pie(autopct=autopct, ax=axs[0]) axs[0].set_title("Under-sampling") sampling_strategy = "not majority" ros = RandomOverSampler(sampling_strategy=sampling_strategy) X_res, y_res = ros.fit_resample(X, y) y_res.value_counts().plot.pie(autopct=autopct, ax=axs[1]) _ = axs[1].set_title("Over-sampling") # %% [markdown] # With **cleaning method**, the number of samples in each class will not be # equalized even if targeted. # %% from imblearn.under_sampling import TomekLinks sampling_strategy = "not minority" tl = TomekLinks(sampling_strategy=sampling_strategy) X_res, y_res = tl.fit_resample(X, y) ax = y_res.value_counts().plot.pie(autopct=autopct) _ = ax.set_title("Cleaning") # %% [markdown] # `sampling_strategy` as a `dict` # ------------------------------- # # When `sampling_strategy` is a `dict`, the keys correspond to the targeted # classes. The values correspond to the desired number of samples for each # targeted class. This is working for both **under- and over-sampling** # algorithms but not for the **cleaning algorithms**. Use a `list` instead. # %% fig, axs = plt.subplots(ncols=2, figsize=(10, 5)) sampling_strategy = {0: 10, 1: 15, 2: 20} rus = RandomUnderSampler(sampling_strategy=sampling_strategy) X_res, y_res = rus.fit_resample(X, y) y_res.value_counts().plot.pie(autopct=autopct, ax=axs[0]) axs[0].set_title("Under-sampling") sampling_strategy = {0: 25, 1: 35, 2: 47} ros = RandomOverSampler(sampling_strategy=sampling_strategy) X_res, y_res = ros.fit_resample(X, y) y_res.value_counts().plot.pie(autopct=autopct, ax=axs[1]) _ = axs[1].set_title("Under-sampling") # %% [markdown] # `sampling_strategy` as a `list` # ------------------------------- # # When `sampling_strategy` is a `list`, the list contains the targeted # classes. It is used only for **cleaning methods** and raise an error # otherwise. # %% sampling_strategy = [0, 1, 2] tl = TomekLinks(sampling_strategy=sampling_strategy) X_res, y_res = tl.fit_resample(X, y) ax = y_res.value_counts().plot.pie(autopct=autopct) _ = ax.set_title("Cleaning") # %% [markdown] # `sampling_strategy` as a callable # --------------------------------- # # When callable, function taking `y` and returns a `dict`. The keys # correspond to the targeted classes. The values correspond to the desired # number of samples for each class. # %% def ratio_multiplier(y): from collections import Counter multiplier = {1: 0.7, 2: 0.95} target_stats = Counter(y) for key, value in target_stats.items(): if key in multiplier: target_stats[key] = int(value * multiplier[key]) return target_stats X_res, y_res = RandomUnderSampler(sampling_strategy=ratio_multiplier).fit_resample(X, y) ax = y_res.value_counts().plot.pie(autopct=autopct) ax.set_title("Under-sampling") plt.show() scikit-learn-contrib-imbalanced-learn-8504e95/examples/applications/000077500000000000000000000000001521136113700255025ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/applications/README.txt000066400000000000000000000002131521136113700271740ustar00rootroot00000000000000.. _realword_examples: Examples based on real world datasets ------------------------------------- Examples which use real-word dataset. plot_impact_imbalanced_classes.py000066400000000000000000000301301521136113700341610ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/applications""" ========================================================== Fitting model on imbalanced datasets and how to fight bias ========================================================== This example illustrates the problem induced by learning on datasets having imbalanced classes. Subsequently, we compare different approaches alleviating these negative effects. """ # Authors: Guillaume Lemaitre # License: MIT # %% print(__doc__) # %% [markdown] # Problem definition # ------------------ # # We are dropping the following features: # # - "fnlwgt": this feature was created while studying the "adult" dataset. # Thus, we will not use this feature which is not acquired during the survey. # - "education-num": it is encoding the same information than "education". # Thus, we are removing one of these 2 features. # %% from sklearn.datasets import fetch_openml df, y = fetch_openml("adult", version=2, as_frame=True, return_X_y=True) df = df.drop(columns=["fnlwgt", "education-num"]) # %% [markdown] # The "adult" dataset as a class ratio of about 3:1 # %% classes_count = y.value_counts() classes_count # %% [markdown] # This dataset is only slightly imbalanced. To better highlight the effect of # learning from an imbalanced dataset, we will increase its ratio to 30:1 # %% from imblearn.datasets import make_imbalance ratio = 30 df_res, y_res = make_imbalance( df, y, sampling_strategy={classes_count.idxmin(): classes_count.max() // ratio}, ) y_res.value_counts() # %% [markdown] # We will perform a cross-validation evaluation to get an estimate of the test # score. # # As a baseline, we could use a classifier which will always predict the # majority class independently of the features provided. from sklearn.dummy import DummyClassifier # %% from sklearn.model_selection import cross_validate dummy_clf = DummyClassifier(strategy="most_frequent") scoring = ["accuracy", "balanced_accuracy"] cv_result = cross_validate(dummy_clf, df_res, y_res, scoring=scoring) print(f"Accuracy score of a dummy classifier: {cv_result['test_accuracy'].mean():.3f}") # %% [markdown] # Instead of using the accuracy, we can use the balanced accuracy which will # take into account the balancing issue. # %% print( "Balanced accuracy score of a dummy classifier: " f"{cv_result['test_balanced_accuracy'].mean():.3f}" ) # %% [markdown] # Strategies to learn from an imbalanced dataset # ---------------------------------------------- # We will use a dictionary and a list to continuously store the results of # our experiments and show them as a pandas dataframe. # %% index = [] scores = {"Accuracy": [], "Balanced accuracy": []} # %% [markdown] # Dummy baseline # .............. # # Before to train a real machine learning model, we can store the results # obtained with our :class:`~sklearn.dummy.DummyClassifier`. # %% import pandas as pd index += ["Dummy classifier"] cv_result = cross_validate(dummy_clf, df_res, y_res, scoring=scoring) scores["Accuracy"].append(cv_result["test_accuracy"].mean()) scores["Balanced accuracy"].append(cv_result["test_balanced_accuracy"].mean()) df_scores = pd.DataFrame(scores, index=index) df_scores # %% [markdown] # Linear classifier baseline # .......................... # # We will create a machine learning pipeline using a # :class:`~sklearn.linear_model.LogisticRegression` classifier. In this regard, # we will need to one-hot encode the categorical columns and standardized the # numerical columns before to inject the data into the # :class:`~sklearn.linear_model.LogisticRegression` classifier. # # First, we define our numerical and categorical pipelines. # %% from sklearn.impute import SimpleImputer from sklearn.pipeline import make_pipeline from sklearn.preprocessing import OneHotEncoder, StandardScaler num_pipe = make_pipeline( StandardScaler(), SimpleImputer(strategy="mean", add_indicator=True) ) cat_pipe = make_pipeline( SimpleImputer(strategy="constant", fill_value="missing"), OneHotEncoder(handle_unknown="ignore"), ) # %% [markdown] # Then, we can create a preprocessor which will dispatch the categorical # columns to the categorical pipeline and the numerical columns to the # numerical pipeline # %% from sklearn.compose import make_column_selector as selector from sklearn.compose import make_column_transformer preprocessor_linear = make_column_transformer( (num_pipe, selector(dtype_include="number")), (cat_pipe, selector(dtype_include="category")), n_jobs=2, ) # %% [markdown] # Finally, we connect our preprocessor with our # :class:`~sklearn.linear_model.LogisticRegression`. We can then evaluate our # model. # %% from sklearn.linear_model import LogisticRegression lr_clf = make_pipeline(preprocessor_linear, LogisticRegression(max_iter=1000)) # %% index += ["Logistic regression"] cv_result = cross_validate(lr_clf, df_res, y_res, scoring=scoring) scores["Accuracy"].append(cv_result["test_accuracy"].mean()) scores["Balanced accuracy"].append(cv_result["test_balanced_accuracy"].mean()) df_scores = pd.DataFrame(scores, index=index) df_scores # %% [markdown] # We can see that our linear model is learning slightly better than our dummy # baseline. However, it is impacted by the class imbalance. # # We can verify that something similar is happening with a tree-based model # such as :class:`~sklearn.ensemble.RandomForestClassifier`. With this type of # classifier, we will not need to scale the numerical data, and we will only # need to ordinal encode the categorical data. from sklearn.ensemble import RandomForestClassifier # %% from sklearn.preprocessing import OrdinalEncoder num_pipe = SimpleImputer(strategy="mean", add_indicator=True) cat_pipe = make_pipeline( SimpleImputer(strategy="constant", fill_value="missing"), OrdinalEncoder(handle_unknown="use_encoded_value", unknown_value=-1), ) preprocessor_tree = make_column_transformer( (num_pipe, selector(dtype_include="number")), (cat_pipe, selector(dtype_include="category")), n_jobs=2, ) rf_clf = make_pipeline( preprocessor_tree, RandomForestClassifier(random_state=42, n_jobs=2) ) # %% index += ["Random forest"] cv_result = cross_validate(rf_clf, df_res, y_res, scoring=scoring) scores["Accuracy"].append(cv_result["test_accuracy"].mean()) scores["Balanced accuracy"].append(cv_result["test_balanced_accuracy"].mean()) df_scores = pd.DataFrame(scores, index=index) df_scores # %% [markdown] # The :class:`~sklearn.ensemble.RandomForestClassifier` is as well affected by # the class imbalanced, slightly less than the linear model. Now, we will # present different approach to improve the performance of these 2 models. # # Use `class_weight` # .................. # # Most of the models in `scikit-learn` have a parameter `class_weight`. This # parameter will affect the computation of the loss in linear model or the # criterion in the tree-based model to penalize differently a false # classification from the minority and majority class. We can set # `class_weight="balanced"` such that the weight applied is inversely # proportional to the class frequency. We test this parametrization in both # linear model and tree-based model. # %% lr_clf.set_params(logisticregression__class_weight="balanced") index += ["Logistic regression with balanced class weights"] cv_result = cross_validate(lr_clf, df_res, y_res, scoring=scoring) scores["Accuracy"].append(cv_result["test_accuracy"].mean()) scores["Balanced accuracy"].append(cv_result["test_balanced_accuracy"].mean()) df_scores = pd.DataFrame(scores, index=index) df_scores # %% rf_clf.set_params(randomforestclassifier__class_weight="balanced") index += ["Random forest with balanced class weights"] cv_result = cross_validate(rf_clf, df_res, y_res, scoring=scoring) scores["Accuracy"].append(cv_result["test_accuracy"].mean()) scores["Balanced accuracy"].append(cv_result["test_balanced_accuracy"].mean()) df_scores = pd.DataFrame(scores, index=index) df_scores # %% [markdown] # We can see that using `class_weight` was really effective for the linear # model, alleviating the issue of learning from imbalanced classes. However, # the :class:`~sklearn.ensemble.RandomForestClassifier` is still biased toward # the majority class, mainly due to the criterion which is not suited enough to # fight the class imbalance. # # Resample the training set during learning # ......................................... # # Another way is to resample the training set by under-sampling or # over-sampling some of the samples. `imbalanced-learn` provides some samplers # to do such processing. # %% from imblearn.pipeline import make_pipeline as make_pipeline_with_sampler from imblearn.under_sampling import RandomUnderSampler lr_clf = make_pipeline_with_sampler( preprocessor_linear, RandomUnderSampler(random_state=42), LogisticRegression(max_iter=1000), ) # %% index += ["Under-sampling + Logistic regression"] cv_result = cross_validate(lr_clf, df_res, y_res, scoring=scoring) scores["Accuracy"].append(cv_result["test_accuracy"].mean()) scores["Balanced accuracy"].append(cv_result["test_balanced_accuracy"].mean()) df_scores = pd.DataFrame(scores, index=index) df_scores # %% rf_clf = make_pipeline_with_sampler( preprocessor_tree, RandomUnderSampler(random_state=42), RandomForestClassifier(random_state=42, n_jobs=2), ) # %% index += ["Under-sampling + Random forest"] cv_result = cross_validate(rf_clf, df_res, y_res, scoring=scoring) scores["Accuracy"].append(cv_result["test_accuracy"].mean()) scores["Balanced accuracy"].append(cv_result["test_balanced_accuracy"].mean()) df_scores = pd.DataFrame(scores, index=index) df_scores # %% [markdown] # Applying a random under-sampler before the training of the linear model or # random forest, allows to not focus on the majority class at the cost of # making more mistake for samples in the majority class (i.e. decreased # accuracy). # # We could apply any type of samplers and find which sampler is working best # on the current dataset. # # Instead, we will present another way by using classifiers which will apply # sampling internally. # # Use of specific balanced algorithms from imbalanced-learn # ......................................................... # # We already showed that random under-sampling can be effective on decision # tree. However, instead of under-sampling once the dataset, one could # under-sample the original dataset before to take a bootstrap sample. This is # the base of the :class:`imblearn.ensemble.BalancedRandomForestClassifier` and # :class:`~imblearn.ensemble.BalancedBaggingClassifier`. # %% from imblearn.ensemble import BalancedRandomForestClassifier rf_clf = make_pipeline( preprocessor_tree, BalancedRandomForestClassifier( sampling_strategy="all", replacement=True, bootstrap=False, random_state=42, n_jobs=2, ), ) # %% index += ["Balanced random forest"] cv_result = cross_validate(rf_clf, df_res, y_res, scoring=scoring) scores["Accuracy"].append(cv_result["test_accuracy"].mean()) scores["Balanced accuracy"].append(cv_result["test_balanced_accuracy"].mean()) df_scores = pd.DataFrame(scores, index=index) df_scores # %% [markdown] # The performance with the # :class:`~imblearn.ensemble.BalancedRandomForestClassifier` is better than # applying a single random under-sampling. We will use a gradient-boosting # classifier within a :class:`~imblearn.ensemble.BalancedBaggingClassifier`. from sklearn.ensemble import HistGradientBoostingClassifier from imblearn.ensemble import BalancedBaggingClassifier bag_clf = make_pipeline( preprocessor_tree, BalancedBaggingClassifier( estimator=HistGradientBoostingClassifier(random_state=42), n_estimators=10, random_state=42, n_jobs=2, ), ) index += ["Balanced bag of histogram gradient boosting"] cv_result = cross_validate(bag_clf, df_res, y_res, scoring=scoring) scores["Accuracy"].append(cv_result["test_accuracy"].mean()) scores["Balanced accuracy"].append(cv_result["test_balanced_accuracy"].mean()) df_scores = pd.DataFrame(scores, index=index) df_scores # %% [markdown] # This last approach is the most effective. The different under-sampling allows # to bring some diversity for the different GBDT to learn and not focus on a # portion of the majority class. plot_multi_class_under_sampling.py000066400000000000000000000027261521136113700344500ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/applications""" ============================================= Multiclass classification with under-sampling ============================================= Some balancing methods allow for balancing dataset with multiples classes. We provide an example to illustrate the use of those methods which do not differ from the binary case. """ # Authors: Guillaume Lemaitre # License: MIT from collections import Counter from sklearn.datasets import load_iris from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from imblearn.datasets import make_imbalance from imblearn.metrics import classification_report_imbalanced from imblearn.pipeline import make_pipeline from imblearn.under_sampling import NearMiss print(__doc__) RANDOM_STATE = 42 # Create a folder to fetch the dataset iris = load_iris() X, y = make_imbalance( iris.data, iris.target, sampling_strategy={0: 25, 1: 50, 2: 50}, random_state=RANDOM_STATE, ) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=RANDOM_STATE) print(f"Training target statistics: {Counter(y_train)}") print(f"Testing target statistics: {Counter(y_test)}") # Create a pipeline pipeline = make_pipeline(NearMiss(version=2), StandardScaler(), LogisticRegression()) pipeline.fit(X_train, y_train) # Classify and report the results print(classification_report_imbalanced(y_test, pipeline.predict(X_test))) scikit-learn-contrib-imbalanced-learn-8504e95/examples/applications/plot_outlier_rejections.py000066400000000000000000000102711521136113700330230ustar00rootroot00000000000000""" =============================================================== Customized sampler to implement an outlier rejections estimator =============================================================== This example illustrates the use of a custom sampler to implement an outlier rejections estimator. It can be used easily within a pipeline in which the number of samples can vary during training, which usually is a limitation of the current scikit-learn pipeline. """ # Authors: Guillaume Lemaitre # License: MIT import matplotlib.pyplot as plt import numpy as np from sklearn.datasets import make_blobs, make_moons from sklearn.ensemble import IsolationForest from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report from imblearn import FunctionSampler from imblearn.pipeline import make_pipeline print(__doc__) rng = np.random.RandomState(42) def plot_scatter(X, y, title): """Function to plot some data as a scatter plot.""" plt.figure() plt.scatter(X[y == 1, 0], X[y == 1, 1], label="Class #1") plt.scatter(X[y == 0, 0], X[y == 0, 1], label="Class #0") plt.legend() plt.title(title) ############################################################################## # Toy data generation ############################################################################## ############################################################################## # We are generating some non Gaussian data set contaminated with some unform # noise. moons, _ = make_moons(n_samples=500, noise=0.05) blobs, _ = make_blobs( n_samples=500, centers=[(-0.75, 2.25), (1.0, 2.0)], cluster_std=0.25 ) outliers = rng.uniform(low=-3, high=3, size=(500, 2)) X_train = np.vstack([moons, blobs, outliers]) y_train = np.hstack( [ np.ones(moons.shape[0], dtype=np.int8), np.zeros(blobs.shape[0], dtype=np.int8), rng.randint(0, 2, size=outliers.shape[0], dtype=np.int8), ] ) plot_scatter(X_train, y_train, "Training dataset") ############################################################################## # We will generate some cleaned test data without outliers. moons, _ = make_moons(n_samples=50, noise=0.05) blobs, _ = make_blobs( n_samples=50, centers=[(-0.75, 2.25), (1.0, 2.0)], cluster_std=0.25 ) X_test = np.vstack([moons, blobs]) y_test = np.hstack( [np.ones(moons.shape[0], dtype=np.int8), np.zeros(blobs.shape[0], dtype=np.int8)] ) plot_scatter(X_test, y_test, "Testing dataset") ############################################################################## # How to use the :class:`~imblearn.FunctionSampler` ############################################################################## ############################################################################## # We first define a function which will use # :class:`~sklearn.ensemble.IsolationForest` to eliminate some outliers from # our dataset during training. The function passed to the # :class:`~imblearn.FunctionSampler` will be called when using the method # ``fit_resample``. def outlier_rejection(X, y): """This will be our function used to resample our dataset.""" model = IsolationForest(max_samples=100, contamination=0.4, random_state=rng) model.fit(X) y_pred = model.predict(X) return X[y_pred == 1], y[y_pred == 1] reject_sampler = FunctionSampler(func=outlier_rejection) X_inliers, y_inliers = reject_sampler.fit_resample(X_train, y_train) plot_scatter(X_inliers, y_inliers, "Training data without outliers") ############################################################################## # Integrate it within a pipeline ############################################################################## ############################################################################## # By elimnating outliers before the training, the classifier will be less # affected during the prediction. pipe = make_pipeline( FunctionSampler(func=outlier_rejection), LogisticRegression(random_state=rng), ) y_pred = pipe.fit(X_train, y_train).predict(X_test) print(classification_report(y_test, y_pred)) clf = LogisticRegression(random_state=rng) y_pred = clf.fit(X_train, y_train).predict(X_test) print(classification_report(y_test, y_pred)) plt.show() plot_over_sampling_benchmark_lfw.py000066400000000000000000000112261521136113700345640ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/applications""" ========================================================== Benchmark over-sampling methods in a face recognition task ========================================================== In this face recognition example two faces are used from the LFW (Faces in the Wild) dataset. Several implemented over-sampling methods are used in conjunction with a 3NN classifier in order to examine the improvement of the classifier's output quality by using an over-sampler. """ # Authors: Christos Aridas # Guillaume Lemaitre # License: MIT # %% print(__doc__) import seaborn as sns sns.set_context("poster") # %% [markdown] # Load the dataset # ---------------- # # We will use a dataset containing image from know person where we will # build a model to recognize the person on the image. We will make this problem # a binary problem by taking picture of only George W. Bush and Bill Clinton. # %% import numpy as np from sklearn.datasets import fetch_lfw_people data = fetch_lfw_people() george_bush_id = 1871 # Photos of George W. Bush bill_clinton_id = 531 # Photos of Bill Clinton classes = [george_bush_id, bill_clinton_id] classes_name = np.array(["B. Clinton", "G.W. Bush"], dtype=object) # %% mask_photos = np.isin(data.target, classes) X, y = data.data[mask_photos], data.target[mask_photos] y = (y == george_bush_id).astype(np.int8) y = classes_name[y] # %% [markdown] # We can check the ratio between the two classes. # %% import matplotlib.pyplot as plt import pandas as pd class_distribution = pd.Series(y).value_counts(normalize=True) ax = class_distribution.plot.barh() ax.set_title("Class distribution") pos_label = class_distribution.idxmin() plt.tight_layout() print(f"The positive label considered as the minority class is {pos_label}") # %% [markdown] # We see that we have an imbalanced classification problem with ~95% of the # data belonging to the class G.W. Bush. # # Compare over-sampling approaches # -------------------------------- # # We will use different over-sampling approaches and use a kNN classifier # to check if we can recognize the 2 presidents. The evaluation will be # performed through cross-validation and we will plot the mean ROC curve. # # We will create different pipelines and evaluate them. from sklearn.neighbors import KNeighborsClassifier from imblearn import FunctionSampler from imblearn.over_sampling import ADASYN, SMOTE, RandomOverSampler from imblearn.pipeline import make_pipeline classifier = KNeighborsClassifier(n_neighbors=3) pipeline = [ make_pipeline(FunctionSampler(), classifier), make_pipeline(RandomOverSampler(random_state=42), classifier), make_pipeline(ADASYN(random_state=42), classifier), make_pipeline(SMOTE(random_state=42), classifier), ] # %% from sklearn.model_selection import StratifiedKFold cv = StratifiedKFold(n_splits=3) # %% [markdown] # We will compute the mean ROC curve for each pipeline using a different splits # provided by the :class:`~sklearn.model_selection.StratifiedKFold` # cross-validation. # %% from sklearn.metrics import RocCurveDisplay, auc, roc_curve disp = [] for model in pipeline: # compute the mean fpr/tpr to get the mean ROC curve mean_tpr, mean_fpr = 0.0, np.linspace(0, 1, 100) for train, test in cv.split(X, y): model.fit(X[train], y[train]) y_proba = model.predict_proba(X[test]) pos_label_idx = np.flatnonzero(model.classes_ == pos_label)[0] fpr, tpr, thresholds = roc_curve( y[test], y_proba[:, pos_label_idx], pos_label=pos_label ) mean_tpr += np.interp(mean_fpr, fpr, tpr) mean_tpr[0] = 0.0 mean_tpr /= cv.get_n_splits(X, y) mean_tpr[-1] = 1.0 mean_auc = auc(mean_fpr, mean_tpr) # Create a display that we will reuse to make the aggregated plots for # all methods disp.append( RocCurveDisplay( fpr=mean_fpr, tpr=mean_tpr, roc_auc=mean_auc, name=f"{model[0].__class__.__name__}", ) ) # %% [markdown] # In the previous cell, we created the different mean ROC curve and we can plot # them on the same plot. # %% fig, ax = plt.subplots(figsize=(9, 9)) for d in disp: d.plot(ax=ax, curve_kwargs={"linestyle": "--"}) ax.plot([0, 1], [0, 1], linestyle="--", color="k") ax.axis("square") fig.suptitle("Comparison of over-sampling methods \nwith a 3NN classifier") ax.set_xlim([0, 1]) ax.set_ylim([0, 1]) sns.despine(offset=10, ax=ax) plt.legend(loc="lower right", fontsize=16) plt.tight_layout() plt.show() # %% [markdown] # We see that for this task, methods that are generating new samples with some # interpolation (i.e. ADASYN and SMOTE) perform better than random # over-sampling or no resampling. scikit-learn-contrib-imbalanced-learn-8504e95/examples/applications/plot_topic_classication.py000066400000000000000000000064361521136113700327750ustar00rootroot00000000000000""" ================================================= Example of topic classification in text documents ================================================= This example shows how to balance the text data before to train a classifier. Note that for this example, the data are slightly imbalanced but it can happen that for some data sets, the imbalanced ratio is more significant. """ # Authors: Guillaume Lemaitre # License: MIT # %% print(__doc__) # %% [markdown] # Setting the data set # -------------------- # # We use a part of the 20 newsgroups data set by loading 4 topics. Using the # scikit-learn loader, the data are split into a training and a testing set. # # Note the class \#3 is the minority class and has almost twice less samples # than the majority class. # %% from sklearn.datasets import fetch_20newsgroups categories = [ "alt.atheism", "talk.religion.misc", "comp.graphics", "sci.space", ] newsgroups_train = fetch_20newsgroups(subset="train", categories=categories) newsgroups_test = fetch_20newsgroups(subset="test", categories=categories) X_train = newsgroups_train.data X_test = newsgroups_test.data y_train = newsgroups_train.target y_test = newsgroups_test.target # %% from collections import Counter print(f"Training class distributions summary: {Counter(y_train)}") print(f"Test class distributions summary: {Counter(y_test)}") # %% [markdown] # The usual scikit-learn pipeline # ------------------------------- # # You might usually use scikit-learn pipeline by combining the TF-IDF # vectorizer to feed a multinomial naive bayes classifier. A classification # report summarized the results on the testing set. # # As expected, the recall of the class \#3 is low mainly due to the class # imbalanced. # %% from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.naive_bayes import MultinomialNB from sklearn.pipeline import make_pipeline model = make_pipeline(TfidfVectorizer(), MultinomialNB()) model.fit(X_train, y_train) y_pred = model.predict(X_test) # %% from imblearn.metrics import classification_report_imbalanced print(classification_report_imbalanced(y_test, y_pred)) # %% [markdown] # Balancing the class before classification # ----------------------------------------- # # To improve the prediction of the class \#3, it could be interesting to apply # a balancing before to train the naive bayes classifier. Therefore, we will # use a :class:`~imblearn.under_sampling.RandomUnderSampler` to equalize the # number of samples in all the classes before the training. # # It is also important to note that we are using the # :class:`~imblearn.pipeline.make_pipeline` function implemented in # imbalanced-learn to properly handle the samplers. from imblearn.pipeline import make_pipeline as make_pipeline_imb # %% from imblearn.under_sampling import RandomUnderSampler model = make_pipeline_imb(TfidfVectorizer(), RandomUnderSampler(), MultinomialNB()) model.fit(X_train, y_train) y_pred = model.predict(X_test) # %% [markdown] # Although the results are almost identical, it can be seen that the resampling # allowed to correct the poor recall of the class \#3 at the cost of reducing # the other metrics for the other classes. However, the overall results are # slightly better. # %% print(classification_report_imbalanced(y_test, y_pred)) porto_seguro_keras_under_sampling.py000066400000000000000000000210611521136113700350000ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/applications""" ========================================================== Porto Seguro: balancing samples in mini-batches with Keras ========================================================== This example compares two strategies to train a neural-network on the Porto Seguro Kaggle data set [1]_. The data set is imbalanced and we show that balancing each mini-batch allows to improve performance and reduce the training time. References ---------- .. [1] https://www.kaggle.com/c/porto-seguro-safe-driver-prediction/data """ # Authors: Guillaume Lemaitre # License: MIT print(__doc__) ############################################################################### # Data loading ############################################################################### from collections import Counter import numpy as np import pandas as pd ############################################################################### # First, you should download the Porto Seguro data set from Kaggle. See the # link in the introduction. training_data = pd.read_csv("./input/train.csv") testing_data = pd.read_csv("./input/test.csv") y_train = training_data[["id", "target"]].set_index("id") X_train = training_data.drop(["target"], axis=1).set_index("id") X_test = testing_data.set_index("id") ############################################################################### # The data set is imbalanced and it will have an effect on the fitting. print(f"The data set is imbalanced: {Counter(y_train['target'])}") ############################################################################### # Define the pre-processing pipeline ############################################################################### from sklearn.compose import ColumnTransformer from sklearn.impute import SimpleImputer from sklearn.pipeline import make_pipeline from sklearn.preprocessing import FunctionTransformer, OneHotEncoder, StandardScaler def convert_float64(X): return X.astype(np.float64) ############################################################################### # We want to standard scale the numerical features while we want to one-hot # encode the categorical features. In this regard, we make use of the # :class:`~sklearn.compose.ColumnTransformer`. numerical_columns = [ name for name in X_train.columns if "_calc_" in name and "_bin" not in name ] numerical_pipeline = make_pipeline( FunctionTransformer(func=convert_float64, validate=False), StandardScaler() ) categorical_columns = [name for name in X_train.columns if "_cat" in name] categorical_pipeline = make_pipeline( SimpleImputer(missing_values=-1, strategy="most_frequent"), OneHotEncoder(categories="auto"), ) preprocessor = ColumnTransformer( [ ("numerical_preprocessing", numerical_pipeline, numerical_columns), ( "categorical_preprocessing", categorical_pipeline, categorical_columns, ), ], remainder="drop", ) # Create an environment variable to avoid using the GPU. This can be changed. import os os.environ["CUDA_VISIBLE_DEVICES"] = "-1" from tensorflow.keras.layers import Activation, BatchNormalization, Dense, Dropout ############################################################################### # Create a neural-network ############################################################################### from tensorflow.keras.models import Sequential def make_model(n_features): model = Sequential() model.add(Dense(200, input_shape=(n_features,), kernel_initializer="glorot_normal")) model.add(BatchNormalization()) model.add(Activation("relu")) model.add(Dropout(0.5)) model.add(Dense(100, kernel_initializer="glorot_normal", use_bias=False)) model.add(BatchNormalization()) model.add(Activation("relu")) model.add(Dropout(0.25)) model.add(Dense(50, kernel_initializer="glorot_normal", use_bias=False)) model.add(BatchNormalization()) model.add(Activation("relu")) model.add(Dropout(0.15)) model.add(Dense(25, kernel_initializer="glorot_normal", use_bias=False)) model.add(BatchNormalization()) model.add(Activation("relu")) model.add(Dropout(0.1)) model.add(Dense(1, activation="sigmoid")) model.compile(loss="binary_crossentropy", optimizer="adam", metrics=["accuracy"]) return model ############################################################################### # We create a decorator to report the computation time import time from functools import wraps def timeit(f): @wraps(f) def wrapper(*args, **kwds): start_time = time.time() result = f(*args, **kwds) elapsed_time = time.time() - start_time print(f"Elapsed computation time: {elapsed_time:.3f} secs") return (elapsed_time, result) return wrapper ############################################################################### # The first model will be trained using the ``fit`` method and with imbalanced # mini-batches. import tensorflow from sklearn.metrics import roc_auc_score from sklearn.utils.fixes import parse_version tf_version = parse_version(tensorflow.__version__) @timeit def fit_predict_imbalanced_model(X_train, y_train, X_test, y_test): model = make_model(X_train.shape[1]) model.fit(X_train, y_train, epochs=2, verbose=1, batch_size=1000) if tf_version < parse_version("2.6"): # predict_proba was removed in tensorflow 2.6 predict_method = "predict_proba" else: predict_method = "predict" y_pred = getattr(model, predict_method)(X_test, batch_size=1000) return roc_auc_score(y_test, y_pred) ############################################################################### # In the contrary, we will use imbalanced-learn to create a generator of # mini-batches which will yield balanced mini-batches. from imblearn.keras import BalancedBatchGenerator @timeit def fit_predict_balanced_model(X_train, y_train, X_test, y_test): model = make_model(X_train.shape[1]) training_generator = BalancedBatchGenerator( X_train, y_train, batch_size=1000, random_state=42 ) model.fit(training_generator, epochs=5, verbose=1) y_pred = model.predict(X_test, batch_size=1000) return roc_auc_score(y_test, y_pred) ############################################################################### # Classification loop ############################################################################### ############################################################################### # We will perform a 10-fold cross-validation and train the neural-network with # the two different strategies previously presented. from sklearn.model_selection import StratifiedKFold skf = StratifiedKFold(n_splits=10) cv_results_imbalanced = [] cv_time_imbalanced = [] cv_results_balanced = [] cv_time_balanced = [] for train_idx, valid_idx in skf.split(X_train, y_train): X_local_train = preprocessor.fit_transform(X_train.iloc[train_idx]) y_local_train = y_train.iloc[train_idx].values.ravel() X_local_test = preprocessor.transform(X_train.iloc[valid_idx]) y_local_test = y_train.iloc[valid_idx].values.ravel() elapsed_time, roc_auc = fit_predict_imbalanced_model( X_local_train, y_local_train, X_local_test, y_local_test ) cv_time_imbalanced.append(elapsed_time) cv_results_imbalanced.append(roc_auc) elapsed_time, roc_auc = fit_predict_balanced_model( X_local_train, y_local_train, X_local_test, y_local_test ) cv_time_balanced.append(elapsed_time) cv_results_balanced.append(roc_auc) ############################################################################### # Plot of the results and computation time ############################################################################### df_results = pd.DataFrame( { "Balanced model": cv_results_balanced, "Imbalanced model": cv_results_imbalanced, } ) df_results = df_results.unstack().reset_index() df_time = pd.DataFrame( {"Balanced model": cv_time_balanced, "Imbalanced model": cv_time_imbalanced} ) df_time = df_time.unstack().reset_index() import matplotlib.pyplot as plt import seaborn as sns plt.figure() sns.boxplot(y="level_0", x=0, data=df_time) sns.despine(top=True, right=True, left=True) plt.xlabel("time [s]") plt.ylabel("") plt.title("Computation time difference using a random under-sampling") plt.figure() sns.boxplot(y="level_0", x=0, data=df_results, whis=10.0) sns.despine(top=True, right=True, left=True) ax = plt.gca() ax.xaxis.set_major_formatter(plt.FuncFormatter(lambda x, pos: "%i%%" % (100 * x))) plt.xlabel("ROC-AUC") plt.ylabel("") plt.title("Difference in terms of ROC-AUC using a random under-sampling") scikit-learn-contrib-imbalanced-learn-8504e95/examples/combine/000077500000000000000000000000001521136113700244305ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/combine/README.txt000066400000000000000000000004261521136113700261300ustar00rootroot00000000000000.. _combine_examples: Examples using combine class methods ==================================== Combine methods mixed over- and under-sampling methods. Generally SMOTE is used for over-sampling while some cleaning methods (i.e., ENN and Tomek links) are used to under-sample. scikit-learn-contrib-imbalanced-learn-8504e95/examples/combine/plot_comparison_combine.py000066400000000000000000000073541521136113700317170ustar00rootroot00000000000000""" ================================================== Compare sampler combining over- and under-sampling ================================================== This example shows the effect of applying an under-sampling algorithms after SMOTE over-sampling. In the literature, Tomek's link and edited nearest neighbours are the two methods which have been used and are available in imbalanced-learn. """ # Authors: Guillaume Lemaitre # License: MIT # %% print(__doc__) import matplotlib.pyplot as plt import seaborn as sns sns.set_context("poster") # %% [markdown] # Dataset generation # ------------------ # # We will create an imbalanced dataset with a couple of samples. We will use # :func:`~sklearn.datasets.make_classification` to generate this dataset. # %% from sklearn.datasets import make_classification X, y = make_classification( n_samples=100, n_features=2, n_informative=2, n_redundant=0, n_repeated=0, n_classes=3, n_clusters_per_class=1, weights=[0.1, 0.2, 0.7], class_sep=0.8, random_state=0, ) # %% _, ax = plt.subplots(figsize=(6, 6)) _ = ax.scatter(X[:, 0], X[:, 1], c=y, alpha=0.8, edgecolor="k") # %% [markdown] # The following function will be used to plot the sample space after resampling # to illustrate the characteristic of an algorithm. # %% from collections import Counter def plot_resampling(X, y, sampler, ax): """Plot the resampled dataset using the sampler.""" X_res, y_res = sampler.fit_resample(X, y) ax.scatter(X_res[:, 0], X_res[:, 1], c=y_res, alpha=0.8, edgecolor="k") sns.despine(ax=ax, offset=10) ax.set_title(f"Decision function for {sampler.__class__.__name__}") return Counter(y_res) # %% [markdown] # The following function will be used to plot the decision function of a # classifier given some data. # %% import numpy as np def plot_decision_function(X, y, clf, ax): """Plot the decision function of the classifier and the original data""" plot_step = 0.02 x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1 y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1 xx, yy = np.meshgrid( np.arange(x_min, x_max, plot_step), np.arange(y_min, y_max, plot_step) ) Z = clf.predict(np.c_[xx.ravel(), yy.ravel()]) Z = Z.reshape(xx.shape) ax.contourf(xx, yy, Z, alpha=0.4) ax.scatter(X[:, 0], X[:, 1], alpha=0.8, c=y, edgecolor="k") ax.set_title(f"Resampling using {clf[0].__class__.__name__}") # %% [markdown] # :class:`~imblearn.over_sampling.SMOTE` allows to generate samples. However, # this method of over-sampling does not have any knowledge regarding the # underlying distribution. Therefore, some noisy samples can be generated, e.g. # when the different classes cannot be well separated. Hence, it can be # beneficial to apply an under-sampling algorithm to clean the noisy samples. # Two methods are usually used in the literature: (i) Tomek's link and (ii) # edited nearest neighbours cleaning methods. Imbalanced-learn provides two # ready-to-use samplers :class:`~imblearn.combine.SMOTETomek` and # :class:`~imblearn.combine.SMOTEENN`. In general, # :class:`~imblearn.combine.SMOTEENN` cleans more noisy data than # :class:`~imblearn.combine.SMOTETomek`. from sklearn.linear_model import LogisticRegression from imblearn.combine import SMOTEENN, SMOTETomek # %% from imblearn.over_sampling import SMOTE from imblearn.pipeline import make_pipeline samplers = [SMOTE(random_state=0), SMOTEENN(random_state=0), SMOTETomek(random_state=0)] fig, axs = plt.subplots(3, 2, figsize=(15, 25)) for ax, sampler in zip(axs, samplers): clf = make_pipeline(sampler, LogisticRegression()).fit(X, y) plot_decision_function(X, y, clf, ax[0]) plot_resampling(X, y, sampler, ax[1]) fig.tight_layout() plt.show() scikit-learn-contrib-imbalanced-learn-8504e95/examples/datasets/000077500000000000000000000000001521136113700246245ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/datasets/README.txt000066400000000000000000000001721521136113700263220ustar00rootroot00000000000000.. _dataset_examples: Dataset examples ----------------------- Examples concerning the :mod:`imblearn.datasets` module. scikit-learn-contrib-imbalanced-learn-8504e95/examples/datasets/plot_make_imbalance.py000066400000000000000000000046521521136113700311530ustar00rootroot00000000000000""" ============================ Create an imbalanced dataset ============================ An illustration of the :func:`~imblearn.datasets.make_imbalance` function to create an imbalanced dataset from a balanced dataset. We show the ability of :func:`~imblearn.datasets.make_imbalance` of dealing with Pandas DataFrame. """ # Authors: Dayvid Oliveira # Christos Aridas # Guillaume Lemaitre # License: MIT # %% print(__doc__) import seaborn as sns sns.set_context("poster") # %% [markdown] # Generate the dataset # -------------------- # # First, we will generate a dataset and convert it to a # :class:`~pandas.DataFrame` with arbitrary column names. We will plot the # original dataset. # %% import matplotlib.pyplot as plt import pandas as pd from sklearn.datasets import make_moons X, y = make_moons(n_samples=200, shuffle=True, noise=0.5, random_state=10) X = pd.DataFrame(X, columns=["feature 1", "feature 2"]) ax = X.plot.scatter( x="feature 1", y="feature 2", c=y, colormap="viridis", colorbar=False, ) sns.despine(ax=ax, offset=10) plt.tight_layout() # %% [markdown] # Make a dataset imbalanced # ------------------------- # # Now, we will show the helpers :func:`~imblearn.datasets.make_imbalance` # that is useful to random select a subset of samples. It will impact the # class distribution as specified by the parameters. # %% from collections import Counter def ratio_func(y, multiplier, minority_class): target_stats = Counter(y) return {minority_class: int(multiplier * target_stats[minority_class])} # %% from imblearn.datasets import make_imbalance fig, axs = plt.subplots(nrows=2, ncols=3, figsize=(15, 10)) X.plot.scatter( x="feature 1", y="feature 2", c=y, ax=axs[0, 0], colormap="viridis", colorbar=False, ) axs[0, 0].set_title("Original set") sns.despine(ax=axs[0, 0], offset=10) multipliers = [0.9, 0.75, 0.5, 0.25, 0.1] for ax, multiplier in zip(axs.ravel()[1:], multipliers): X_resampled, y_resampled = make_imbalance( X, y, sampling_strategy=ratio_func, **{"multiplier": multiplier, "minority_class": 1}, ) X_resampled.plot.scatter( x="feature 1", y="feature 2", c=y_resampled, ax=ax, colormap="viridis", colorbar=False, ) ax.set_title(f"Sampling ratio = {multiplier}") sns.despine(ax=ax, offset=10) plt.tight_layout() plt.show() scikit-learn-contrib-imbalanced-learn-8504e95/examples/ensemble/000077500000000000000000000000001521136113700246065ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/ensemble/README.txt000066400000000000000000000005511521136113700263050ustar00rootroot00000000000000.. _ensemble_examples: Example using ensemble class methods ==================================== Under-sampling methods implies that samples of the majority class are lost during the balancing procedure. Ensemble methods offer an alternative to use most of the samples. In fact, an ensemble of balanced sets is created and used to later train any classifier. scikit-learn-contrib-imbalanced-learn-8504e95/examples/ensemble/plot_bagging_classifier.py000066400000000000000000000136061521136113700320260ustar00rootroot00000000000000""" ================================= Bagging classifiers using sampler ================================= In this example, we show how :class:`~imblearn.ensemble.BalancedBaggingClassifier` can be used to create a large variety of classifiers by giving different samplers. We will give several examples that have been published in the passed year. """ # Authors: Guillaume Lemaitre # License: MIT # %% print(__doc__) # %% [markdown] # Generate an imbalanced dataset # ------------------------------ # # For this example, we will create a synthetic dataset using the function # :func:`~sklearn.datasets.make_classification`. The problem will be a toy # classification problem with a ratio of 1:9 between the two classes. # %% from sklearn.datasets import make_classification X, y = make_classification( n_samples=10_000, n_features=10, weights=[0.1, 0.9], class_sep=0.5, random_state=0, ) # %% import pandas as pd pd.Series(y).value_counts(normalize=True) # %% [markdown] # In the following sections, we will show a couple of algorithms that have # been proposed over the years. We intend to illustrate how one can reuse the # :class:`~imblearn.ensemble.BalancedBaggingClassifier` by passing different # sampler. from sklearn.ensemble import BaggingClassifier # %% from sklearn.model_selection import cross_validate ebb = BaggingClassifier() cv_results = cross_validate(ebb, X, y, scoring="balanced_accuracy") print(f"{cv_results['test_score'].mean():.3f} +/- {cv_results['test_score'].std():.3f}") # %% [markdown] # Exactly Balanced Bagging and Over-Bagging # ----------------------------------------- # # The :class:`~imblearn.ensemble.BalancedBaggingClassifier` can use in # conjunction with a :class:`~imblearn.under_sampling.RandomUnderSampler` or # :class:`~imblearn.over_sampling.RandomOverSampler`. These methods are # referred as Exactly Balanced Bagging and Over-Bagging, respectively and have # been proposed first in [1]_. # %% from imblearn.ensemble import BalancedBaggingClassifier from imblearn.under_sampling import RandomUnderSampler # Exactly Balanced Bagging ebb = BalancedBaggingClassifier(sampler=RandomUnderSampler()) cv_results = cross_validate(ebb, X, y, scoring="balanced_accuracy") print(f"{cv_results['test_score'].mean():.3f} +/- {cv_results['test_score'].std():.3f}") # %% from imblearn.over_sampling import RandomOverSampler # Over-bagging over_bagging = BalancedBaggingClassifier(sampler=RandomOverSampler()) cv_results = cross_validate(over_bagging, X, y, scoring="balanced_accuracy") print(f"{cv_results['test_score'].mean():.3f} +/- {cv_results['test_score'].std():.3f}") # %% [markdown] # SMOTE-Bagging # ------------- # # Instead of using a :class:`~imblearn.over_sampling.RandomOverSampler` that # make a bootstrap, an alternative is to use # :class:`~imblearn.over_sampling.SMOTE` as an over-sampler. This is known as # SMOTE-Bagging [2]_. # %% from imblearn.over_sampling import SMOTE # SMOTE-Bagging smote_bagging = BalancedBaggingClassifier(sampler=SMOTE()) cv_results = cross_validate(smote_bagging, X, y, scoring="balanced_accuracy") print(f"{cv_results['test_score'].mean():.3f} +/- {cv_results['test_score'].std():.3f}") # %% [markdown] # Roughly Balanced Bagging # ------------------------ # While using a :class:`~imblearn.under_sampling.RandomUnderSampler` or # :class:`~imblearn.over_sampling.RandomOverSampler` will create exactly the # desired number of samples, it does not follow the statistical spirit wanted # in the bagging framework. The authors in [3]_ proposes to use a negative # binomial distribution to compute the number of samples of the majority # class to be selected and then perform a random under-sampling. # # Here, we illustrate this method by implementing a function in charge of # resampling and use the :class:`~imblearn.FunctionSampler` to integrate it # within a :class:`~imblearn.pipeline.Pipeline` and # :class:`~sklearn.model_selection.cross_validate`. # %% from collections import Counter import numpy as np from imblearn import FunctionSampler def roughly_balanced_bagging(X, y, replace=False): """Implementation of Roughly Balanced Bagging for binary problem.""" # find the minority and majority classes class_counts = Counter(y) majority_class = max(class_counts, key=class_counts.get) minority_class = min(class_counts, key=class_counts.get) # compute the number of sample to draw from the majority class using # a negative binomial distribution n_minority_class = class_counts[minority_class] n_majority_resampled = np.random.negative_binomial(n=n_minority_class, p=0.5) # draw randomly with or without replacement majority_indices = np.random.choice( np.flatnonzero(y == majority_class), size=n_majority_resampled, replace=replace, ) minority_indices = np.random.choice( np.flatnonzero(y == minority_class), size=n_minority_class, replace=replace, ) indices = np.hstack([majority_indices, minority_indices]) return X[indices], y[indices] # Roughly Balanced Bagging rbb = BalancedBaggingClassifier( sampler=FunctionSampler(func=roughly_balanced_bagging, kw_args={"replace": True}) ) cv_results = cross_validate(rbb, X, y, scoring="balanced_accuracy") print(f"{cv_results['test_score'].mean():.3f} +/- {cv_results['test_score'].std():.3f}") # %% [markdown] # .. topic:: References: # # .. [1] R. Maclin, and D. Opitz. "An empirical evaluation of bagging and # boosting." AAAI/IAAI 1997 (1997): 546-551. # # .. [2] S. Wang, and X. Yao. "Diversity analysis on imbalanced data sets by # using ensemble models." 2009 IEEE symposium on computational # intelligence and data mining. IEEE, 2009. # # .. [3] S. Hido, H. Kashima, and Y. Takahashi. "Roughly balanced bagging # for imbalanced data." Statistical Analysis and Data Mining: The ASA # Data Science Journal 2.5‐6 (2009): 412-426. plot_comparison_ensemble_classifier.py000066400000000000000000000162521521136113700343750ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/ensemble""" ============================================= Compare ensemble classifiers using resampling ============================================= Ensemble classifiers have shown to improve classification performance compare to single learner. However, they will be affected by class imbalance. This example shows the benefit of balancing the training set before to learn learners. We are making the comparison with non-balanced ensemble methods. We make a comparison using the balanced accuracy and geometric mean which are metrics widely used in the literature to evaluate models learned on imbalanced set. """ # Authors: Guillaume Lemaitre # License: MIT # %% print(__doc__) # %% [markdown] # Load an imbalanced dataset # -------------------------- # # We will load the UCI SatImage dataset which has an imbalanced ratio of 9.3:1 # (number of majority sample for a minority sample). The data are then split # into training and testing. from sklearn.model_selection import train_test_split # %% from imblearn.datasets import fetch_datasets satimage = fetch_datasets()["satimage"] X, y = satimage.data, satimage.target X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=0) # %% [markdown] # Classification using a single decision tree # ------------------------------------------- # # We train a decision tree classifier which will be used as a baseline for the # rest of this example. # # The results are reported in terms of balanced accuracy and geometric mean # which are metrics widely used in the literature to validate model trained on # imbalanced set. # %% from sklearn.tree import DecisionTreeClassifier tree = DecisionTreeClassifier() tree.fit(X_train, y_train) y_pred_tree = tree.predict(X_test) # %% from sklearn.metrics import balanced_accuracy_score from imblearn.metrics import geometric_mean_score print("Decision tree classifier performance:") print( f"Balanced accuracy: {balanced_accuracy_score(y_test, y_pred_tree):.2f} - " f"Geometric mean {geometric_mean_score(y_test, y_pred_tree):.2f}" ) # %% import seaborn as sns from sklearn.metrics import ConfusionMatrixDisplay sns.set_context("poster") disp = ConfusionMatrixDisplay.from_estimator(tree, X_test, y_test, colorbar=False) _ = disp.ax_.set_title("Decision tree") # %% [markdown] # Classification using bagging classifier with and without sampling # ----------------------------------------------------------------- # # Instead of using a single tree, we will check if an ensemble of decision tree # can actually alleviate the issue induced by the class imbalancing. First, we # will use a bagging classifier and its counter part which internally uses a # random under-sampling to balanced each bootstrap sample. # %% from sklearn.ensemble import BaggingClassifier from imblearn.ensemble import BalancedBaggingClassifier bagging = BaggingClassifier(n_estimators=50, random_state=0) balanced_bagging = BalancedBaggingClassifier(n_estimators=50, random_state=0) bagging.fit(X_train, y_train) balanced_bagging.fit(X_train, y_train) y_pred_bc = bagging.predict(X_test) y_pred_bbc = balanced_bagging.predict(X_test) # %% [markdown] # Balancing each bootstrap sample allows to increase significantly the balanced # accuracy and the geometric mean. # %% print("Bagging classifier performance:") print( f"Balanced accuracy: {balanced_accuracy_score(y_test, y_pred_bc):.2f} - " f"Geometric mean {geometric_mean_score(y_test, y_pred_bc):.2f}" ) print("Balanced Bagging classifier performance:") print( f"Balanced accuracy: {balanced_accuracy_score(y_test, y_pred_bbc):.2f} - " f"Geometric mean {geometric_mean_score(y_test, y_pred_bbc):.2f}" ) # %% import matplotlib.pyplot as plt fig, axs = plt.subplots(ncols=2, figsize=(10, 5)) ConfusionMatrixDisplay.from_estimator( bagging, X_test, y_test, ax=axs[0], colorbar=False ) axs[0].set_title("Bagging") ConfusionMatrixDisplay.from_estimator( balanced_bagging, X_test, y_test, ax=axs[1], colorbar=False ) axs[1].set_title("Balanced Bagging") fig.tight_layout() # %% [markdown] # Classification using random forest classifier with and without sampling # ----------------------------------------------------------------------- # # Random forest is another popular ensemble method and it is usually # outperforming bagging. Here, we used a vanilla random forest and its balanced # counterpart in which each bootstrap sample is balanced. # %% from sklearn.ensemble import RandomForestClassifier from imblearn.ensemble import BalancedRandomForestClassifier rf = RandomForestClassifier(n_estimators=50, random_state=0) brf = BalancedRandomForestClassifier( n_estimators=50, sampling_strategy="all", replacement=True, bootstrap=False, random_state=0, ) rf.fit(X_train, y_train) brf.fit(X_train, y_train) y_pred_rf = rf.predict(X_test) y_pred_brf = brf.predict(X_test) # %% [markdown] # Similarly to the previous experiment, the balanced classifier outperform the # classifier which learn from imbalanced bootstrap samples. In addition, random # forest outperforms the bagging classifier. # %% print("Random Forest classifier performance:") print( f"Balanced accuracy: {balanced_accuracy_score(y_test, y_pred_rf):.2f} - " f"Geometric mean {geometric_mean_score(y_test, y_pred_rf):.2f}" ) print("Balanced Random Forest classifier performance:") print( f"Balanced accuracy: {balanced_accuracy_score(y_test, y_pred_brf):.2f} - " f"Geometric mean {geometric_mean_score(y_test, y_pred_brf):.2f}" ) # %% fig, axs = plt.subplots(ncols=2, figsize=(10, 5)) ConfusionMatrixDisplay.from_estimator(rf, X_test, y_test, ax=axs[0], colorbar=False) axs[0].set_title("Random forest") ConfusionMatrixDisplay.from_estimator(brf, X_test, y_test, ax=axs[1], colorbar=False) axs[1].set_title("Balanced random forest") fig.tight_layout() # %% [markdown] # Boosting classifier # ------------------- # # In the same manner, easy ensemble classifier is a bag of balanced AdaBoost # classifier. However, it will be slower to train than random forest and will # achieve worse performance. # %% from sklearn.ensemble import AdaBoostClassifier from imblearn.ensemble import EasyEnsembleClassifier, RUSBoostClassifier estimator = AdaBoostClassifier(n_estimators=10) eec = EasyEnsembleClassifier(n_estimators=10, estimator=estimator) eec.fit(X_train, y_train) y_pred_eec = eec.predict(X_test) rusboost = RUSBoostClassifier(n_estimators=10, estimator=estimator) rusboost.fit(X_train, y_train) y_pred_rusboost = rusboost.predict(X_test) # %% print("Easy ensemble classifier performance:") print( f"Balanced accuracy: {balanced_accuracy_score(y_test, y_pred_eec):.2f} - " f"Geometric mean {geometric_mean_score(y_test, y_pred_eec):.2f}" ) print("RUSBoost classifier performance:") print( f"Balanced accuracy: {balanced_accuracy_score(y_test, y_pred_rusboost):.2f} - " f"Geometric mean {geometric_mean_score(y_test, y_pred_rusboost):.2f}" ) # %% fig, axs = plt.subplots(ncols=2, figsize=(10, 5)) ConfusionMatrixDisplay.from_estimator(eec, X_test, y_test, ax=axs[0], colorbar=False) axs[0].set_title("Easy Ensemble") ConfusionMatrixDisplay.from_estimator( rusboost, X_test, y_test, ax=axs[1], colorbar=False ) axs[1].set_title("RUSBoost classifier") fig.tight_layout() plt.show() scikit-learn-contrib-imbalanced-learn-8504e95/examples/evaluation/000077500000000000000000000000001521136113700251635ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/evaluation/README.txt000066400000000000000000000002221521136113700266550ustar00rootroot00000000000000.. _evaluation_examples: Evaluation examples ------------------- Examples illustrating how classification using imbalanced dataset can be done. scikit-learn-contrib-imbalanced-learn-8504e95/examples/evaluation/plot_classification_report.py000066400000000000000000000030601521136113700331600ustar00rootroot00000000000000""" ============================================= Evaluate classification by compiling a report ============================================= Specific metrics have been developed to evaluate classifier which has been trained using imbalanced data. :mod:`imblearn` provides a classification report similar to :mod:`sklearn`, with additional metrics specific to imbalanced learning problem. """ # Authors: Guillaume Lemaitre # License: MIT from sklearn import datasets from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler from imblearn import over_sampling as os from imblearn import pipeline as pl from imblearn.metrics import classification_report_imbalanced print(__doc__) RANDOM_STATE = 42 # Generate a dataset X, y = datasets.make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=10, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=4, n_samples=5000, random_state=RANDOM_STATE, ) pipeline = pl.make_pipeline( StandardScaler(), os.SMOTE(random_state=RANDOM_STATE), LogisticRegression(max_iter=10_000), ) # Split the data X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=RANDOM_STATE) # Train the classifier with balancing pipeline.fit(X_train, y_train) # Test the classifier and get the prediction y_pred_bal = pipeline.predict(X_test) # Show the classification report print(classification_report_imbalanced(y_test, y_pred_bal)) scikit-learn-contrib-imbalanced-learn-8504e95/examples/evaluation/plot_metrics.py000066400000000000000000000055241521136113700302470ustar00rootroot00000000000000""" ======================================= Metrics specific to imbalanced learning ======================================= Specific metrics have been developed to evaluate classifier which has been trained using imbalanced data. :mod:`imblearn` provides mainly two additional metrics which are not implemented in :mod:`sklearn`: (i) geometric mean and (ii) index balanced accuracy. """ # Authors: Guillaume Lemaitre # License: MIT # %% print(__doc__) RANDOM_STATE = 42 # %% [markdown] # First, we will generate some imbalanced dataset. # %% from sklearn.datasets import make_classification X, y = make_classification( n_classes=3, class_sep=2, weights=[0.1, 0.9], n_informative=10, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=4, n_samples=5000, random_state=RANDOM_STATE, ) # %% [markdown] # We will split the data into a training and testing set. # %% from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split( X, y, stratify=y, random_state=RANDOM_STATE ) # %% [markdown] # We will create a pipeline made of a :class:`~imblearn.over_sampling.SMOTE` # over-sampler followed by a :class:`~sklearn.linear_model.LogisticRegression` # classifier. from sklearn.linear_model import LogisticRegression from sklearn.preprocessing import StandardScaler from imblearn.over_sampling import SMOTE # %% from imblearn.pipeline import make_pipeline model = make_pipeline( StandardScaler(), SMOTE(random_state=RANDOM_STATE), LogisticRegression(max_iter=10_000, random_state=RANDOM_STATE), ) # %% [markdown] # Now, we will train the model on the training set and get the prediction # associated with the testing set. Be aware that the resampling will happen # only when calling `fit`: the number of samples in `y_pred` is the same than # in `y_test`. # %% model.fit(X_train, y_train) y_pred = model.predict(X_test) # %% [markdown] # The geometric mean corresponds to the square root of the product of the # sensitivity and specificity. Combining the two metrics should account for # the balancing of the dataset. # %% from imblearn.metrics import geometric_mean_score print(f"The geometric mean is {geometric_mean_score(y_test, y_pred):.3f}") # %% [markdown] # The index balanced accuracy can transform any metric to be used in # imbalanced learning problems. # %% from imblearn.metrics import make_index_balanced_accuracy alpha = 0.1 geo_mean = make_index_balanced_accuracy(alpha=alpha, squared=True)(geometric_mean_score) print( f"The IBA using alpha={alpha} and the geometric mean: " f"{geo_mean(y_test, y_pred):.3f}" ) # %% alpha = 0.5 geo_mean = make_index_balanced_accuracy(alpha=alpha, squared=True)(geometric_mean_score) print( f"The IBA using alpha={alpha} and the geometric mean: " f"{geo_mean(y_test, y_pred):.3f}" ) scikit-learn-contrib-imbalanced-learn-8504e95/examples/model_selection/000077500000000000000000000000001521136113700261615ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/model_selection/README.txt000066400000000000000000000001701521136113700276550ustar00rootroot00000000000000.. _model_selection_examples: Model Selection --------------- Examples related to the selection of balancing methods. scikit-learn-contrib-imbalanced-learn-8504e95/examples/model_selection/plot_instance_hardness_cv.py000066400000000000000000000071701521136113700337610ustar00rootroot00000000000000""" ==================================================== Distribute hard-to-classify datapoints over CV folds ==================================================== 'Instance hardness' refers to the difficulty to classify an instance. The way hard-to-classify instances are distributed over train and test sets has significant effect on the test set performance metrics. In this example we show how to deal with this problem. We are making the comparison with normal :class:`~sklearn.model_selection.StratifiedKFold` cross-validation splitter. """ # Authors: Frits Hermans, https://fritshermans.github.io # License: MIT # %% print(__doc__) # %% # Create an imbalanced dataset with instance hardness # --------------------------------------------------- # # We create an imbalanced dataset with using scikit-learn's # :func:`~sklearn.datasets.make_blobs` function and set the class imbalance ratio to # 5%. import numpy as np from matplotlib import pyplot as plt from sklearn.datasets import make_blobs X, y = make_blobs(n_samples=[950, 50], centers=((-3, 0), (3, 0)), random_state=10) _ = plt.scatter(X[:, 0], X[:, 1], c=y) # %% # To introduce instance hardness in our dataset, we add some hard to classify samples: X_hard, y_hard = make_blobs( n_samples=10, centers=((3, 0), (-3, 0)), cluster_std=1, random_state=10 ) X, y = np.vstack((X, X_hard)), np.hstack((y, y_hard)) _ = plt.scatter(X[:, 0], X[:, 1], c=y) # %% # Compare cross validation scores using `StratifiedKFold` and `InstanceHardnessCV` # -------------------------------------------------------------------------------- # # Now, we want to assess a linear predictive model. Therefore, we should use # cross-validation. The most important concept with cross-validation is to create # training and test splits that are representative of the the data in production to have # statistical results that one can expect in production. # # By applying a standard :class:`~sklearn.model_selection.StratifiedKFold` # cross-validation splitter, we do not control in which fold the hard-to-classify # samples will be. # # The :class:`~imblearn.model_selection.InstanceHardnessCV` splitter allows to # control the distribution of the hard-to-classify samples over the folds. # # Let's make an experiment to compare the results that we get with both splitters. # We use a :class:`~sklearn.linear_model.LogisticRegression` classifier and # :func:`~sklearn.model_selection.cross_validate` to calculate the cross validation # scores. We use average precision for scoring. import pandas as pd from sklearn.linear_model import LogisticRegression from sklearn.model_selection import StratifiedKFold, cross_validate from imblearn.model_selection import InstanceHardnessCV logistic_regression = LogisticRegression() results = {} for cv in ( StratifiedKFold(n_splits=5, shuffle=True, random_state=10), InstanceHardnessCV(estimator=LogisticRegression()), ): result = cross_validate( logistic_regression, X, y, cv=cv, scoring="average_precision", ) results[cv.__class__.__name__] = result["test_score"] results = pd.DataFrame(results) # %% ax = results.plot.box(vert=False, whis=[0, 100]) _ = ax.set( xlabel="Average precision", title="Cross validation scores with different splitters", xlim=(0, 1), ) # %% # The boxplot shows that the :class:`~imblearn.model_selection.InstanceHardnessCV` # splitter results in less variation of average precision than # :class:`~sklearn.model_selection.StratifiedKFold` splitter. When doing # hyperparameter tuning or feature selection using a wrapper method (like # :class:`~sklearn.feature_selection.RFECV`) this will give more stable results. scikit-learn-contrib-imbalanced-learn-8504e95/examples/model_selection/plot_validation_curve.py000066400000000000000000000061251521136113700331330ustar00rootroot00000000000000""" ========================== Plotting Validation Curves ========================== In this example the impact of the :class:`~imblearn.over_sampling.SMOTE`'s `k_neighbors` parameter is examined. In the plot you can see the validation scores of a SMOTE-CART classifier for different values of the :class:`~imblearn.over_sampling.SMOTE`'s `k_neighbors` parameter. """ # Authors: Christos Aridas # Guillaume Lemaitre # License: MIT # %% print(__doc__) import seaborn as sns sns.set_context("poster") RANDOM_STATE = 42 # %% [markdown] # Let's first generate a dataset with imbalanced class distribution. # %% from sklearn.datasets import make_classification X, y = make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=10, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=4, n_samples=5000, random_state=RANDOM_STATE, ) # %% [markdown] # We will use an over-sampler :class:`~imblearn.over_sampling.SMOTE` followed # by a :class:`~sklearn.tree.DecisionTreeClassifier`. The aim will be to # search which `k_neighbors` parameter is the most adequate with the dataset # that we generated. from sklearn.tree import DecisionTreeClassifier # %% from imblearn.over_sampling import SMOTE from imblearn.pipeline import make_pipeline model = make_pipeline( SMOTE(random_state=RANDOM_STATE), DecisionTreeClassifier(random_state=RANDOM_STATE) ) # %% [markdown] # We can use the :class:`~sklearn.model_selection.validation_curve` to inspect # the impact of varying the parameter `k_neighbors`. In this case, we need # to use a score to evaluate the generalization score during the # cross-validation. # %% from sklearn.metrics import cohen_kappa_score, make_scorer from sklearn.model_selection import validation_curve scorer = make_scorer(cohen_kappa_score) param_range = range(1, 11) train_scores, test_scores = validation_curve( model, X, y, param_name="smote__k_neighbors", param_range=param_range, cv=3, scoring=scorer, ) # %% train_scores_mean = train_scores.mean(axis=1) train_scores_std = train_scores.std(axis=1) test_scores_mean = test_scores.mean(axis=1) test_scores_std = test_scores.std(axis=1) # %% [markdown] # We can now plot the results of the cross-validation for the different # parameter values that we tried. # %% import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(7, 7)) ax.plot(param_range, test_scores_mean, label="SMOTE") ax.fill_between( param_range, test_scores_mean + test_scores_std, test_scores_mean - test_scores_std, alpha=0.2, ) idx_max = test_scores_mean.argmax() ax.scatter( param_range[idx_max], test_scores_mean[idx_max], label=( r"Cohen Kappa:" rf" ${test_scores_mean[idx_max]:.2f}\pm{test_scores_std[idx_max]:.2f}$" ), ) fig.suptitle("Validation Curve with SMOTE-CART") ax.set_xlabel("Number of neighbors") ax.set_ylabel("Cohen's kappa") # make nice plotting sns.despine(ax=ax, offset=10) ax.set_xlim([1, 10]) ax.set_ylim([0.4, 0.8]) ax.legend(loc="lower right", fontsize=16) plt.tight_layout() plt.show() scikit-learn-contrib-imbalanced-learn-8504e95/examples/over-sampling/000077500000000000000000000000001521136113700255775ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/over-sampling/README.txt000066400000000000000000000003771521136113700273040ustar00rootroot00000000000000.. _over_sampling_examples: Example using over-sampling class methods ========================================= Data balancing can be performed by over-sampling such that new samples are generated in the minority class to reach a given balancing ratio. plot_comparison_over_sampling.py000066400000000000000000000253531521136113700342370ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/over-sampling""" ============================== Compare over-sampling samplers ============================== The following example attends to make a qualitative comparison between the different over-sampling algorithms available in the imbalanced-learn package. """ # Authors: Guillaume Lemaitre # License: MIT # %% print(__doc__) import matplotlib.pyplot as plt import seaborn as sns sns.set_context("poster") # %% [markdown] # The following function will be used to create toy dataset. It uses the # :func:`~sklearn.datasets.make_classification` from scikit-learn but fixing # some parameters. # %% from sklearn.datasets import make_classification def create_dataset( n_samples=1000, weights=(0.01, 0.01, 0.98), n_classes=3, class_sep=0.8, n_clusters=1, ): return make_classification( n_samples=n_samples, n_features=2, n_informative=2, n_redundant=0, n_repeated=0, n_classes=n_classes, n_clusters_per_class=n_clusters, weights=list(weights), class_sep=class_sep, random_state=0, ) # %% [markdown] # The following function will be used to plot the sample space after resampling # to illustrate the specificities of an algorithm. # %% def plot_resampling(X, y, sampler, ax, title=None): X_res, y_res = sampler.fit_resample(X, y) ax.scatter(X_res[:, 0], X_res[:, 1], c=y_res, alpha=0.8, edgecolor="k") if title is None: title = f"Resampling with {sampler.__class__.__name__}" ax.set_title(title) sns.despine(ax=ax, offset=10) # %% [markdown] # The following function will be used to plot the decision function of a # classifier given some data. # %% import numpy as np def plot_decision_function(X, y, clf, ax, title=None): plot_step = 0.02 x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1 y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1 xx, yy = np.meshgrid( np.arange(x_min, x_max, plot_step), np.arange(y_min, y_max, plot_step) ) Z = clf.predict(np.c_[xx.ravel(), yy.ravel()]) Z = Z.reshape(xx.shape) ax.contourf(xx, yy, Z, alpha=0.4) ax.scatter(X[:, 0], X[:, 1], alpha=0.8, c=y, edgecolor="k") if title is not None: ax.set_title(title) # %% [markdown] # Illustration of the influence of the balancing ratio # ---------------------------------------------------- # # We will first illustrate the influence of the balancing ratio on some toy # data using a logistic regression classifier which is a linear model. # %% from sklearn.linear_model import LogisticRegression clf = LogisticRegression() # %% [markdown] # We will fit and show the decision boundary model to illustrate the impact of # dealing with imbalanced classes. # %% fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(15, 12)) weights_arr = ( (0.01, 0.01, 0.98), (0.01, 0.05, 0.94), (0.2, 0.1, 0.7), (0.33, 0.33, 0.33), ) for ax, weights in zip(axs.ravel(), weights_arr): X, y = create_dataset(n_samples=300, weights=weights) clf.fit(X, y) plot_decision_function(X, y, clf, ax, title=f"weight={weights}") fig.suptitle(f"Decision function of {clf.__class__.__name__}") fig.tight_layout() # %% [markdown] # Greater is the difference between the number of samples in each class, poorer # are the classification results. # # Random over-sampling to balance the data set # -------------------------------------------- # # Random over-sampling can be used to repeat some samples and balance the # number of samples between the dataset. It can be seen that with this trivial # approach the boundary decision is already less biased toward the majority # class. The class :class:`~imblearn.over_sampling.RandomOverSampler` # implements such of a strategy. from imblearn.over_sampling import RandomOverSampler # %% from imblearn.pipeline import make_pipeline X, y = create_dataset(n_samples=100, weights=(0.05, 0.25, 0.7)) fig, axs = plt.subplots(nrows=1, ncols=2, figsize=(15, 7)) clf.fit(X, y) plot_decision_function(X, y, clf, axs[0], title="Without resampling") sampler = RandomOverSampler(random_state=0) model = make_pipeline(sampler, clf).fit(X, y) plot_decision_function(X, y, model, axs[1], f"Using {model[0].__class__.__name__}") fig.suptitle(f"Decision function of {clf.__class__.__name__}") fig.tight_layout() # %% [markdown] # By default, random over-sampling generates a bootstrap. The parameter # `shrinkage` allows adding a small perturbation to the generated data # to generate a smoothed bootstrap instead. The plot below shows the difference # between the two data generation strategies. # %% fig, axs = plt.subplots(nrows=1, ncols=2, figsize=(15, 7)) sampler.set_params(shrinkage=None) plot_resampling(X, y, sampler, ax=axs[0], title="Normal bootstrap") sampler.set_params(shrinkage=0.3) plot_resampling(X, y, sampler, ax=axs[1], title="Smoothed bootstrap") fig.suptitle(f"Resampling with {sampler.__class__.__name__}") fig.tight_layout() # %% [markdown] # It looks like more samples are generated with smoothed bootstrap. This is due # to the fact that the samples generated are not superimposing with the # original samples. # # More advanced over-sampling using ADASYN and SMOTE # -------------------------------------------------- # # Instead of repeating the same samples when over-sampling or perturbating the # generated bootstrap samples, one can use some specific heuristic instead. # :class:`~imblearn.over_sampling.ADASYN` and # :class:`~imblearn.over_sampling.SMOTE` can be used in this case. # %% from imblearn import FunctionSampler # to use a idendity sampler from imblearn.over_sampling import ADASYN, SMOTE X, y = create_dataset(n_samples=150, weights=(0.1, 0.2, 0.7)) fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(15, 15)) samplers = [ FunctionSampler(), RandomOverSampler(random_state=0), SMOTE(random_state=0), ADASYN(random_state=0), ] for ax, sampler in zip(axs.ravel(), samplers): title = "Original dataset" if isinstance(sampler, FunctionSampler) else None plot_resampling(X, y, sampler, ax, title=title) fig.tight_layout() # %% [markdown] # The following plot illustrates the difference between # :class:`~imblearn.over_sampling.ADASYN` and # :class:`~imblearn.over_sampling.SMOTE`. # :class:`~imblearn.over_sampling.ADASYN` will focus on the samples which are # difficult to classify with a nearest-neighbors rule while regular # :class:`~imblearn.over_sampling.SMOTE` will not make any distinction. # Therefore, the decision function depending of the algorithm. X, y = create_dataset(n_samples=150, weights=(0.05, 0.25, 0.7)) fig, axs = plt.subplots(nrows=1, ncols=3, figsize=(20, 6)) models = { "Without sampler": clf, "ADASYN sampler": make_pipeline(ADASYN(random_state=0), clf), "SMOTE sampler": make_pipeline(SMOTE(random_state=0), clf), } for ax, (title, model) in zip(axs, models.items()): model.fit(X, y) plot_decision_function(X, y, model, ax=ax, title=title) fig.suptitle(f"Decision function using a {clf.__class__.__name__}") fig.tight_layout() # %% [markdown] # Due to those sampling particularities, it can give rise to some specific # issues as illustrated below. # %% X, y = create_dataset(n_samples=5000, weights=(0.01, 0.05, 0.94), class_sep=0.8) samplers = [SMOTE(random_state=0), ADASYN(random_state=0)] fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(15, 15)) for ax, sampler in zip(axs, samplers): model = make_pipeline(sampler, clf).fit(X, y) plot_decision_function( X, y, clf, ax[0], title=f"Decision function with {sampler.__class__.__name__}" ) plot_resampling(X, y, sampler, ax[1]) fig.suptitle("Particularities of over-sampling with SMOTE and ADASYN") fig.tight_layout() # %% [markdown] # SMOTE proposes several variants by identifying specific samples to consider # during the resampling. The borderline version # (:class:`~imblearn.over_sampling.BorderlineSMOTE`) will detect which point to # select which are in the border between two classes. The SVM version # (:class:`~imblearn.over_sampling.SVMSMOTE`) will use the support vectors # found using an SVM algorithm to create new sample while the KMeans version # (:class:`~imblearn.over_sampling.KMeansSMOTE`) will make a clustering before # to generate samples in each cluster independently depending each cluster # density. # %% from sklearn.cluster import MiniBatchKMeans from imblearn.over_sampling import SVMSMOTE, BorderlineSMOTE, KMeansSMOTE X, y = create_dataset(n_samples=5000, weights=(0.01, 0.05, 0.94), class_sep=0.8) fig, axs = plt.subplots(5, 2, figsize=(15, 30)) samplers = [ SMOTE(random_state=0), BorderlineSMOTE(random_state=0, kind="borderline-1"), BorderlineSMOTE(random_state=0, kind="borderline-2"), KMeansSMOTE( kmeans_estimator=MiniBatchKMeans(n_clusters=10, n_init=1, random_state=0), random_state=0, ), SVMSMOTE(random_state=0), ] for ax, sampler in zip(axs, samplers): model = make_pipeline(sampler, clf).fit(X, y) plot_decision_function( X, y, clf, ax[0], title=f"Decision function for {sampler.__class__.__name__}" ) plot_resampling(X, y, sampler, ax[1]) fig.suptitle("Decision function and resampling using SMOTE variants") fig.tight_layout() # %% [markdown] # When dealing with a mixed of continuous and categorical features, # :class:`~imblearn.over_sampling.SMOTENC` is the only method which can handle # this case. # %% from collections import Counter from imblearn.over_sampling import SMOTENC rng = np.random.RandomState(42) n_samples = 50 # Create a dataset of a mix of numerical and categorical data X = np.empty((n_samples, 3), dtype=object) X[:, 0] = rng.choice(["A", "B", "C"], size=n_samples).astype(object) X[:, 1] = rng.randn(n_samples) X[:, 2] = rng.randint(3, size=n_samples) y = np.array([0] * 20 + [1] * 30) print("The original imbalanced dataset") print(sorted(Counter(y).items())) print() print("The first and last columns are containing categorical features:") print(X[:5]) print() smote_nc = SMOTENC(categorical_features=[0, 2], random_state=0) X_resampled, y_resampled = smote_nc.fit_resample(X, y) print("Dataset after resampling:") print(sorted(Counter(y_resampled).items())) print() print("SMOTE-NC will generate categories for the categorical features:") print(X_resampled[-5:]) print() # %% [markdown] # However, if the dataset is composed of only categorical features then one # should use :class:`~imblearn.over_sampling.SMOTEN`. # %% from imblearn.over_sampling import SMOTEN # Generate only categorical data X = np.array(["A"] * 10 + ["B"] * 20 + ["C"] * 30, dtype=object).reshape(-1, 1) y = np.array([0] * 20 + [1] * 40, dtype=np.int32) print(f"Original class counts: {Counter(y)}") print() print(X[:5]) print() sampler = SMOTEN(random_state=0) X_res, y_res = sampler.fit_resample(X, y) print(f"Class counts after resampling {Counter(y_res)}") print() print(X_res[-5:]) print() plot_illustration_generation_sample.py000066400000000000000000000037321521136113700354420ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/over-sampling""" ============================================ Sample generator used in SMOTE-like samplers ============================================ This example illustrates how a new sample is generated taking into account the neighbourhood of this sample. A new sample is generated by selecting the randomly 2 samples of the same class and interpolating a point between these samples. """ # Authors: Guillaume Lemaitre # License: MIT # %% print(__doc__) import matplotlib.pyplot as plt import numpy as np import seaborn as sns sns.set_context("poster") rng = np.random.RandomState(18) f, ax = plt.subplots(figsize=(8, 8)) # generate some data points y = np.array([3.65284, 3.52623, 3.51468, 3.22199, 3.21]) z = np.array([0.43, 0.45, 0.6, 0.4, 0.211]) y_2 = np.array([3.3, 3.6]) z_2 = np.array([0.58, 0.34]) # plot the majority and minority samples ax.scatter(z, y, label="Minority class", s=100) ax.scatter(z_2, y_2, label="Majority class", s=100) idx = rng.randint(len(y), size=2) annotation = [r"$x_i$", r"$x_{zi}$"] for a, i in zip(annotation, idx): ax.annotate(a, (z[i], y[i]), xytext=tuple([z[i] + 0.01, y[i] + 0.005]), fontsize=15) # draw the circle in which the new sample will generated radius = np.sqrt((z[idx[0]] - z[idx[1]]) ** 2 + (y[idx[0]] - y[idx[1]]) ** 2) circle = plt.Circle((z[idx[0]], y[idx[0]]), radius=radius, alpha=0.2) ax.add_artist(circle) # plot the line on which the sample will be generated ax.plot(z[idx], y[idx], "--", alpha=0.5) # create and plot the new sample step = rng.uniform() y_gen = y[idx[0]] + step * (y[idx[1]] - y[idx[0]]) z_gen = z[idx[0]] + step * (z[idx[1]] - z[idx[0]]) ax.scatter(z_gen, y_gen, s=100) ax.annotate( r"$x_{new}$", (z_gen, y_gen), xytext=tuple([z_gen + 0.01, y_gen + 0.005]), fontsize=15, ) # make the plot nicer with legend and label sns.despine(ax=ax, offset=10) ax.set_xlim([0.2, 0.7]) ax.set_ylim([3.2, 3.7]) plt.xlabel(r"$X_1$") plt.ylabel(r"$X_2$") plt.legend() plt.tight_layout() plt.show() scikit-learn-contrib-imbalanced-learn-8504e95/examples/over-sampling/plot_shrinkage_effect.py000066400000000000000000000075641521136113700325120ustar00rootroot00000000000000""" ====================================================== Effect of the shrinkage factor in random over-sampling ====================================================== This example shows the effect of the shrinkage factor used to generate the smoothed bootstrap using the :class:`~imblearn.over_sampling.RandomOverSampler`. """ # Authors: Guillaume Lemaitre # License: MIT # %% print(__doc__) import seaborn as sns sns.set_context("poster") # %% # First, we will generate a toy classification dataset with only few samples. # The ratio between the classes will be imbalanced. from collections import Counter from sklearn.datasets import make_classification X, y = make_classification( n_samples=100, n_features=2, n_redundant=0, weights=[0.1, 0.9], random_state=0, ) Counter(y) # %% import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(7, 7)) scatter = plt.scatter(X[:, 0], X[:, 1], c=y, alpha=0.4) class_legend = ax.legend(*scatter.legend_elements(), loc="lower left", title="Classes") ax.add_artist(class_legend) ax.set_xlabel("Feature #1") _ = ax.set_ylabel("Feature #2") plt.tight_layout() # %% # Now, we will use a :class:`~imblearn.over_sampling.RandomOverSampler` to # generate a bootstrap for the minority class with as many samples as in the # majority class. from imblearn.over_sampling import RandomOverSampler sampler = RandomOverSampler(random_state=0) X_res, y_res = sampler.fit_resample(X, y) Counter(y_res) # %% fig, ax = plt.subplots(figsize=(7, 7)) scatter = plt.scatter(X_res[:, 0], X_res[:, 1], c=y_res, alpha=0.4) class_legend = ax.legend(*scatter.legend_elements(), loc="lower left", title="Classes") ax.add_artist(class_legend) ax.set_xlabel("Feature #1") _ = ax.set_ylabel("Feature #2") plt.tight_layout() # %% # We observe that the minority samples are less transparent than the samples # from the majority class. Indeed, it is due to the fact that these samples # of the minority class are repeated during the bootstrap generation. # # We can set `shrinkage` to a floating value to add a small perturbation to the # samples created and therefore create a smoothed bootstrap. sampler = RandomOverSampler(shrinkage=1, random_state=0) X_res, y_res = sampler.fit_resample(X, y) Counter(y_res) # %% fig, ax = plt.subplots(figsize=(7, 7)) scatter = plt.scatter(X_res[:, 0], X_res[:, 1], c=y_res, alpha=0.4) class_legend = ax.legend(*scatter.legend_elements(), loc="lower left", title="Classes") ax.add_artist(class_legend) ax.set_xlabel("Feature #1") _ = ax.set_ylabel("Feature #2") plt.tight_layout() # %% # In this case, we see that the samples in the minority class are not # overlapping anymore due to the added noise. # # The parameter `shrinkage` allows to add more or less perturbation. Let's # add more perturbation when generating the smoothed bootstrap. sampler = RandomOverSampler(shrinkage=3, random_state=0) X_res, y_res = sampler.fit_resample(X, y) Counter(y_res) # %% fig, ax = plt.subplots(figsize=(7, 7)) scatter = plt.scatter(X_res[:, 0], X_res[:, 1], c=y_res, alpha=0.4) class_legend = ax.legend(*scatter.legend_elements(), loc="lower left", title="Classes") ax.add_artist(class_legend) ax.set_xlabel("Feature #1") _ = ax.set_ylabel("Feature #2") plt.tight_layout() # %% # Increasing the value of `shrinkage` will disperse the new samples. Forcing # the shrinkage to 0 will be equivalent to generating a normal bootstrap. sampler = RandomOverSampler(shrinkage=0, random_state=0) X_res, y_res = sampler.fit_resample(X, y) Counter(y_res) # %% fig, ax = plt.subplots(figsize=(7, 7)) scatter = plt.scatter(X_res[:, 0], X_res[:, 1], c=y_res, alpha=0.4) class_legend = ax.legend(*scatter.legend_elements(), loc="lower left", title="Classes") ax.add_artist(class_legend) ax.set_xlabel("Feature #1") _ = ax.set_ylabel("Feature #2") plt.tight_layout() # %% # Therefore, the `shrinkage` is handy to manually tune the dispersion of the # new samples. scikit-learn-contrib-imbalanced-learn-8504e95/examples/pipeline/000077500000000000000000000000001521136113700246215ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/pipeline/README.txt000066400000000000000000000002341521136113700263160ustar00rootroot00000000000000.. _pipeline_examples: Pipeline examples ================= Example of how to use the a pipeline to include under-sampling with `scikit-learn` estimators. scikit-learn-contrib-imbalanced-learn-8504e95/examples/pipeline/plot_pipeline_classification.py000066400000000000000000000037261521136113700331210ustar00rootroot00000000000000""" ==================================== Usage of pipeline embedding samplers ==================================== An example of the :class:~imblearn.pipeline.Pipeline` object (or :func:`~imblearn.pipeline.make_pipeline` helper function) working with transformers and resamplers. """ # Authors: Christos Aridas # Guillaume Lemaitre # License: MIT # %% print(__doc__) # %% [markdown] # Let's first create an imbalanced dataset and split in to two sets. # %% from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split X, y = make_classification( n_classes=2, class_sep=1.25, weights=[0.3, 0.7], n_informative=3, n_redundant=1, flip_y=0, n_features=5, n_clusters_per_class=1, n_samples=5000, random_state=10, ) X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=42) # %% [markdown] # Now, we will create each individual steps that we would like later to combine # %% from sklearn.decomposition import PCA from sklearn.neighbors import KNeighborsClassifier from imblearn.over_sampling import SMOTE from imblearn.under_sampling import EditedNearestNeighbours pca = PCA(n_components=2) enn = EditedNearestNeighbours() smote = SMOTE(random_state=0) knn = KNeighborsClassifier(n_neighbors=1) # %% [markdown] # Now, we can finally create a pipeline to specify in which order the different # transformers and samplers should be executed before to provide the data to # the final classifier. # %% from imblearn.pipeline import make_pipeline model = make_pipeline(pca, enn, smote, knn) # %% [markdown] # We can now use the pipeline created as a normal classifier where resampling # will happen when calling `fit` and disabled when calling `decision_function`, # `predict_proba`, or `predict`. # %% from sklearn.metrics import classification_report model.fit(X_train, y_train) y_pred = model.predict(X_test) print(classification_report(y_test, y_pred)) scikit-learn-contrib-imbalanced-learn-8504e95/examples/under-sampling/000077500000000000000000000000001521136113700257415ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/under-sampling/README.txt000066400000000000000000000005121521136113700274350ustar00rootroot00000000000000.. _under_sampling_examples: Example using under-sampling class methods ========================================== Under-sampling refers to the process of reducing the number of samples in the majority classes. The implemented methods can be categorized into 2 groups: (i) fixed under-sampling and (ii) cleaning under-sampling. plot_comparison_under_sampling.py000066400000000000000000000227531521136113700345440ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/under-sampling""" =============================== Compare under-sampling samplers =============================== The following example attends to make a qualitative comparison between the different under-sampling algorithms available in the imbalanced-learn package. """ # Authors: Guillaume Lemaitre # License: MIT # %% print(__doc__) import seaborn as sns sns.set_context("poster") # %% [markdown] # The following function will be used to create toy dataset. It uses the # :func:`~sklearn.datasets.make_classification` from scikit-learn but fixing # some parameters. # %% from sklearn.datasets import make_classification def create_dataset( n_samples=1000, weights=(0.01, 0.01, 0.98), n_classes=3, class_sep=0.8, n_clusters=1, ): return make_classification( n_samples=n_samples, n_features=2, n_informative=2, n_redundant=0, n_repeated=0, n_classes=n_classes, n_clusters_per_class=n_clusters, weights=list(weights), class_sep=class_sep, random_state=0, ) # %% [markdown] # The following function will be used to plot the sample space after resampling # to illustrate the specificities of an algorithm. # %% def plot_resampling(X, y, sampler, ax, title=None): X_res, y_res = sampler.fit_resample(X, y) ax.scatter(X_res[:, 0], X_res[:, 1], c=y_res, alpha=0.8, edgecolor="k") if title is None: title = f"Resampling with {sampler.__class__.__name__}" ax.set_title(title) sns.despine(ax=ax, offset=10) # %% [markdown] # The following function will be used to plot the decision function of a # classifier given some data. # %% import numpy as np def plot_decision_function(X, y, clf, ax, title=None): plot_step = 0.02 x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1 y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1 xx, yy = np.meshgrid( np.arange(x_min, x_max, plot_step), np.arange(y_min, y_max, plot_step) ) Z = clf.predict(np.c_[xx.ravel(), yy.ravel()]) Z = Z.reshape(xx.shape) ax.contourf(xx, yy, Z, alpha=0.4) ax.scatter(X[:, 0], X[:, 1], alpha=0.8, c=y, edgecolor="k") if title is not None: ax.set_title(title) # %% from sklearn.linear_model import LogisticRegression clf = LogisticRegression() # %% [markdown] # Prototype generation: under-sampling by generating new samples # -------------------------------------------------------------- # # :class:`~imblearn.under_sampling.ClusterCentroids` under-samples by replacing # the original samples by the centroids of the cluster found. # %% import matplotlib.pyplot as plt from sklearn.cluster import MiniBatchKMeans from imblearn import FunctionSampler from imblearn.pipeline import make_pipeline from imblearn.under_sampling import ClusterCentroids X, y = create_dataset(n_samples=400, weights=(0.05, 0.15, 0.8), class_sep=0.8) samplers = { FunctionSampler(), # identity resampler ClusterCentroids( estimator=MiniBatchKMeans(n_init=1, random_state=0), random_state=0 ), } fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(15, 15)) for ax, sampler in zip(axs, samplers): model = make_pipeline(sampler, clf).fit(X, y) plot_decision_function( X, y, model, ax[0], title=f"Decision function with {sampler.__class__.__name__}" ) plot_resampling(X, y, sampler, ax[1]) fig.tight_layout() # %% [markdown] # Prototype selection: under-sampling by selecting existing samples # ----------------------------------------------------------------- # # The algorithm performing prototype selection can be subdivided into two # groups: (i) the controlled under-sampling methods and (ii) the cleaning # under-sampling methods. # # With the controlled under-sampling methods, the number of samples to be # selected can be specified. # :class:`~imblearn.under_sampling.RandomUnderSampler` is the most naive way of # performing such selection by randomly selecting a given number of samples by # the targeted class. # %% from imblearn.under_sampling import RandomUnderSampler X, y = create_dataset(n_samples=400, weights=(0.05, 0.15, 0.8), class_sep=0.8) samplers = { FunctionSampler(), # identity resampler RandomUnderSampler(random_state=0), } fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(15, 15)) for ax, sampler in zip(axs, samplers): model = make_pipeline(sampler, clf).fit(X, y) plot_decision_function( X, y, model, ax[0], title=f"Decision function with {sampler.__class__.__name__}" ) plot_resampling(X, y, sampler, ax[1]) fig.tight_layout() # %% [markdown] # :class:`~imblearn.under_sampling.NearMiss` algorithms implement some # heuristic rules in order to select samples. NearMiss-1 selects samples from # the majority class for which the average distance of the :math:`k`` nearest # samples of the minority class is the smallest. NearMiss-2 selects the samples # from the majority class for which the average distance to the farthest # samples of the negative class is the smallest. NearMiss-3 is a 2-step # algorithm: first, for each minority sample, their :math:`m` # nearest-neighbors will be kept; then, the majority samples selected are the # on for which the average distance to the :math:`k` nearest neighbors is the # largest. # %% from imblearn.under_sampling import NearMiss X, y = create_dataset(n_samples=1000, weights=(0.05, 0.15, 0.8), class_sep=1.5) samplers = [NearMiss(version=1), NearMiss(version=2), NearMiss(version=3)] fig, axs = plt.subplots(nrows=3, ncols=2, figsize=(15, 25)) for ax, sampler in zip(axs, samplers): model = make_pipeline(sampler, clf).fit(X, y) plot_decision_function( X, y, model, ax[0], title=f"Decision function for {sampler.__class__.__name__}-{sampler.version}", ) plot_resampling( X, y, sampler, ax[1], title=f"Resampling using {sampler.__class__.__name__}-{sampler.version}", ) fig.tight_layout() # %% [markdown] # :class:`~imblearn.under_sampling.EditedNearestNeighbours` removes samples of # the majority class for which their class differ from the one of their # nearest-neighbors. This sieve can be repeated which is the principle of the # :class:`~imblearn.under_sampling.RepeatedEditedNearestNeighbours`. # :class:`~imblearn.under_sampling.AllKNN` is slightly different from the # :class:`~imblearn.under_sampling.RepeatedEditedNearestNeighbours` by changing # the :math:`k` parameter of the internal nearest neighors algorithm, # increasing it at each iteration. # %% from imblearn.under_sampling import ( AllKNN, EditedNearestNeighbours, RepeatedEditedNearestNeighbours, ) X, y = create_dataset(n_samples=500, weights=(0.2, 0.3, 0.5), class_sep=0.8) samplers = [ EditedNearestNeighbours(), RepeatedEditedNearestNeighbours(), AllKNN(allow_minority=True), ] fig, axs = plt.subplots(3, 2, figsize=(15, 25)) for ax, sampler in zip(axs, samplers): model = make_pipeline(sampler, clf).fit(X, y) plot_decision_function( X, y, clf, ax[0], title=f"Decision function for \n{sampler.__class__.__name__}" ) plot_resampling( X, y, sampler, ax[1], title=f"Resampling using \n{sampler.__class__.__name__}" ) fig.tight_layout() # %% [markdown] # :class:`~imblearn.under_sampling.CondensedNearestNeighbour` makes use of a # 1-NN to iteratively decide if a sample should be kept in a dataset or not. # The issue is that :class:`~imblearn.under_sampling.CondensedNearestNeighbour` # is sensitive to noise by preserving the noisy samples. # :class:`~imblearn.under_sampling.OneSidedSelection` also used the 1-NN and # use :class:`~imblearn.under_sampling.TomekLinks` to remove the samples # considered noisy. The # :class:`~imblearn.under_sampling.NeighbourhoodCleaningRule` use a # :class:`~imblearn.under_sampling.EditedNearestNeighbours` to remove some # sample. Additionally, they use a 3 nearest-neighbors to remove samples which # do not agree with this rule. # %% from imblearn.under_sampling import ( CondensedNearestNeighbour, NeighbourhoodCleaningRule, OneSidedSelection, ) X, y = create_dataset(n_samples=500, weights=(0.2, 0.3, 0.5), class_sep=0.8) fig, axs = plt.subplots(nrows=3, ncols=2, figsize=(15, 25)) samplers = [ CondensedNearestNeighbour(random_state=0), OneSidedSelection(random_state=0), NeighbourhoodCleaningRule(n_neighbors=11), ] for ax, sampler in zip(axs, samplers): model = make_pipeline(sampler, clf).fit(X, y) plot_decision_function( X, y, clf, ax[0], title=f"Decision function for \n{sampler.__class__.__name__}" ) plot_resampling( X, y, sampler, ax[1], title=f"Resampling using \n{sampler.__class__.__name__}" ) fig.tight_layout() # %% [markdown] # :class:`~imblearn.under_sampling.InstanceHardnessThreshold` uses the # prediction of classifier to exclude samples. All samples which are classified # with a low probability will be removed. # %% from imblearn.under_sampling import InstanceHardnessThreshold samplers = { FunctionSampler(), # identity resampler InstanceHardnessThreshold( estimator=LogisticRegression(), random_state=0, ), } fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(15, 15)) for ax, sampler in zip(axs, samplers): model = make_pipeline(sampler, clf).fit(X, y) plot_decision_function( X, y, model, ax[0], title=f"Decision function with \n{sampler.__class__.__name__}", ) plot_resampling( X, y, sampler, ax[1], title=f"Resampling using \n{sampler.__class__.__name__}" ) fig.tight_layout() plt.show() scikit-learn-contrib-imbalanced-learn-8504e95/examples/under-sampling/plot_illustration_nearmiss.py000066400000000000000000000132071521136113700340060ustar00rootroot00000000000000""" ============================ Sample selection in NearMiss ============================ This example illustrates the different way of selecting example in :class:`~imblearn.under_sampling.NearMiss`. """ # Authors: Guillaume Lemaitre # License: MIT # %% print(__doc__) import seaborn as sns sns.set_context("poster") # %% [markdown] # We define a function allowing to make some nice decoration on the plot. # %% def make_plot_despine(ax): sns.despine(ax=ax, offset=10) ax.set_xlim([0, 3.5]) ax.set_ylim([0, 3.5]) ax.set_xticks(np.arange(0, 3.6, 0.5)) ax.set_yticks(np.arange(0, 3.6, 0.5)) ax.set_xlabel(r"$X_1$") ax.set_ylabel(r"$X_2$") ax.legend(loc="upper left", fontsize=16) # %% [markdown] # We can start by generating some data to later illustrate the principle of # each :class:`~imblearn.under_sampling.NearMiss` heuristic rules. # %% import numpy as np rng = np.random.RandomState(18) X_minority = np.transpose( [[1.1, 1.3, 1.15, 0.8, 0.8, 0.6, 0.55], [1.0, 1.5, 1.7, 2.5, 2.0, 1.2, 0.55]] ) X_majority = np.transpose( [ [2.1, 2.12, 2.13, 2.14, 2.2, 2.3, 2.5, 2.45], [1.5, 2.1, 2.7, 0.9, 1.0, 1.4, 2.4, 2.9], ] ) # %% [mardown] # NearMiss-1 # ---------- # # NearMiss-1 selects samples from the majority class for which the average # distance to some nearest neighbours is the smallest. In the following # example, we use a 3-NN to compute the average distance on 2 specific samples # of the majority class. Therefore, in this case the point linked by the # green-dashed line will be selected since the average distance is smaller. # %% import matplotlib.pyplot as plt from sklearn.neighbors import NearestNeighbors fig, ax = plt.subplots(figsize=(8, 8)) ax.scatter( X_minority[:, 0], X_minority[:, 1], label="Minority class", s=200, marker="_", ) ax.scatter( X_majority[:, 0], X_majority[:, 1], label="Majority class", s=200, marker="+", ) nearest_neighbors = NearestNeighbors(n_neighbors=3) nearest_neighbors.fit(X_minority) dist, ind = nearest_neighbors.kneighbors(X_majority[:2, :]) dist_avg = dist.sum(axis=1) / 3 for positive_idx, (neighbors, distance, color) in enumerate( zip(ind, dist_avg, ["g", "r"]) ): for make_plot, sample_idx in enumerate(neighbors): ax.plot( [X_majority[positive_idx, 0], X_minority[sample_idx, 0]], [X_majority[positive_idx, 1], X_minority[sample_idx, 1]], "--" + color, alpha=0.3, label=f"Avg. dist.={distance:.2f}" if make_plot == 0 else "", ) ax.set_title("NearMiss-1") make_plot_despine(ax) plt.tight_layout() # %% [mardown] # NearMiss-2 # ---------- # # NearMiss-2 selects samples from the majority class for which the average # distance to the farthest neighbors is the smallest. With the same # configuration as previously presented, the sample linked to the green-dashed # line will be selected since its distance the 3 farthest neighbors is the # smallest. # %% fig, ax = plt.subplots(figsize=(8, 8)) ax.scatter( X_minority[:, 0], X_minority[:, 1], label="Minority class", s=200, marker="_", ) ax.scatter( X_majority[:, 0], X_majority[:, 1], label="Majority class", s=200, marker="+", ) nearest_neighbors = NearestNeighbors(n_neighbors=X_minority.shape[0]) nearest_neighbors.fit(X_minority) dist, ind = nearest_neighbors.kneighbors(X_majority[:2, :]) dist = dist[:, -3::] ind = ind[:, -3::] dist_avg = dist.sum(axis=1) / 3 for positive_idx, (neighbors, distance, color) in enumerate( zip(ind, dist_avg, ["g", "r"]) ): for make_plot, sample_idx in enumerate(neighbors): ax.plot( [X_majority[positive_idx, 0], X_minority[sample_idx, 0]], [X_majority[positive_idx, 1], X_minority[sample_idx, 1]], "--" + color, alpha=0.3, label=f"Avg. dist.={distance:.2f}" if make_plot == 0 else "", ) ax.set_title("NearMiss-2") make_plot_despine(ax) plt.tight_layout() # %% [mardown] # NearMiss-3 # ---------- # # NearMiss-3 can be divided into 2 steps. First, a nearest-neighbors is used to # short-list samples from the majority class (i.e. correspond to the # highlighted samples in the following plot). Then, the sample with the largest # average distance to the *k* nearest-neighbors are selected. # %% fig, ax = plt.subplots(figsize=(8.5, 8.5)) ax.scatter( X_minority[:, 0], X_minority[:, 1], label="Minority class", s=200, marker="_", ) ax.scatter( X_majority[:, 0], X_majority[:, 1], label="Majority class", s=200, marker="+", ) nearest_neighbors = NearestNeighbors(n_neighbors=3) nearest_neighbors.fit(X_majority) # select only the majority point of interest selected_idx = nearest_neighbors.kneighbors(X_minority, return_distance=False) X_majority = X_majority[np.unique(selected_idx), :] ax.scatter( X_majority[:, 0], X_majority[:, 1], label="Short-listed samples", s=200, alpha=0.3, color="g", ) nearest_neighbors = NearestNeighbors(n_neighbors=3) nearest_neighbors.fit(X_minority) dist, ind = nearest_neighbors.kneighbors(X_majority[:2, :]) dist_avg = dist.sum(axis=1) / 3 for positive_idx, (neighbors, distance, color) in enumerate( zip(ind, dist_avg, ["r", "g"]) ): for make_plot, sample_idx in enumerate(neighbors): ax.plot( [X_majority[positive_idx, 0], X_minority[sample_idx, 0]], [X_majority[positive_idx, 1], X_minority[sample_idx, 1]], "--" + color, alpha=0.3, label=f"Avg. dist.={distance:.2f}" if make_plot == 0 else "", ) ax.set_title("NearMiss-3") make_plot_despine(ax) plt.tight_layout() plt.show() plot_illustration_tomek_links.py000066400000000000000000000061541521136113700344300ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/examples/under-sampling""" ============================================== Illustration of the definition of a Tomek link ============================================== This example illustrates what is a Tomek link. """ # Authors: Guillaume Lemaitre # License: MIT # %% print(__doc__) import matplotlib.pyplot as plt import seaborn as sns sns.set_context("poster") # %% [markdown] # This function allows to make nice plotting # %% def make_plot_despine(ax): sns.despine(ax=ax, offset=10) ax.set_xlim([0, 3]) ax.set_ylim([0, 3]) ax.set_xlabel(r"$X_1$") ax.set_ylabel(r"$X_2$") ax.legend(loc="lower right") # %% [markdown] # We will generate some toy data that illustrates how # :class:`~imblearn.under_sampling.TomekLinks` is used to clean a dataset. # %% import numpy as np rng = np.random.RandomState(18) X_minority = np.transpose( [[1.1, 1.3, 1.15, 0.8, 0.55, 2.1], [1.0, 1.5, 1.7, 2.5, 0.55, 1.9]] ) X_majority = np.transpose( [ [2.1, 2.12, 2.13, 2.14, 2.2, 2.3, 2.5, 2.45], [1.5, 2.1, 2.7, 0.9, 1.0, 1.4, 2.4, 2.9], ] ) # %% [markdown] # In the figure above, the samples highlighted in green form a Tomek link since # they are of different classes and are nearest neighbors of each other. fig, ax = plt.subplots(figsize=(8, 8)) ax.scatter( X_minority[:, 0], X_minority[:, 1], label="Minority class", s=200, marker="_", ) ax.scatter( X_majority[:, 0], X_majority[:, 1], label="Majority class", s=200, marker="+", ) # highlight the samples of interest ax.scatter( [X_minority[-1, 0], X_majority[1, 0]], [X_minority[-1, 1], X_majority[1, 1]], label="Tomek link", s=200, alpha=0.3, ) make_plot_despine(ax) fig.suptitle("Illustration of a Tomek link") fig.tight_layout() # %% [markdown] # We can run the :class:`~imblearn.under_sampling.TomekLinks` sampling to # remove the corresponding samples. If `sampling_strategy='auto'` only the # sample from the majority class will be removed. If `sampling_strategy='all'` # both samples will be removed. # %% from imblearn.under_sampling import TomekLinks fig, axs = plt.subplots(nrows=1, ncols=2, figsize=(16, 8)) samplers = { "Removing only majority samples": TomekLinks(sampling_strategy="auto"), "Removing all samples": TomekLinks(sampling_strategy="all"), } for ax, (title, sampler) in zip(axs, samplers.items()): X_res, y_res = sampler.fit_resample( np.vstack((X_minority, X_majority)), np.array([0] * X_minority.shape[0] + [1] * X_majority.shape[0]), ) ax.scatter( X_res[y_res == 0][:, 0], X_res[y_res == 0][:, 1], label="Minority class", s=200, marker="_", ) ax.scatter( X_res[y_res == 1][:, 0], X_res[y_res == 1][:, 1], label="Majority class", s=200, marker="+", ) # highlight the samples of interest ax.scatter( [X_minority[-1, 0], X_majority[1, 0]], [X_minority[-1, 1], X_majority[1, 1]], label="Tomek link", s=200, alpha=0.3, ) ax.set_title(title) make_plot_despine(ax) fig.tight_layout() plt.show() scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/000077500000000000000000000000001521136113700227675ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/VERSION.txt000066400000000000000000000000121521136113700246460ustar00rootroot000000000000000.15.dev0 scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/__init__.py000066400000000000000000000100041521136113700250730ustar00rootroot00000000000000"""Toolbox for imbalanced dataset in machine learning. ``imbalanced-learn`` is a set of python methods to deal with imbalanced datset in machine learning and pattern recognition. Subpackages ----------- combine Module which provides methods based on over-sampling and under-sampling. ensemble Module which provides methods generating an ensemble of under-sampled subsets. exceptions Module including custom warnings and error classes used across imbalanced-learn. keras Module which provides custom generator, layers for deep learning using keras. metrics Module which provides metrics to quantified the classification performance with imbalanced dataset. model_selection Module which provides methods to split the dataset into training and test sets. over_sampling Module which provides methods to over-sample a dataset. tensorflow Module which provides custom generator, layers for deep learning using tensorflow. under-sampling Module which provides methods to under-sample a dataset. utils Module including various utilities. pipeline Module which allowing to create pipeline with scikit-learn estimators. """ import importlib import sys import types try: # This variable is injected in the __builtins__ by the build # process. It is used to enable importing subpackages of sklearn when # the binaries are not built # mypy error: Cannot determine type of '__SKLEARN_SETUP__' __IMBLEARN_SETUP__ # type: ignore except NameError: __IMBLEARN_SETUP__ = False if __IMBLEARN_SETUP__: sys.stderr.write("Partial import of imblearn during the build process.\n") # We are not importing the rest of scikit-learn during the build # process, as it may not be compiled yet else: from . import ( combine, ensemble, exceptions, metrics, model_selection, over_sampling, pipeline, tensorflow, under_sampling, utils, ) from ._version import __version__ from .base import FunctionSampler from .utils._show_versions import show_versions # noqa: F401 # FIXME: When we get Python 3.7 as minimal version, we will need to switch to # the following solution: # https://snarky.ca/lazy-importing-in-python-3-7/ class LazyLoader(types.ModuleType): """Lazily import a module, mainly to avoid pulling in large dependencies. Adapted from TensorFlow: https://github.com/tensorflow/tensorflow/blob/master/tensorflow/ python/util/lazy_loader.py """ def __init__(self, local_name, parent_module_globals, name, warning=None): self._local_name = local_name self._parent_module_globals = parent_module_globals self._warning = warning super().__init__(name) def _load(self): """Load the module and insert it into the parent's globals.""" # Import the target module and insert it into the parent's namespace module = importlib.import_module(self.__name__) self._parent_module_globals[self._local_name] = module # Update this object's dict so that if someone keeps a reference to the # LazyLoader, lookups are efficient (__getattr__ is only called on # lookups that fail). self.__dict__.update(module.__dict__) return module def __getattr__(self, item): module = self._load() return getattr(module, item) def __dir__(self): module = self._load() return dir(module) # delay the import of keras since we are going to import either tensorflow # or keras keras = LazyLoader("keras", globals(), "imblearn.keras") __all__ = [ "combine", "ensemble", "exceptions", "keras", "metrics", "model_selection", "over_sampling", "tensorflow", "under_sampling", "utils", "pipeline", "FunctionSampler", "__version__", ] scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/_version.py000066400000000000000000000013171521136113700251670ustar00rootroot00000000000000""" ``imbalanced-learn`` is a set of python methods to deal with imbalanced datset in machine learning and pattern recognition. """ # Based on NiLearn package # License: simplified BSD # PEP0440 compatible formatted version, see: # https://www.python.org/dev/peps/pep-0440/ # # Generic release markers: # X.Y # X.Y.Z # For bugfix releases # # Admissible pre-release markers: # X.YaN # Alpha release # X.YbN # Beta release # X.YrcN # Release Candidate # X.Y # Final release # # Dev branch marker is: 'X.Y.dev' or 'X.Y.devN' where N is an integer. # 'X.Y.dev0' is the canonical version of 'X.Y.dev' from pathlib import Path with open(Path(__file__).parent / "VERSION.txt") as _fh: __version__ = _fh.read().strip() scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/base.py000066400000000000000000000324251521136113700242610ustar00rootroot00000000000000"""Base class for sampling""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT from abc import ABCMeta, abstractmethod import numpy as np from sklearn.base import BaseEstimator, OneToOneFeatureMixin from sklearn.preprocessing import label_binarize from sklearn.utils._metadata_requests import METHODS from sklearn.utils.multiclass import check_classification_targets from sklearn_compat.base import _fit_context from sklearn_compat.utils.validation import validate_data from imblearn.utils import check_sampling_strategy, check_target_type from imblearn.utils._tags import get_tags from imblearn.utils._validation import ArraysTransformer if "fit_predict" not in METHODS: METHODS.append("fit_predict") if "fit_transform" not in METHODS: METHODS.append("fit_transform") METHODS.append("fit_resample") try: from sklearn.utils._metadata_requests import SIMPLE_METHODS SIMPLE_METHODS.append("fit_resample") except ImportError: # in older versions of scikit-learn, only METHODS is used pass class SamplerMixin(metaclass=ABCMeta): """Mixin class for samplers with abstract method. Warning: This class should not be used directly. Use the derive classes instead. """ _estimator_type = "sampler" @_fit_context(prefer_skip_nested_validation=True) def fit(self, X, y, **params): """Check inputs and statistics of the sampler. You should use ``fit_resample`` in all cases. Parameters ---------- X : {array-like, dataframe, sparse matrix} of shape \ (n_samples, n_features) Data array. y : array-like of shape (n_samples,) Target array. **params : dict Extra parameters to use by the sampler. Returns ------- self : object Return the instance itself. """ X, y, _ = self._check_X_y(X, y) self.sampling_strategy_ = check_sampling_strategy( self.sampling_strategy, y, self._sampling_type ) return self @_fit_context(prefer_skip_nested_validation=True) def fit_resample(self, X, y, **params): """Resample the dataset. Parameters ---------- X : {array-like, dataframe, sparse matrix} of shape \ (n_samples, n_features) Matrix containing the data which have to be sampled. y : array-like of shape (n_samples,) Corresponding label for each sample in X. **params : dict Extra parameters to use by the sampler. Returns ------- X_resampled : {array-like, dataframe, sparse matrix} of shape \ (n_samples_new, n_features) The array containing the resampled data. y_resampled : array-like of shape (n_samples_new,) The corresponding label of `X_resampled`. """ check_classification_targets(y) arrays_transformer = ArraysTransformer(X, y) X, y, binarize_y = self._check_X_y(X, y) self.sampling_strategy_ = check_sampling_strategy( self.sampling_strategy, y, self._sampling_type ) output = self._fit_resample(X, y, **params) y_ = ( label_binarize(output[1], classes=np.unique(y)) if binarize_y else output[1] ) X_, y_ = arrays_transformer.transform(output[0], y_) return (X_, y_) if len(output) == 2 else (X_, y_, output[2]) @abstractmethod def _fit_resample(self, X, y, **params): """Base method defined in each sampler to defined the sampling strategy. Parameters ---------- X : {array-like, sparse matrix} of shape (n_samples, n_features) Matrix containing the data which have to be sampled. y : array-like of shape (n_samples,) Corresponding label for each sample in X. **params : dict Extra parameters to use by the sampler. Returns ------- X_resampled : {ndarray, sparse matrix} of shape \ (n_samples_new, n_features) The array containing the resampled data. y_resampled : ndarray of shape (n_samples_new,) The corresponding label of `X_resampled`. """ pass class BaseSampler(SamplerMixin, OneToOneFeatureMixin, BaseEstimator): """Base class for sampling algorithms. Warning: This class should not be used directly. Use the derive classes instead. """ def __init__(self, sampling_strategy="auto"): self.sampling_strategy = sampling_strategy def _check_X_y(self, X, y, accept_sparse=None): if accept_sparse is None: accept_sparse = ["csr", "csc"] y, binarize_y = check_target_type(y, indicate_one_vs_all=True) X, y = validate_data(self, X=X, y=y, reset=True, accept_sparse=accept_sparse) return X, y, binarize_y def fit(self, X, y, **params): """Check inputs and statistics of the sampler. You should use ``fit_resample`` in all cases. Parameters ---------- X : {array-like, dataframe, sparse matrix} of shape \ (n_samples, n_features) Data array. y : array-like of shape (n_samples,) Target array. Returns ------- self : object Return the instance itself. """ return super().fit(X, y, **params) def fit_resample(self, X, y, **params): """Resample the dataset. Parameters ---------- X : {array-like, dataframe, sparse matrix} of shape \ (n_samples, n_features) Matrix containing the data which have to be sampled. y : array-like of shape (n_samples,) Corresponding label for each sample in X. Returns ------- X_resampled : {array-like, dataframe, sparse matrix} of shape \ (n_samples_new, n_features) The array containing the resampled data. y_resampled : array-like of shape (n_samples_new,) The corresponding label of `X_resampled`. """ return super().fit_resample(X, y, **params) def _more_tags(self): return {"X_types": ["2darray", "sparse", "dataframe"]} def __sklearn_tags__(self): from sklearn_compat.utils._tags import TargetTags from imblearn.utils._tags import InputTags, SamplerTags, Tags tags = Tags( estimator_type="sampler", target_tags=TargetTags(required=True), transformer_tags=None, regressor_tags=None, classifier_tags=None, sampler_tags=SamplerTags(), ) tags.input_tags = InputTags() tags.input_tags.two_d_array = True tags.input_tags.sparse = True tags.input_tags.dataframe = True return tags def _identity(X, y): return X, y def is_sampler(estimator): """Return True if the given estimator is a sampler, False otherwise. Parameters ---------- estimator : object Estimator to test. Returns ------- is_sampler : bool True if estimator is a sampler, otherwise False. """ if hasattr(estimator, "_estimator_type") and estimator._estimator_type == "sampler": return True tags = get_tags(estimator) if hasattr(tags, "sampler_tags") and tags.sampler_tags is not None: return True return False class FunctionSampler(BaseSampler): """Construct a sampler from calling an arbitrary callable. Read more in the :ref:`User Guide `. Parameters ---------- func : callable, default=None The callable to use for the transformation. This will be passed the same arguments as transform, with args and kwargs forwarded. If func is None, then func will be the identity function. accept_sparse : bool, default=True Whether sparse input are supported. By default, sparse inputs are supported. kw_args : dict, default=None The keyword argument expected by ``func``. validate : bool, default=True Whether or not to bypass the validation of ``X`` and ``y``. Turning-off validation allows to use the ``FunctionSampler`` with any type of data. .. versionadded:: 0.6 Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- sklearn.preprocessing.FunctionTransfomer : Stateless transformer. Notes ----- See :ref:`sphx_glr_auto_examples_applications_plot_outlier_rejections.py` Examples -------- >>> import numpy as np >>> from sklearn.datasets import make_classification >>> from imblearn import FunctionSampler >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) We can create to select only the first ten samples for instance. >>> def func(X, y): ... return X[:10], y[:10] >>> sampler = FunctionSampler(func=func) >>> X_res, y_res = sampler.fit_resample(X, y) >>> np.all(X_res == X[:10]) True >>> np.all(y_res == y[:10]) True We can also create a specific function which take some arguments. >>> from collections import Counter >>> from imblearn.under_sampling import RandomUnderSampler >>> def func(X, y, sampling_strategy, random_state): ... return RandomUnderSampler( ... sampling_strategy=sampling_strategy, ... random_state=random_state).fit_resample(X, y) >>> sampler = FunctionSampler(func=func, ... kw_args={'sampling_strategy': 'auto', ... 'random_state': 0}) >>> X_res, y_res = sampler.fit_resample(X, y) >>> print(f'Resampled dataset shape {sorted(Counter(y_res).items())}') Resampled dataset shape [(0, 100), (1, 100)] """ _sampling_type = "bypass" _parameter_constraints: dict = { "func": [callable, None], "accept_sparse": ["boolean"], "kw_args": [dict, None], "validate": ["boolean"], } def __init__(self, *, func=None, accept_sparse=True, kw_args=None, validate=True): super().__init__() self.func = func self.accept_sparse = accept_sparse self.kw_args = kw_args self.validate = validate def fit(self, X, y): """Check inputs and statistics of the sampler. You should use ``fit_resample`` in all cases. Parameters ---------- X : {array-like, dataframe, sparse matrix} of shape \ (n_samples, n_features) Data array. y : array-like of shape (n_samples,) Target array. Returns ------- self : object Return the instance itself. """ self._validate_params() # we need to overwrite SamplerMixin.fit to bypass the validation if self.validate: check_classification_targets(y) X, y, _ = self._check_X_y(X, y, accept_sparse=self.accept_sparse) self.sampling_strategy_ = check_sampling_strategy( self.sampling_strategy, y, self._sampling_type ) return self def fit_resample(self, X, y): """Resample the dataset. Parameters ---------- X : {array-like, sparse matrix} of shape (n_samples, n_features) Matrix containing the data which have to be sampled. y : array-like of shape (n_samples,) Corresponding label for each sample in X. Returns ------- X_resampled : {array-like, sparse matrix} of shape \ (n_samples_new, n_features) The array containing the resampled data. y_resampled : array-like of shape (n_samples_new,) The corresponding label of `X_resampled`. """ self._validate_params() arrays_transformer = ArraysTransformer(X, y) if self.validate: check_classification_targets(y) X, y, binarize_y = self._check_X_y(X, y, accept_sparse=self.accept_sparse) self.sampling_strategy_ = check_sampling_strategy( self.sampling_strategy, y, self._sampling_type ) output = self._fit_resample(X, y) if self.validate: y_ = ( label_binarize(output[1], classes=np.unique(y)) if binarize_y else output[1] ) X_, y_ = arrays_transformer.transform(output[0], y_) return (X_, y_) if len(output) == 2 else (X_, y_, output[2]) return output def _fit_resample(self, X, y): func = _identity if self.func is None else self.func output = func(X, y, **(self.kw_args if self.kw_args else {})) return output scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/combine/000077500000000000000000000000001521136113700244035ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/combine/__init__.py000066400000000000000000000003611521136113700265140ustar00rootroot00000000000000"""The :mod:`imblearn.combine` provides methods which combine over-sampling and under-sampling. """ from imblearn.combine._smote_enn import SMOTEENN from imblearn.combine._smote_tomek import SMOTETomek __all__ = ["SMOTEENN", "SMOTETomek"] scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/combine/_smote_enn.py000066400000000000000000000117501521136113700271070ustar00rootroot00000000000000"""Class to perform over-sampling using SMOTE and cleaning using ENN.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numbers from sklearn.base import clone from sklearn.utils import check_X_y from imblearn.base import BaseSampler from imblearn.over_sampling import SMOTE from imblearn.over_sampling.base import BaseOverSampler from imblearn.under_sampling import EditedNearestNeighbours from imblearn.utils import Substitution, check_target_type from imblearn.utils._docstring import _n_jobs_docstring, _random_state_docstring @Substitution( sampling_strategy=BaseOverSampler._sampling_strategy_docstring, n_jobs=_n_jobs_docstring, random_state=_random_state_docstring, ) class SMOTEENN(BaseSampler): """Over-sampling using SMOTE and cleaning using ENN. Combine over- and under-sampling using SMOTE and Edited Nearest Neighbours. Read more in the :ref:`User Guide `. Parameters ---------- {sampling_strategy} {random_state} smote : sampler object, default=None The :class:`~imblearn.over_sampling.SMOTE` object to use. If not given, a :class:`~imblearn.over_sampling.SMOTE` object with default parameters will be given. enn : sampler object, default=None The :class:`~imblearn.under_sampling.EditedNearestNeighbours` object to use. If not given, a :class:`~imblearn.under_sampling.EditedNearestNeighbours` object with sampling strategy='all' will be given. {n_jobs} Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. smote_ : sampler object The validated :class:`~imblearn.over_sampling.SMOTE` instance. enn_ : sampler object The validated :class:`~imblearn.under_sampling.EditedNearestNeighbours` instance. n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- SMOTETomek : Over-sample using SMOTE followed by under-sampling removing the Tomek's links. Notes ----- The method is presented in [1]_. Supports multi-class resampling. Refer to SMOTE and ENN regarding the scheme which used. References ---------- .. [1] G. Batista, R. C. Prati, M. C. Monard. "A study of the behavior of several methods for balancing machine learning training data," ACM Sigkdd Explorations Newsletter 6 (1), 20-29, 2004. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from imblearn.combine import SMOTEENN >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> sme = SMOTEENN(random_state=42) >>> X_res, y_res = sme.fit_resample(X, y) >>> print('Resampled dataset shape %s' % Counter(y_res)) Resampled dataset shape Counter({{0: 900, 1: 881}}) """ _sampling_type = "over-sampling" _parameter_constraints: dict = { **BaseOverSampler._parameter_constraints, "smote": [SMOTE, None], "enn": [EditedNearestNeighbours, None], "n_jobs": [numbers.Integral, None], } def __init__( self, *, sampling_strategy="auto", random_state=None, smote=None, enn=None, n_jobs=None, ): super().__init__() self.sampling_strategy = sampling_strategy self.random_state = random_state self.smote = smote self.enn = enn self.n_jobs = n_jobs def _validate_estimator(self): "Private function to validate SMOTE and ENN objects" if self.smote is not None: self.smote_ = clone(self.smote) else: self.smote_ = SMOTE( sampling_strategy=self.sampling_strategy, random_state=self.random_state, ) if self.enn is not None: self.enn_ = clone(self.enn) else: self.enn_ = EditedNearestNeighbours( sampling_strategy="all", n_jobs=self.n_jobs ) def _fit_resample(self, X, y): self._validate_estimator() y = check_target_type(y) X, y = check_X_y(X, y, accept_sparse=["csr", "csc"]) self.sampling_strategy_ = self.sampling_strategy X_res, y_res = self.smote_.fit_resample(X, y) return self.enn_.fit_resample(X_res, y_res) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/combine/_smote_tomek.py000066400000000000000000000115321521136113700274440ustar00rootroot00000000000000"""Class to perform over-sampling using SMOTE and cleaning using Tomek links.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numbers from sklearn.base import clone from sklearn.utils import check_X_y from imblearn.base import BaseSampler from imblearn.over_sampling import SMOTE from imblearn.over_sampling.base import BaseOverSampler from imblearn.under_sampling import TomekLinks from imblearn.utils import Substitution, check_target_type from imblearn.utils._docstring import _n_jobs_docstring, _random_state_docstring @Substitution( sampling_strategy=BaseOverSampler._sampling_strategy_docstring, n_jobs=_n_jobs_docstring, random_state=_random_state_docstring, ) class SMOTETomek(BaseSampler): """Over-sampling using SMOTE and cleaning using Tomek links. Combine over- and under-sampling using SMOTE and Tomek links. Read more in the :ref:`User Guide `. Parameters ---------- {sampling_strategy} {random_state} smote : sampler object, default=None The :class:`~imblearn.over_sampling.SMOTE` object to use. If not given, a :class:`~imblearn.over_sampling.SMOTE` object with default parameters will be given. tomek : sampler object, default=None The :class:`~imblearn.under_sampling.TomekLinks` object to use. If not given, a :class:`~imblearn.under_sampling.TomekLinks` object with sampling strategy='all' will be given. {n_jobs} Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. smote_ : sampler object The validated :class:`~imblearn.over_sampling.SMOTE` instance. tomek_ : sampler object The validated :class:`~imblearn.under_sampling.TomekLinks` instance. n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- SMOTEENN : Over-sample using SMOTE followed by under-sampling using Edited Nearest Neighbours. Notes ----- The method is presented in [1]_. Supports multi-class resampling. Refer to SMOTE and TomekLinks regarding the scheme which used. References ---------- .. [1] G. Batista, B. Bazzan, M. Monard, "Balancing Training Data for Automated Annotation of Keywords: a Case Study," In WOB, 10-18, 2003. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from imblearn.combine import SMOTETomek >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> smt = SMOTETomek(random_state=42) >>> X_res, y_res = smt.fit_resample(X, y) >>> print('Resampled dataset shape %s' % Counter(y_res)) Resampled dataset shape Counter({{0: 900, 1: 900}}) """ _sampling_type = "over-sampling" _parameter_constraints: dict = { **BaseOverSampler._parameter_constraints, "smote": [SMOTE, None], "tomek": [TomekLinks, None], "n_jobs": [numbers.Integral, None], } def __init__( self, *, sampling_strategy="auto", random_state=None, smote=None, tomek=None, n_jobs=None, ): super().__init__() self.sampling_strategy = sampling_strategy self.random_state = random_state self.smote = smote self.tomek = tomek self.n_jobs = n_jobs def _validate_estimator(self): "Private function to validate SMOTE and ENN objects" if self.smote is not None: self.smote_ = clone(self.smote) else: self.smote_ = SMOTE( sampling_strategy=self.sampling_strategy, random_state=self.random_state, ) if self.tomek is not None: self.tomek_ = clone(self.tomek) else: self.tomek_ = TomekLinks(sampling_strategy="all", n_jobs=self.n_jobs) def _fit_resample(self, X, y): self._validate_estimator() y = check_target_type(y) X, y = check_X_y(X, y, accept_sparse=["csr", "csc"]) self.sampling_strategy_ = self.sampling_strategy X_res, y_res = self.smote_.fit_resample(X, y) return self.tomek_.fit_resample(X_res, y_res) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/combine/tests/000077500000000000000000000000001521136113700255455ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/combine/tests/__init__.py000066400000000000000000000000001521136113700276440ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/combine/tests/test_smote_enn.py000066400000000000000000000111711521136113700311460ustar00rootroot00000000000000"""Test the module SMOTE ENN.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numpy as np from sklearn.utils._testing import assert_allclose, assert_array_equal from imblearn.combine import SMOTEENN from imblearn.over_sampling import SMOTE from imblearn.under_sampling import EditedNearestNeighbours RND_SEED = 0 X = np.array( [ [0.11622591, -0.0317206], [0.77481731, 0.60935141], [1.25192108, -0.22367336], [0.53366841, -0.30312976], [1.52091956, -0.49283504], [-0.28162401, -2.10400981], [0.83680821, 1.72827342], [0.3084254, 0.33299982], [0.70472253, -0.73309052], [0.28893132, -0.38761769], [1.15514042, 0.0129463], [0.88407872, 0.35454207], [1.31301027, -0.92648734], [-1.11515198, -0.93689695], [-0.18410027, -0.45194484], [0.9281014, 0.53085498], [-0.14374509, 0.27370049], [-0.41635887, -0.38299653], [0.08711622, 0.93259929], [1.70580611, -0.11219234], ] ) Y = np.array([0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0]) R_TOL = 1e-4 def test_sample_regular(): smote = SMOTEENN(random_state=RND_SEED) X_resampled, y_resampled = smote.fit_resample(X, Y) X_gt = np.array( [ [1.52091956, -0.49283504], [0.84976473, -0.15570176], [0.61319159, -0.11571667], [0.66052536, -0.28246518], [-0.28162401, -2.10400981], [0.83680821, 1.72827342], [0.08711622, 0.93259929], ] ) y_gt = np.array([0, 0, 0, 0, 1, 1, 1]) assert_allclose(X_resampled, X_gt, rtol=R_TOL) assert_array_equal(y_resampled, y_gt) def test_sample_regular_pass_smote_enn(): smote = SMOTEENN( smote=SMOTE(sampling_strategy="auto", random_state=RND_SEED), enn=EditedNearestNeighbours(sampling_strategy="all"), random_state=RND_SEED, ) X_resampled, y_resampled = smote.fit_resample(X, Y) X_gt = np.array( [ [1.52091956, -0.49283504], [0.84976473, -0.15570176], [0.61319159, -0.11571667], [0.66052536, -0.28246518], [-0.28162401, -2.10400981], [0.83680821, 1.72827342], [0.08711622, 0.93259929], ] ) y_gt = np.array([0, 0, 0, 0, 1, 1, 1]) assert_allclose(X_resampled, X_gt, rtol=R_TOL) assert_array_equal(y_resampled, y_gt) def test_sample_regular_half(): sampling_strategy = {0: 10, 1: 12} smote = SMOTEENN(sampling_strategy=sampling_strategy, random_state=RND_SEED) X_resampled, y_resampled = smote.fit_resample(X, Y) X_gt = np.array( [ [1.52091956, -0.49283504], [-0.28162401, -2.10400981], [0.83680821, 1.72827342], [0.08711622, 0.93259929], ] ) y_gt = np.array([0, 1, 1, 1]) assert_allclose(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) def test_validate_estimator_init(): smote = SMOTE(random_state=RND_SEED) enn = EditedNearestNeighbours(sampling_strategy="all") smt = SMOTEENN(smote=smote, enn=enn, random_state=RND_SEED) X_resampled, y_resampled = smt.fit_resample(X, Y) X_gt = np.array( [ [1.52091956, -0.49283504], [0.84976473, -0.15570176], [0.61319159, -0.11571667], [0.66052536, -0.28246518], [-0.28162401, -2.10400981], [0.83680821, 1.72827342], [0.08711622, 0.93259929], ] ) y_gt = np.array([0, 0, 0, 0, 1, 1, 1]) assert_allclose(X_resampled, X_gt, rtol=R_TOL) assert_array_equal(y_resampled, y_gt) def test_validate_estimator_default(): smt = SMOTEENN(random_state=RND_SEED) X_resampled, y_resampled = smt.fit_resample(X, Y) X_gt = np.array( [ [1.52091956, -0.49283504], [0.84976473, -0.15570176], [0.61319159, -0.11571667], [0.66052536, -0.28246518], [-0.28162401, -2.10400981], [0.83680821, 1.72827342], [0.08711622, 0.93259929], ] ) y_gt = np.array([0, 0, 0, 0, 1, 1, 1]) assert_allclose(X_resampled, X_gt, rtol=R_TOL) assert_array_equal(y_resampled, y_gt) def test_parallelisation(): # Check if default job count is none smt = SMOTEENN(random_state=RND_SEED) smt._validate_estimator() assert smt.n_jobs is None assert smt.enn_.n_jobs is None # Check if job count is set smt = SMOTEENN(random_state=RND_SEED, n_jobs=8) smt._validate_estimator() assert smt.n_jobs == 8 assert smt.enn_.n_jobs == 8 scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/combine/tests/test_smote_tomek.py000066400000000000000000000126011521136113700315040ustar00rootroot00000000000000"""Test the module SMOTE ENN.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numpy as np from sklearn.utils._testing import assert_allclose, assert_array_equal from imblearn.combine import SMOTETomek from imblearn.over_sampling import SMOTE from imblearn.under_sampling import TomekLinks RND_SEED = 0 X = np.array( [ [0.20622591, 0.0582794], [0.68481731, 0.51935141], [1.34192108, -0.13367336], [0.62366841, -0.21312976], [1.61091956, -0.40283504], [-0.37162401, -2.19400981], [0.74680821, 1.63827342], [0.2184254, 0.24299982], [0.61472253, -0.82309052], [0.19893132, -0.47761769], [1.06514042, -0.0770537], [0.97407872, 0.44454207], [1.40301027, -0.83648734], [-1.20515198, -1.02689695], [-0.27410027, -0.54194484], [0.8381014, 0.44085498], [-0.23374509, 0.18370049], [-0.32635887, -0.29299653], [-0.00288378, 0.84259929], [1.79580611, -0.02219234], ] ) Y = np.array([0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0]) R_TOL = 1e-4 def test_sample_regular(): smote = SMOTETomek(random_state=RND_SEED) X_resampled, y_resampled = smote.fit_resample(X, Y) X_gt = np.array( [ [0.68481731, 0.51935141], [1.34192108, -0.13367336], [0.62366841, -0.21312976], [1.61091956, -0.40283504], [-0.37162401, -2.19400981], [0.74680821, 1.63827342], [0.61472253, -0.82309052], [0.19893132, -0.47761769], [1.40301027, -0.83648734], [-1.20515198, -1.02689695], [-0.23374509, 0.18370049], [-0.00288378, 0.84259929], [1.79580611, -0.02219234], [0.38307743, -0.05670439], [0.70319159, -0.02571667], [0.75052536, -0.19246518], ] ) y_gt = np.array([1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0]) assert_allclose(X_resampled, X_gt, rtol=R_TOL) assert_array_equal(y_resampled, y_gt) def test_sample_regular_half(): sampling_strategy = {0: 9, 1: 12} smote = SMOTETomek(sampling_strategy=sampling_strategy, random_state=RND_SEED) X_resampled, y_resampled = smote.fit_resample(X, Y) X_gt = np.array( [ [0.68481731, 0.51935141], [0.62366841, -0.21312976], [1.61091956, -0.40283504], [-0.37162401, -2.19400981], [0.74680821, 1.63827342], [0.61472253, -0.82309052], [0.19893132, -0.47761769], [1.40301027, -0.83648734], [-1.20515198, -1.02689695], [-0.23374509, 0.18370049], [-0.00288378, 0.84259929], [1.79580611, -0.02219234], [0.45784496, -0.1053161], ] ) y_gt = np.array([1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0]) assert_allclose(X_resampled, X_gt, rtol=R_TOL) assert_array_equal(y_resampled, y_gt) def test_validate_estimator_init(): smote = SMOTE(random_state=RND_SEED) tomek = TomekLinks(sampling_strategy="all") smt = SMOTETomek(smote=smote, tomek=tomek, random_state=RND_SEED) X_resampled, y_resampled = smt.fit_resample(X, Y) X_gt = np.array( [ [0.68481731, 0.51935141], [1.34192108, -0.13367336], [0.62366841, -0.21312976], [1.61091956, -0.40283504], [-0.37162401, -2.19400981], [0.74680821, 1.63827342], [0.61472253, -0.82309052], [0.19893132, -0.47761769], [1.40301027, -0.83648734], [-1.20515198, -1.02689695], [-0.23374509, 0.18370049], [-0.00288378, 0.84259929], [1.79580611, -0.02219234], [0.38307743, -0.05670439], [0.70319159, -0.02571667], [0.75052536, -0.19246518], ] ) y_gt = np.array([1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0]) assert_allclose(X_resampled, X_gt, rtol=R_TOL) assert_array_equal(y_resampled, y_gt) def test_validate_estimator_default(): smt = SMOTETomek(random_state=RND_SEED) X_resampled, y_resampled = smt.fit_resample(X, Y) X_gt = np.array( [ [0.68481731, 0.51935141], [1.34192108, -0.13367336], [0.62366841, -0.21312976], [1.61091956, -0.40283504], [-0.37162401, -2.19400981], [0.74680821, 1.63827342], [0.61472253, -0.82309052], [0.19893132, -0.47761769], [1.40301027, -0.83648734], [-1.20515198, -1.02689695], [-0.23374509, 0.18370049], [-0.00288378, 0.84259929], [1.79580611, -0.02219234], [0.38307743, -0.05670439], [0.70319159, -0.02571667], [0.75052536, -0.19246518], ] ) y_gt = np.array([1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0]) assert_allclose(X_resampled, X_gt, rtol=R_TOL) assert_array_equal(y_resampled, y_gt) def test_parallelisation(): # Check if default job count is None smt = SMOTETomek(random_state=RND_SEED) smt._validate_estimator() assert smt.n_jobs is None assert smt.tomek_.n_jobs is None # Check if job count is set smt = SMOTETomek(random_state=RND_SEED, n_jobs=8) smt._validate_estimator() assert smt.n_jobs == 8 assert smt.tomek_.n_jobs == 8 scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/datasets/000077500000000000000000000000001521136113700245775ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/datasets/__init__.py000066400000000000000000000003611521136113700267100ustar00rootroot00000000000000""" The :mod:`imblearn.datasets` provides methods to generate imbalanced data. """ from imblearn.datasets._imbalance import make_imbalance from imblearn.datasets._zenodo import fetch_datasets __all__ = ["make_imbalance", "fetch_datasets"] scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/datasets/_imbalance.py000066400000000000000000000100641521136113700272240ustar00rootroot00000000000000"""Transform a dataset into an imbalanced dataset.""" # Authors: Dayvid Oliveira # Guillaume Lemaitre # Christos Aridas # License: MIT from collections import Counter from collections.abc import Mapping from sklearn_compat.utils._param_validation import validate_params from imblearn.under_sampling import RandomUnderSampler from imblearn.utils import check_sampling_strategy @validate_params( { "X": ["array-like"], "y": ["array-like"], "sampling_strategy": [Mapping, callable, None], "random_state": ["random_state"], "verbose": ["boolean"], }, prefer_skip_nested_validation=True, ) def make_imbalance( X, y, *, sampling_strategy=None, random_state=None, verbose=False, **kwargs ): """Turn a dataset into an imbalanced dataset with a specific sampling strategy. A simple toy dataset to visualize clustering and classification algorithms. Read more in the :ref:`User Guide `. Parameters ---------- X : {array-like, dataframe} of shape (n_samples, n_features) Matrix containing the data to be imbalanced. y : array-like of shape (n_samples,) Corresponding label for each sample in X. sampling_strategy : dict or callable, Ratio to use for resampling the data set. - When ``dict``, the keys correspond to the targeted classes. The values correspond to the desired number of samples for each targeted class. - When callable, function taking ``y`` and returns a ``dict``. The keys correspond to the targeted classes. The values correspond to the desired number of samples for each class. random_state : int, RandomState instance or None, default=None If int, random_state is the seed used by the random number generator; If RandomState instance, random_state is the random number generator; If None, the random number generator is the RandomState instance used by np.random. verbose : bool, default=False Show information regarding the sampling. **kwargs : dict Dictionary of additional keyword arguments to pass to ``sampling_strategy``. Returns ------- X_resampled : {ndarray, dataframe} of shape (n_samples_new, n_features) The array containing the imbalanced data. y_resampled : ndarray of shape (n_samples_new) The corresponding label of `X_resampled`. Notes ----- See :ref:`sphx_glr_auto_examples_applications_plot_multi_class_under_sampling.py`, :ref:`sphx_glr_auto_examples_datasets_plot_make_imbalance.py`, and :ref:`sphx_glr_auto_examples_api_plot_sampling_strategy_usage.py`. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import load_iris >>> from imblearn.datasets import make_imbalance >>> data = load_iris() >>> X, y = data.data, data.target >>> print(f'Distribution before imbalancing: {Counter(y)}') Distribution before imbalancing: Counter({0: 50, 1: 50, 2: 50}) >>> X_res, y_res = make_imbalance(X, y, ... sampling_strategy={0: 10, 1: 20, 2: 30}, ... random_state=42) >>> print(f'Distribution after imbalancing: {Counter(y_res)}') Distribution after imbalancing: Counter({2: 30, 1: 20, 0: 10}) """ target_stats = Counter(y) # restrict ratio to be a dict or a callable if isinstance(sampling_strategy, Mapping) or callable(sampling_strategy): sampling_strategy_ = check_sampling_strategy( sampling_strategy, y, "under-sampling", **kwargs ) if verbose: print(f"The original target distribution in the dataset is: {target_stats}") rus = RandomUnderSampler( sampling_strategy=sampling_strategy_, replacement=False, random_state=random_state, ) X_resampled, y_resampled = rus.fit_resample(X, y) if verbose: print(f"Make the dataset imbalanced: {Counter(y_resampled)}") return X_resampled, y_resampled scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/datasets/_zenodo.py000066400000000000000000000315701521136113700266140ustar00rootroot00000000000000"""Collection of imbalanced datasets. This collection of datasets has been proposed in [1]_. The characteristics of the available datasets are presented in the table below. ID Name Repository & Target Ratio #S #F 1 ecoli UCI, target: imU 8.6:1 336 7 2 optical_digits UCI, target: 8 9.1:1 5,620 64 3 satimage UCI, target: 4 9.3:1 6,435 36 4 pen_digits UCI, target: 5 9.4:1 10,992 16 5 abalone UCI, target: 7 9.7:1 4,177 10 6 sick_euthyroid UCI, target: sick euthyroid 9.8:1 3,163 42 7 spectrometer UCI, target: >=44 11:1 531 93 8 car_eval_34 UCI, target: good, v good 12:1 1,728 21 9 isolet UCI, target: A, B 12:1 7,797 617 10 us_crime UCI, target: >0.65 12:1 1,994 100 11 yeast_ml8 LIBSVM, target: 8 13:1 2,417 103 12 scene LIBSVM, target: >one label 13:1 2,407 294 13 libras_move UCI, target: 1 14:1 360 90 14 thyroid_sick UCI, target: sick 15:1 3,772 52 15 coil_2000 KDD, CoIL, target: minority 16:1 9,822 85 16 arrhythmia UCI, target: 06 17:1 452 278 17 solar_flare_m0 UCI, target: M->0 19:1 1,389 32 18 oil UCI, target: minority 22:1 937 49 19 car_eval_4 UCI, target: vgood 26:1 1,728 21 20 wine_quality UCI, wine, target: <=4 26:1 4,898 11 21 letter_img UCI, target: Z 26:1 20,000 16 22 yeast_me2 UCI, target: ME2 28:1 1,484 8 23 webpage LIBSVM, w7a, target: minority 33:1 34,780 300 24 ozone_level UCI, ozone, data 34:1 2,536 72 25 mammography UCI, target: minority 42:1 11,183 6 26 protein_homo KDD CUP 2004, minority 111:1 145,751 74 27 abalone_19 UCI, target: 19 130:1 4,177 10 References ---------- .. [1] Ding, Zejin, "Diversified Ensemble Classifiers for Highly Imbalanced Data Learning and their Application in Bioinformatics." Dissertation, Georgia State University, (2011). """ # Author: Guillaume Lemaitre # License: BSD 3 clause import tarfile from collections import OrderedDict from inspect import signature from io import BytesIO from os import makedirs from os.path import isfile, join from urllib.request import urlopen import numpy as np from sklearn.datasets import get_data_home from sklearn.utils import Bunch, check_random_state from sklearn_compat.utils._param_validation import validate_params URL = "https://zenodo.org/record/61452/files/benchmark-imbalanced-learn.tar.gz" PRE_FILENAME = "x" POST_FILENAME = "data.npz" MAP_NAME_ID_KEYS = [ "ecoli", "optical_digits", "satimage", "pen_digits", "abalone", "sick_euthyroid", "spectrometer", "car_eval_34", "isolet", "us_crime", "yeast_ml8", "scene", "libras_move", "thyroid_sick", "coil_2000", "arrhythmia", "solar_flare_m0", "oil", "car_eval_4", "wine_quality", "letter_img", "yeast_me2", "webpage", "ozone_level", "mammography", "protein_homo", "abalone_19", ] MAP_NAME_ID = OrderedDict() MAP_ID_NAME = OrderedDict() for v, k in enumerate(MAP_NAME_ID_KEYS): MAP_NAME_ID[k] = v + 1 MAP_ID_NAME[v + 1] = k @validate_params( { "data_home": [None, str], "filter_data": [None, tuple], "download_if_missing": ["boolean"], "random_state": ["random_state"], "shuffle": ["boolean"], "verbose": ["boolean"], }, prefer_skip_nested_validation=True, ) def fetch_datasets( *, data_home=None, filter_data=None, download_if_missing=True, random_state=None, shuffle=False, verbose=False, ): """Load the benchmark datasets from Zenodo, downloading it if necessary. .. versionadded:: 0.3 Parameters ---------- data_home : str, default=None Specify another download and cache folder for the datasets. By default all scikit-learn data is stored in '~/scikit_learn_data' subfolders. filter_data : tuple of str/int, default=None A tuple containing the ID or the name of the datasets to be returned. Refer to the above table to get the ID and name of the datasets. download_if_missing : bool, default=True If False, raise a IOError if the data is not locally available instead of trying to download the data from the source site. random_state : int, RandomState instance or None, default=None Random state for shuffling the dataset. If int, random_state is the seed used by the random number generator; If RandomState instance, random_state is the random number generator; If None, the random number generator is the RandomState instance used by `np.random`. shuffle : bool, default=False Whether to shuffle dataset. verbose : bool, default=False Show information regarding the fetching. Returns ------- datasets : OrderedDict of Bunch object, The ordered is defined by ``filter_data``. Each Bunch object --- referred as dataset --- have the following attributes: dataset.data : ndarray of shape (n_samples, n_features) dataset.target : ndarray of shape (n_samples,) dataset.DESCR : str Description of the each dataset. Notes ----- This collection of datasets have been proposed in [1]_. The characteristics of the available datasets are presented in the table below. +--+--------------+-------------------------------+-------+---------+-----+ |ID|Name | Repository & Target | Ratio | #S | #F | +==+==============+===============================+=======+=========+=====+ |1 |ecoli | UCI, target: imU | 8.6:1 | 336 | 7 | +--+--------------+-------------------------------+-------+---------+-----+ |2 |optical_digits| UCI, target: 8 | 9.1:1 | 5,620 | 64 | +--+--------------+-------------------------------+-------+---------+-----+ |3 |satimage | UCI, target: 4 | 9.3:1 | 6,435 | 36 | +--+--------------+-------------------------------+-------+---------+-----+ |4 |pen_digits | UCI, target: 5 | 9.4:1 | 10,992 | 16 | +--+--------------+-------------------------------+-------+---------+-----+ |5 |abalone | UCI, target: 7 | 9.7:1 | 4,177 | 10 | +--+--------------+-------------------------------+-------+---------+-----+ |6 |sick_euthyroid| UCI, target: sick euthyroid | 9.8:1 | 3,163 | 42 | +--+--------------+-------------------------------+-------+---------+-----+ |7 |spectrometer | UCI, target: >=44 | 11:1 | 531 | 93 | +--+--------------+-------------------------------+-------+---------+-----+ |8 |car_eval_34 | UCI, target: good, v good | 12:1 | 1,728 | 21 | +--+--------------+-------------------------------+-------+---------+-----+ |9 |isolet | UCI, target: A, B | 12:1 | 7,797 | 617 | +--+--------------+-------------------------------+-------+---------+-----+ |10|us_crime | UCI, target: >0.65 | 12:1 | 1,994 | 100 | +--+--------------+-------------------------------+-------+---------+-----+ |11|yeast_ml8 | LIBSVM, target: 8 | 13:1 | 2,417 | 103 | +--+--------------+-------------------------------+-------+---------+-----+ |12|scene | LIBSVM, target: >one label | 13:1 | 2,407 | 294 | +--+--------------+-------------------------------+-------+---------+-----+ |13|libras_move | UCI, target: 1 | 14:1 | 360 | 90 | +--+--------------+-------------------------------+-------+---------+-----+ |14|thyroid_sick | UCI, target: sick | 15:1 | 3,772 | 52 | +--+--------------+-------------------------------+-------+---------+-----+ |15|coil_2000 | KDD, CoIL, target: minority | 16:1 | 9,822 | 85 | +--+--------------+-------------------------------+-------+---------+-----+ |16|arrhythmia | UCI, target: 06 | 17:1 | 452 | 278 | +--+--------------+-------------------------------+-------+---------+-----+ |17|solar_flare_m0| UCI, target: M->0 | 19:1 | 1,389 | 32 | +--+--------------+-------------------------------+-------+---------+-----+ |18|oil | UCI, target: minority | 22:1 | 937 | 49 | +--+--------------+-------------------------------+-------+---------+-----+ |19|car_eval_4 | UCI, target: vgood | 26:1 | 1,728 | 21 | +--+--------------+-------------------------------+-------+---------+-----+ |20|wine_quality | UCI, wine, target: <=4 | 26:1 | 4,898 | 11 | +--+--------------+-------------------------------+-------+---------+-----+ |21|letter_img | UCI, target: Z | 26:1 | 20,000 | 16 | +--+--------------+-------------------------------+-------+---------+-----+ |22|yeast_me2 | UCI, target: ME2 | 28:1 | 1,484 | 8 | +--+--------------+-------------------------------+-------+---------+-----+ |23|webpage | LIBSVM, w7a, target: minority | 33:1 | 34,780 | 300 | +--+--------------+-------------------------------+-------+---------+-----+ |24|ozone_level | UCI, ozone, data | 34:1 | 2,536 | 72 | +--+--------------+-------------------------------+-------+---------+-----+ |25|mammography | UCI, target: minority | 42:1 | 11,183 | 6 | +--+--------------+-------------------------------+-------+---------+-----+ |26|protein_homo | KDD CUP 2004, minority | 111:1 | 145,751 | 74 | +--+--------------+-------------------------------+-------+---------+-----+ |27|abalone_19 | UCI, target: 19 | 130:1 | 4,177 | 10 | +--+--------------+-------------------------------+-------+---------+-----+ References ---------- .. [1] Ding, Zejin, "Diversified Ensemble Classifiers for Highly Imbalanced Data Learning and their Application in Bioinformatics." Dissertation, Georgia State University, (2011). """ data_home = get_data_home(data_home=data_home) zenodo_dir = join(data_home, "zenodo") datasets = OrderedDict() if filter_data is None: filter_data_ = MAP_NAME_ID.keys() else: list_data = MAP_NAME_ID.keys() filter_data_ = [] for it in filter_data: if isinstance(it, str): if it not in list_data: raise ValueError( f"{it} is not a dataset available. " f"The available datasets are {list_data}" ) else: filter_data_.append(it) elif isinstance(it, int): if it < 1 or it > 27: raise ValueError( f"The dataset with the ID={it} is not an " "available dataset. The IDs are " f"{range(1, 28)}" ) else: # The index start at one, then we need to remove one # to not have issue with the indexing. filter_data_.append(MAP_ID_NAME[it]) else: raise ValueError( "The value in the tuple should be str or int." f" Got {type(it)} instead." ) # go through the list and check if the data are available for it in filter_data_: filename = PRE_FILENAME + str(MAP_NAME_ID[it]) + POST_FILENAME filename = join(zenodo_dir, filename) available = isfile(filename) if download_if_missing and not available: makedirs(zenodo_dir, exist_ok=True) if verbose: print(f"Downloading {URL}") f = BytesIO(urlopen(URL).read()) tar = tarfile.open(fileobj=f) if "filter" in signature(tar.extractall).parameters: tar.extractall(path=zenodo_dir, filter="data") else: # Python < 3.12 tar.extractall(path=zenodo_dir) elif not download_if_missing and not available: raise OSError("Data not found and `download_if_missing` is False") data = np.load(filename) X, y = data["data"], data["label"] if shuffle: ind = np.arange(X.shape[0]) rng = check_random_state(random_state) rng.shuffle(ind) X = X[ind] y = y[ind] datasets[it] = Bunch(data=X, target=y, DESCR=it) return datasets scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/datasets/tests/000077500000000000000000000000001521136113700257415ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/datasets/tests/__init__.py000066400000000000000000000000001521136113700300400ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/datasets/tests/test_imbalance.py000066400000000000000000000047261521136113700312760ustar00rootroot00000000000000"""Test the module easy ensemble.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT from collections import Counter import numpy as np import pytest from sklearn.datasets import load_iris from imblearn.datasets import make_imbalance @pytest.fixture def iris(): return load_iris(return_X_y=True) @pytest.mark.parametrize( "sampling_strategy, err_msg", [ ({0: -100, 1: 50, 2: 50}, "in a class cannot be negative"), ({0: 10, 1: 70}, "should be less or equal to the original"), ], ) def test_make_imbalance_error(iris, sampling_strategy, err_msg): # we are reusing part of utils.check_sampling_strategy, however this is not # cover in the common tests so we will repeat it here X, y = iris with pytest.raises(ValueError, match=err_msg): make_imbalance(X, y, sampling_strategy=sampling_strategy) def test_make_imbalance_error_single_class(iris): X, y = iris y = np.zeros_like(y) with pytest.raises(ValueError, match="needs to have more than 1 class."): make_imbalance(X, y, sampling_strategy={0: 10}) @pytest.mark.parametrize( "sampling_strategy, expected_counts", [ ({0: 10, 1: 20, 2: 30}, {0: 10, 1: 20, 2: 30}), ({0: 10, 1: 20}, {0: 10, 1: 20, 2: 50}), ], ) def test_make_imbalance_dict(iris, sampling_strategy, expected_counts): X, y = iris _, y_ = make_imbalance(X, y, sampling_strategy=sampling_strategy) assert Counter(y_) == expected_counts @pytest.mark.parametrize("as_frame", [True, False], ids=["dataframe", "array"]) @pytest.mark.parametrize( "sampling_strategy, expected_counts", [ ( {"setosa": 10, "versicolor": 20, "virginica": 30}, {"setosa": 10, "versicolor": 20, "virginica": 30}, ), ( {"setosa": 10, "versicolor": 20}, {"setosa": 10, "versicolor": 20, "virginica": 50}, ), ], ) def test_make_imbalanced_iris(as_frame, sampling_strategy, expected_counts): pd = pytest.importorskip("pandas") iris = load_iris(as_frame=as_frame) X, y = iris.data, iris.target y = iris.target_names[iris.target] if as_frame: y = pd.Series(iris.target_names[iris.target], name="target") X_res, y_res = make_imbalance(X, y, sampling_strategy=sampling_strategy) if as_frame: assert hasattr(X_res, "loc") pd.testing.assert_index_equal(X_res.index, y_res.index) assert Counter(y_res) == expected_counts scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/datasets/tests/test_zenodo.py000066400000000000000000000053251521136113700306550ustar00rootroot00000000000000"""Test the datasets loader. Skipped if datasets is not already downloaded to data_home. """ # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import pytest from sklearn.utils._testing import SkipTest from imblearn.datasets import fetch_datasets DATASET_SHAPE = { "ecoli": (336, 7), "optical_digits": (5620, 64), "satimage": (6435, 36), "pen_digits": (10992, 16), "abalone": (4177, 10), "sick_euthyroid": (3163, 42), "spectrometer": (531, 93), "car_eval_34": (1728, 21), "isolet": (7797, 617), "us_crime": (1994, 100), "yeast_ml8": (2417, 103), "scene": (2407, 294), "libras_move": (360, 90), "thyroid_sick": (3772, 52), "coil_2000": (9822, 85), "arrhythmia": (452, 278), "solar_flare_m0": (1389, 32), "oil": (937, 49), "car_eval_4": (1728, 21), "wine_quality": (4898, 11), "letter_img": (20000, 16), "yeast_me2": (1484, 8), "webpage": (34780, 300), "ozone_level": (2536, 72), "mammography": (11183, 6), "protein_homo": (145751, 74), "abalone_19": (4177, 10), } def fetch(*args, **kwargs): return fetch_datasets(*args, download_if_missing=True, **kwargs) @pytest.mark.xfail def test_fetch(): try: datasets1 = fetch(shuffle=True, random_state=42) except OSError: raise SkipTest("Zenodo dataset can not be loaded.") datasets2 = fetch(shuffle=True, random_state=37) for k in DATASET_SHAPE.keys(): X1, X2 = datasets1[k].data, datasets2[k].data assert DATASET_SHAPE[k] == X1.shape assert X1.shape == X2.shape y1, y2 = datasets1[k].target, datasets2[k].target assert (X1.shape[0],) == y1.shape assert (X1.shape[0],) == y2.shape def test_fetch_filter(): try: datasets1 = fetch(filter_data=tuple([1]), shuffle=True, random_state=42) except OSError: raise SkipTest("Zenodo dataset can not be loaded.") datasets2 = fetch(filter_data=tuple(["ecoli"]), shuffle=True, random_state=37) X1, X2 = datasets1["ecoli"].data, datasets2["ecoli"].data assert DATASET_SHAPE["ecoli"] == X1.shape assert X1.shape == X2.shape assert X1.sum() == pytest.approx(X2.sum()) y1, y2 = datasets1["ecoli"].target, datasets2["ecoli"].target assert (X1.shape[0],) == y1.shape assert (X1.shape[0],) == y2.shape @pytest.mark.parametrize( "filter_data, err_msg", [ (("rnf",), "is not a dataset available"), ((-1,), "dataset with the ID="), ((100,), "dataset with the ID="), ((1.00,), "value in the tuple"), ], ) def test_fetch_error(filter_data, err_msg): with pytest.raises(ValueError, match=err_msg): fetch_datasets(filter_data=filter_data) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/ensemble/000077500000000000000000000000001521136113700245615ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/ensemble/__init__.py000066400000000000000000000010251521136113700266700ustar00rootroot00000000000000""" The :mod:`imblearn.ensemble` module include methods generating under-sampled subsets combined inside an ensemble. """ from imblearn.ensemble._bagging import BalancedBaggingClassifier from imblearn.ensemble._easy_ensemble import EasyEnsembleClassifier from imblearn.ensemble._forest import BalancedRandomForestClassifier from imblearn.ensemble._weight_boosting import RUSBoostClassifier __all__ = [ "BalancedBaggingClassifier", "BalancedRandomForestClassifier", "EasyEnsembleClassifier", "RUSBoostClassifier", ] scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/ensemble/_bagging.py000066400000000000000000000313321521136113700266720ustar00rootroot00000000000000"""Bagging classifier trained on balanced bootstrap samples.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import copy import numbers import numpy as np from sklearn.base import clone from sklearn.ensemble import BaggingClassifier from sklearn.tree import DecisionTreeClassifier from sklearn.utils._param_validation import HasMethods, Interval, StrOptions from sklearn_compat.base import _fit_context from imblearn.pipeline import Pipeline from imblearn.under_sampling import RandomUnderSampler from imblearn.under_sampling.base import BaseUnderSampler from imblearn.utils import Substitution, check_sampling_strategy, check_target_type from imblearn.utils._docstring import _n_jobs_docstring, _random_state_docstring @Substitution( sampling_strategy=BaseUnderSampler._sampling_strategy_docstring, n_jobs=_n_jobs_docstring, random_state=_random_state_docstring, ) class BalancedBaggingClassifier(BaggingClassifier): """A Bagging classifier with additional balancing. This implementation of Bagging is similar to the scikit-learn implementation. It includes an additional step to balance the training set at fit time using a given sampler. This classifier can serves as a basis to implement various methods such as Exactly Balanced Bagging [6]_, Roughly Balanced Bagging [7]_, Over-Bagging [6]_, or SMOTE-Bagging [8]_. Read more in the :ref:`User Guide `. Parameters ---------- estimator : estimator object, default=None The base estimator to fit on random subsets of the dataset. If None, then the base estimator is a decision tree. .. versionadded:: 0.10 n_estimators : int, default=10 The number of base estimators in the ensemble. max_samples : int or float, default=1.0 The number of samples to draw from X to train each base estimator. - If int, then draw ``max_samples`` samples. - If float, then draw ``max_samples * X.shape[0]`` samples. max_features : int or float, default=1.0 The number of features to draw from X to train each base estimator. - If int, then draw ``max_features`` features. - If float, then draw ``max_features * X.shape[1]`` features. bootstrap : bool, default=True Whether samples are drawn with replacement. .. note:: Note that this bootstrap will be generated from the resampled dataset. bootstrap_features : bool, default=False Whether features are drawn with replacement. oob_score : bool, default=False Whether to use out-of-bag samples to estimate the generalization error. warm_start : bool, default=False When set to True, reuse the solution of the previous call to fit and add more estimators to the ensemble, otherwise, just fit a whole new ensemble. {sampling_strategy} replacement : bool, default=False Whether or not to randomly sample with replacement or not when `sampler is None`, corresponding to a :class:`~imblearn.under_sampling.RandomUnderSampler`. {n_jobs} {random_state} verbose : int, default=0 Controls the verbosity of the building process. sampler : sampler object, default=None The sampler used to balanced the dataset before to bootstrap (if `bootstrap=True`) and `fit` a base estimator. By default, a :class:`~imblearn.under_sampling.RandomUnderSampler` is used. .. versionadded:: 0.8 Attributes ---------- estimator_ : estimator The base estimator from which the ensemble is grown. .. versionadded:: 0.10 estimators_ : list of estimators The collection of fitted base estimators. sampler_ : sampler object The validate sampler created from the `sampler` parameter. estimators_samples_ : list of ndarray The subset of drawn samples (i.e., the in-bag samples) for each base estimator. Each subset is defined by a boolean mask. estimators_features_ : list of ndarray The subset of drawn features for each base estimator. classes_ : ndarray of shape (n_classes,) The classes labels. n_classes_ : int or list The number of classes. oob_score_ : float Score of the training dataset obtained using an out-of-bag estimate. oob_decision_function_ : ndarray of shape (n_samples, n_classes) Decision function computed with out-of-bag estimate on the training set. If n_estimators is small it might be possible that a data point was never left out during the bootstrap. In this case, ``oob_decision_function_`` might contain NaN. n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.9 See Also -------- BalancedRandomForestClassifier : Random forest applying random-under sampling to balance the different bootstraps. EasyEnsembleClassifier : Ensemble of AdaBoost classifier trained on balanced bootstraps. RUSBoostClassifier : AdaBoost classifier were each bootstrap is balanced using random-under sampling at each round of boosting. Notes ----- This is possible to turn this classifier into a balanced random forest [5]_ by passing a :class:`~sklearn.tree.DecisionTreeClassifier` with `max_features='auto'` as a base estimator. See :ref:`sphx_glr_auto_examples_ensemble_plot_comparison_ensemble_classifier.py`. References ---------- .. [1] L. Breiman, "Pasting small votes for classification in large databases and on-line", Machine Learning, 36(1), 85-103, 1999. .. [2] L. Breiman, "Bagging predictors", Machine Learning, 24(2), 123-140, 1996. .. [3] T. Ho, "The random subspace method for constructing decision forests", Pattern Analysis and Machine Intelligence, 20(8), 832-844, 1998. .. [4] G. Louppe and P. Geurts, "Ensembles on Random Patches", Machine Learning and Knowledge Discovery in Databases, 346-361, 2012. .. [5] C. Chen Chao, A. Liaw, and L. Breiman. "Using random forest to learn imbalanced data." University of California, Berkeley 110, 2004. .. [6] R. Maclin, and D. Opitz. "An empirical evaluation of bagging and boosting." AAAI/IAAI 1997 (1997): 546-551. .. [7] S. Hido, H. Kashima, and Y. Takahashi. "Roughly balanced bagging for imbalanced data." Statistical Analysis and Data Mining: The ASA Data Science Journal 2.5‐6 (2009): 412-426. .. [8] S. Wang, and X. Yao. "Diversity analysis on imbalanced data sets by using ensemble models." 2009 IEEE symposium on computational intelligence and data mining. IEEE, 2009. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from sklearn.model_selection import train_test_split >>> from sklearn.metrics import confusion_matrix >>> from imblearn.ensemble import BalancedBaggingClassifier >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> X_train, X_test, y_train, y_test = train_test_split(X, y, ... random_state=0) >>> bbc = BalancedBaggingClassifier(random_state=42) >>> bbc.fit(X_train, y_train) BalancedBaggingClassifier(...) >>> y_pred = bbc.predict(X_test) >>> print(confusion_matrix(y_test, y_pred)) [[ 23 0] [ 2 225]] """ # make a deepcopy to not modify the original dictionary _parameter_constraints = copy.deepcopy(BaggingClassifier._parameter_constraints) _parameter_constraints.update( { "sampling_strategy": [ Interval(numbers.Real, 0, 1, closed="right"), StrOptions({"auto", "majority", "not minority", "not majority", "all"}), dict, callable, ], "replacement": ["boolean"], "sampler": [HasMethods(["fit_resample"]), None], } ) def __init__( self, estimator=None, n_estimators=10, *, max_samples=1.0, max_features=1.0, bootstrap=True, bootstrap_features=False, oob_score=False, warm_start=False, sampling_strategy="auto", replacement=False, n_jobs=None, random_state=None, verbose=0, sampler=None, ): super().__init__( n_estimators=n_estimators, max_samples=max_samples, max_features=max_features, bootstrap=bootstrap, bootstrap_features=bootstrap_features, oob_score=oob_score, warm_start=warm_start, n_jobs=n_jobs, random_state=random_state, verbose=verbose, ) self.estimator = estimator self.sampling_strategy = sampling_strategy self.replacement = replacement self.sampler = sampler def _validate_y(self, y): y_encoded = super()._validate_y(y) if ( isinstance(self.sampling_strategy, dict) and self.sampler_._sampling_type != "bypass" ): self._sampling_strategy = { np.where(self.classes_ == key)[0][0]: value for key, value in check_sampling_strategy( self.sampling_strategy, y, self.sampler_._sampling_type, ).items() } else: self._sampling_strategy = self.sampling_strategy return y_encoded def _validate_estimator(self, default=DecisionTreeClassifier()): """Check the estimator and the n_estimator attribute, set the `estimator_` attribute.""" if self.estimator is not None: estimator = clone(self.estimator) else: estimator = clone(default) if self.sampler_._sampling_type != "bypass": self.sampler_.set_params(sampling_strategy=self._sampling_strategy) self.estimator_ = Pipeline( [("sampler", self.sampler_), ("classifier", estimator)] ) @_fit_context(prefer_skip_nested_validation=False) def fit(self, X, y): """Build a Bagging ensemble of estimators from the training set (X, y). Parameters ---------- X : {array-like, sparse matrix} of shape (n_samples, n_features) The training input samples. Sparse matrices are accepted only if they are supported by the base estimator. y : array-like of shape (n_samples,) The target values (class labels in classification, real numbers in regression). Returns ------- self : object Fitted estimator. """ # overwrite the base class method by disallowing `sample_weight` self._validate_params() return super().fit(X, y) def _fit(self, X, y, max_samples=None, max_depth=None, sample_weight=None): check_target_type(y) # the sampler needs to be validated before to call _fit because # _validate_y is called before _validate_estimator and would require # to know which type of sampler we are using. if self.sampler is None: self.sampler_ = RandomUnderSampler( replacement=self.replacement, ) else: self.sampler_ = clone(self.sampler) # RandomUnderSampler is not supporting sample_weight. We need to pass # None. return super()._fit(X, y, self.max_samples) @property def base_estimator_(self): """Attribute for older sklearn version compatibility.""" error = AttributeError( f"{self.__class__.__name__} object has no attribute 'base_estimator_'." ) raise error def _more_tags(self): tags = super()._more_tags() tags_key = "_xfail_checks" failing_test = "check_estimators_nan_inf" reason = "Fails because the sampler removed infinity and NaN values" if tags_key in tags: tags[tags_key][failing_test] = reason else: tags[tags_key] = {failing_test: reason} return tags def __sklearn_tags__(self): tags = super().__sklearn_tags__() return tags scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/ensemble/_common.py000066400000000000000000000062611521136113700265670ustar00rootroot00000000000000from numbers import Integral, Real from sklearn.tree._criterion import Criterion from sklearn.utils._param_validation import ( HasMethods, Hidden, Interval, RealNotInt, StrOptions, ) def _estimator_has(attr): """Check if we can delegate a method to the underlying estimator. First, we check the first fitted estimator if available, otherwise we check the estimator attribute. """ def check(self): if hasattr(self, "estimators_"): return hasattr(self.estimators_[0], attr) else: # self.estimator is not None return hasattr(self.estimator, attr) return check _bagging_parameter_constraints = { "estimator": [HasMethods(["fit", "predict"]), None], "n_estimators": [Interval(Integral, 1, None, closed="left")], "max_samples": [ Interval(Integral, 1, None, closed="left"), Interval(RealNotInt, 0, 1, closed="right"), ], "max_features": [ Interval(Integral, 1, None, closed="left"), Interval(RealNotInt, 0, 1, closed="right"), ], "bootstrap": ["boolean"], "bootstrap_features": ["boolean"], "oob_score": ["boolean"], "warm_start": ["boolean"], "n_jobs": [None, Integral], "random_state": ["random_state"], "verbose": ["verbose"], } _adaboost_classifier_parameter_constraints = { "estimator": [HasMethods(["fit", "predict"]), None], "n_estimators": [Interval(Integral, 1, None, closed="left")], "learning_rate": [Interval(Real, 0, None, closed="neither")], "random_state": ["random_state"], "base_estimator": [HasMethods(["fit", "predict"]), StrOptions({"deprecated"})], "algorithm": [StrOptions({"SAMME", "SAMME.R"})], } _random_forest_classifier_parameter_constraints = { "n_estimators": [Interval(Integral, 1, None, closed="left")], "bootstrap": ["boolean"], "oob_score": ["boolean"], "n_jobs": [Integral, None], "random_state": ["random_state"], "verbose": ["verbose"], "warm_start": ["boolean"], "criterion": [StrOptions({"gini", "entropy", "log_loss"}), Hidden(Criterion)], "max_samples": [ None, Interval(Real, 0.0, 1.0, closed="right"), Interval(Integral, 1, None, closed="left"), ], "max_depth": [Interval(Integral, 1, None, closed="left"), None], "min_samples_split": [ Interval(Integral, 2, None, closed="left"), Interval(RealNotInt, 0.0, 1.0, closed="right"), ], "min_samples_leaf": [ Interval(Integral, 1, None, closed="left"), Interval(RealNotInt, 0.0, 1.0, closed="neither"), ], "min_weight_fraction_leaf": [Interval(Real, 0.0, 0.5, closed="both")], "max_features": [ Interval(Integral, 1, None, closed="left"), Interval(RealNotInt, 0.0, 1.0, closed="right"), StrOptions({"sqrt", "log2"}), None, ], "max_leaf_nodes": [Interval(Integral, 2, None, closed="left"), None], "min_impurity_decrease": [Interval(Real, 0.0, None, closed="left")], "ccp_alpha": [Interval(Real, 0.0, None, closed="left")], "class_weight": [ StrOptions({"balanced_subsample", "balanced"}), dict, list, None, ], "monotonic_cst": ["array-like", None], } scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/ensemble/_easy_ensemble.py000066400000000000000000000232761521136113700301170ustar00rootroot00000000000000"""Class to perform under-sampling using easy ensemble.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import copy import numbers import numpy as np from sklearn.base import clone from sklearn.ensemble import AdaBoostClassifier, BaggingClassifier from sklearn.utils._param_validation import Interval, StrOptions from sklearn.utils.fixes import parse_version from sklearn_compat._sklearn_compat import sklearn_version from sklearn_compat.base import _fit_context from imblearn.ensemble._common import _bagging_parameter_constraints from imblearn.pipeline import Pipeline from imblearn.under_sampling import RandomUnderSampler from imblearn.under_sampling.base import BaseUnderSampler from imblearn.utils import Substitution, check_sampling_strategy, check_target_type from imblearn.utils._docstring import _n_jobs_docstring, _random_state_docstring from imblearn.utils._tags import get_tags MAX_INT = np.iinfo(np.int32).max @Substitution( sampling_strategy=BaseUnderSampler._sampling_strategy_docstring, n_jobs=_n_jobs_docstring, random_state=_random_state_docstring, ) class EasyEnsembleClassifier(BaggingClassifier): """Bag of balanced boosted learners also known as EasyEnsemble. This algorithm is known as EasyEnsemble [1]_. The classifier is an ensemble of AdaBoost learners trained on different balanced bootstrap samples. The balancing is achieved by random under-sampling. Read more in the :ref:`User Guide `. .. versionadded:: 0.4 Parameters ---------- n_estimators : int, default=10 Number of AdaBoost learners in the ensemble. estimator : estimator object, default=AdaBoostClassifier() The base AdaBoost classifier used in the inner ensemble. Note that you can set the number of inner learner by passing your own instance. .. versionadded:: 0.10 warm_start : bool, default=False When set to True, reuse the solution of the previous call to fit and add more estimators to the ensemble, otherwise, just fit a whole new ensemble. {sampling_strategy} replacement : bool, default=False Whether or not to sample randomly with replacement or not. {n_jobs} {random_state} verbose : int, default=0 Controls the verbosity of the building process. Attributes ---------- estimator_ : estimator The base estimator from which the ensemble is grown. .. versionadded:: 0.10 estimators_ : list of estimators The collection of fitted base estimators. estimators_samples_ : list of arrays The subset of drawn samples for each base estimator. estimators_features_ : list of arrays The subset of drawn features for each base estimator. classes_ : array, shape (n_classes,) The classes labels. n_classes_ : int or list The number of classes. n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.9 See Also -------- BalancedBaggingClassifier : Bagging classifier for which each base estimator is trained on a balanced bootstrap. BalancedRandomForestClassifier : Random forest applying random-under sampling to balance the different bootstraps. RUSBoostClassifier : AdaBoost classifier were each bootstrap is balanced using random-under sampling at each round of boosting. Notes ----- The method is described in [1]_. Supports multi-class resampling by sampling each class independently. References ---------- .. [1] X. Y. Liu, J. Wu and Z. H. Zhou, "Exploratory Undersampling for Class-Imbalance Learning," in IEEE Transactions on Systems, Man, and Cybernetics, Part B (Cybernetics), vol. 39, no. 2, pp. 539-550, April 2009. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from sklearn.model_selection import train_test_split >>> from sklearn.metrics import confusion_matrix >>> from imblearn.ensemble import EasyEnsembleClassifier >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> X_train, X_test, y_train, y_test = train_test_split(X, y, ... random_state=0) >>> eec = EasyEnsembleClassifier(random_state=42) >>> eec.fit(X_train, y_train) EasyEnsembleClassifier(...) >>> y_pred = eec.predict(X_test) >>> print(confusion_matrix(y_test, y_pred)) [[ 23 0] [ 2 225]] """ # make a deepcopy to not modify the original dictionary if sklearn_version >= parse_version("1.4"): _parameter_constraints = copy.deepcopy(BaggingClassifier._parameter_constraints) else: _parameter_constraints = copy.deepcopy(_bagging_parameter_constraints) excluded_params = { "bootstrap", "bootstrap_features", "max_features", "oob_score", "max_samples", } for param in excluded_params: _parameter_constraints.pop(param, None) _parameter_constraints.update( { "sampling_strategy": [ Interval(numbers.Real, 0, 1, closed="right"), StrOptions({"auto", "majority", "not minority", "not majority", "all"}), dict, callable, ], "replacement": ["boolean"], } ) # TODO: remove when minimum supported version of scikit-learn is 1.4 if "base_estimator" in _parameter_constraints: del _parameter_constraints["base_estimator"] def __init__( self, n_estimators=10, estimator=None, *, warm_start=False, sampling_strategy="auto", replacement=False, n_jobs=None, random_state=None, verbose=0, ): super().__init__( n_estimators=n_estimators, max_samples=1.0, max_features=1.0, bootstrap=False, bootstrap_features=False, oob_score=False, warm_start=warm_start, n_jobs=n_jobs, random_state=random_state, verbose=verbose, ) self.estimator = estimator self.sampling_strategy = sampling_strategy self.replacement = replacement def _validate_y(self, y): y_encoded = super()._validate_y(y) if isinstance(self.sampling_strategy, dict): self._sampling_strategy = { np.where(self.classes_ == key)[0][0]: value for key, value in check_sampling_strategy( self.sampling_strategy, y, "under-sampling", ).items() } else: self._sampling_strategy = self.sampling_strategy return y_encoded def _validate_estimator(self, default=None): """Check the estimator and the n_estimator attribute, set the `estimator_` attribute.""" if self.estimator is not None: estimator = clone(self.estimator) else: if default is None: default = self._get_estimator() estimator = clone(default) sampler = RandomUnderSampler( sampling_strategy=self._sampling_strategy, replacement=self.replacement, ) self.estimator_ = Pipeline([("sampler", sampler), ("classifier", estimator)]) @_fit_context(prefer_skip_nested_validation=False) def fit(self, X, y): """Build a Bagging ensemble of estimators from the training set (X, y). Parameters ---------- X : {array-like, sparse matrix} of shape (n_samples, n_features) The training input samples. Sparse matrices are accepted only if they are supported by the base estimator. y : array-like of shape (n_samples,) The target values (class labels in classification, real numbers in regression). Returns ------- self : object Fitted estimator. """ self._validate_params() # overwrite the base class method by disallowing `sample_weight` return super().fit(X, y) def _fit(self, X, y, max_samples=None, max_depth=None, sample_weight=None): check_target_type(y) # RandomUnderSampler is not supporting sample_weight. We need to pass # None. return super()._fit(X, y, self.max_samples) @property def base_estimator_(self): """Attribute for older sklearn version compatibility.""" error = AttributeError( f"{self.__class__.__name__} object has no attribute 'base_estimator_'." ) raise error def _get_estimator(self): if self.estimator is None: if parse_version("1.4") <= sklearn_version < parse_version("1.6"): return AdaBoostClassifier(algorithm="SAMME") else: return AdaBoostClassifier() return self.estimator def _more_tags(self): return {"allow_nan": get_tags(self._get_estimator()).input_tags.allow_nan} def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.input_tags.allow_nan = get_tags(self._get_estimator()).input_tags.allow_nan return tags scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/ensemble/_forest.py000066400000000000000000001005021521136113700265720ustar00rootroot00000000000000"""Forest classifiers trained on balanced boostrasp samples.""" # Authors: Guillaume Lemaitre # License: MIT import numbers from copy import deepcopy from warnings import warn import numpy as np from numpy import float32 as DTYPE from numpy import float64 as DOUBLE from scipy.sparse import issparse from sklearn.base import clone, is_classifier from sklearn.ensemble import RandomForestClassifier from sklearn.ensemble._base import _set_random_states from sklearn.ensemble._forest import ( _generate_unsampled_indices, _get_n_samples_bootstrap, _parallel_build_trees, ) from sklearn.exceptions import DataConversionWarning from sklearn.tree import DecisionTreeClassifier from sklearn.utils import _safe_indexing, check_random_state from sklearn.utils._param_validation import Hidden, Interval, StrOptions from sklearn.utils.fixes import parse_version from sklearn.utils.multiclass import type_of_target from sklearn.utils.parallel import Parallel, delayed from sklearn.utils.validation import _check_sample_weight from sklearn_compat._sklearn_compat import sklearn_version from sklearn_compat.base import _fit_context from sklearn_compat.utils.validation import validate_data from imblearn.ensemble._common import _random_forest_classifier_parameter_constraints from imblearn.pipeline import make_pipeline from imblearn.under_sampling import RandomUnderSampler from imblearn.utils import Substitution from imblearn.utils._docstring import _n_jobs_docstring, _random_state_docstring from imblearn.utils._validation import check_sampling_strategy MAX_INT = np.iinfo(np.int32).max def _local_parallel_build_trees( sampler, tree, bootstrap, X, y, sample_weight, tree_idx, n_trees, verbose=0, class_weight=None, n_samples_bootstrap=None, forest=None, missing_values_in_feature_mask=None, ): # resample before to fit the tree X_resampled, y_resampled = sampler.fit_resample(X, y) if sample_weight is not None: sample_weight = _safe_indexing(sample_weight, sampler.sample_indices_) if _get_n_samples_bootstrap is not None: n_samples_bootstrap = min(n_samples_bootstrap, X_resampled.shape[0]) params_parallel_build_trees = { "tree": tree, "X": X_resampled, "y": y_resampled, "sample_weight": sample_weight, "tree_idx": tree_idx, "n_trees": n_trees, "verbose": verbose, "class_weight": class_weight, "n_samples_bootstrap": n_samples_bootstrap, "bootstrap": bootstrap, } params_parallel_build_trees["missing_values_in_feature_mask"] = ( missing_values_in_feature_mask ) tree = _parallel_build_trees(**params_parallel_build_trees) return sampler, tree @Substitution( n_jobs=_n_jobs_docstring, random_state=_random_state_docstring, ) class BalancedRandomForestClassifier(RandomForestClassifier): """A balanced random forest classifier. A balanced random forest differs from a classical random forest by the fact that it will draw a bootstrap sample from the minority class and sample with replacement the same number of samples from the majority class. Read more in the :ref:`User Guide `. .. versionadded:: 0.4 Parameters ---------- n_estimators : int, default=100 The number of trees in the forest. criterion : {{"gini", "entropy"}}, default="gini" The function to measure the quality of a split. Supported criteria are "gini" for the Gini impurity and "entropy" for the information gain. Note: this parameter is tree-specific. max_depth : int, default=None The maximum depth of the tree. If None, then nodes are expanded until all leaves are pure or until all leaves contain less than min_samples_split samples. min_samples_split : int or float, default=2 The minimum number of samples required to split an internal node: - If int, then consider `min_samples_split` as the minimum number. - If float, then `min_samples_split` is a percentage and `ceil(min_samples_split * n_samples)` are the minimum number of samples for each split. min_samples_leaf : int or float, default=1 The minimum number of samples required to be at a leaf node: - If int, then consider ``min_samples_leaf`` as the minimum number. - If float, then ``min_samples_leaf`` is a fraction and `ceil(min_samples_leaf * n_samples)` are the minimum number of samples for each node. min_weight_fraction_leaf : float, default=0.0 The minimum weighted fraction of the sum total of weights (of all the input samples) required to be at a leaf node. Samples have equal weight when sample_weight is not provided. max_features : {{"auto", "sqrt", "log2"}}, int, float, or None, \ default="sqrt" The number of features to consider when looking for the best split: - If int, then consider `max_features` features at each split. - If float, then `max_features` is a percentage and `int(max_features * n_features)` features are considered at each split. - If "auto", then `max_features=sqrt(n_features)`. - If "sqrt", then `max_features=sqrt(n_features)` (same as "auto"). - If "log2", then `max_features=log2(n_features)`. - If None, then `max_features=n_features`. Note: the search for a split does not stop until at least one valid partition of the node samples is found, even if it requires to effectively inspect more than ``max_features`` features. max_leaf_nodes : int, default=None Grow trees with ``max_leaf_nodes`` in best-first fashion. Best nodes are defined as relative reduction in impurity. If None then unlimited number of leaf nodes. min_impurity_decrease : float, default=0.0 A node will be split if this split induces a decrease of the impurity greater than or equal to this value. The weighted impurity decrease equation is the following:: N_t / N * (impurity - N_t_R / N_t * right_impurity - N_t_L / N_t * left_impurity) where ``N`` is the total number of samples, ``N_t`` is the number of samples at the current node, ``N_t_L`` is the number of samples in the left child, and ``N_t_R`` is the number of samples in the right child. ``N``, ``N_t``, ``N_t_R`` and ``N_t_L`` all refer to the weighted sum, if ``sample_weight`` is passed. bootstrap : bool, default=True Whether bootstrap samples are used when building trees. .. versionchanged:: 0.13 The default of `bootstrap` will change from `True` to `False` in version 0.13. Bootstrapping is already taken care by the internal sampler using `replacement=True`. This implementation follows the algorithm proposed in [1]_. oob_score : bool, default=False Whether to use out-of-bag samples to estimate the generalization accuracy. sampling_strategy : float, str, dict, callable, default="auto" Sampling information to sample the data set. - When ``float``, it corresponds to the desired ratio of the number of samples in the minority class over the number of samples in the majority class after resampling. Therefore, the ratio is expressed as :math:`\\alpha_{{us}} = N_{{m}} / N_{{rM}}` where :math:`N_{{m}}` is the number of samples in the minority class and :math:`N_{{rM}}` is the number of samples in the majority class after resampling. .. warning:: ``float`` is only available for **binary** classification. An error is raised for multi-class classification. - When ``str``, specify the class targeted by the resampling. The number of samples in the different classes will be equalized. Possible choices are: ``'majority'``: resample only the majority class; ``'not minority'``: resample all classes but the minority class; ``'not majority'``: resample all classes but the majority class; ``'all'``: resample all classes; ``'auto'``: equivalent to ``'not minority'``. - When ``dict``, the keys correspond to the targeted classes. The values correspond to the desired number of samples for each targeted class. - When callable, function taking ``y`` and returns a ``dict``. The keys correspond to the targeted classes. The values correspond to the desired number of samples for each class. .. versionchanged:: 0.11 The default of `sampling_strategy` will change from `"auto"` to `"all"` in version 0.13. This forces to use a bootstrap of the minority class as proposed in [1]_. replacement : bool, default=False Whether or not to sample randomly with replacement or not. .. versionchanged:: 0.11 The default of `replacement` will change from `False` to `True` in version 0.13. This forces to use a bootstrap of the minority class and draw with replacement as proposed in [1]_. {n_jobs} {random_state} verbose : int, default=0 Controls the verbosity of the tree building process. warm_start : bool, default=False When set to ``True``, reuse the solution of the previous call to fit and add more estimators to the ensemble, otherwise, just fit a whole new forest. class_weight : dict, list of dicts, {{"balanced", "balanced_subsample"}}, \ default=None Weights associated with classes in the form dictionary with the key being the class_label and the value the weight. If not given, all classes are supposed to have weight one. For multi-output problems, a list of dicts can be provided in the same order as the columns of y. Note that for multioutput (including multilabel) weights should be defined for each class of every column in its own dict. For example, for four-class multilabel classification weights should be [{{0: 1, 1: 1}}, {{0: 1, 1: 5}}, {{0: 1, 1: 1}}, {{0: 1, 1: 1}}] instead of [{{1:1}}, {{2:5}}, {{3:1}}, {{4:1}}]. The "balanced" mode uses the values of y to automatically adjust weights inversely proportional to class frequencies in the input data as ``n_samples / (n_classes * np.bincount(y))`` The "balanced_subsample" mode is the same as "balanced" except that weights are computed based on the bootstrap sample for every tree grown. For multi-output, the weights of each column of y will be multiplied. Note that these weights will be multiplied with sample_weight (passed through the fit method) if sample_weight is specified. ccp_alpha : non-negative float, default=0.0 Complexity parameter used for Minimal Cost-Complexity Pruning. The subtree with the largest cost complexity that is smaller than ``ccp_alpha`` will be chosen. By default, no pruning is performed. .. versionadded:: 0.6 Added in `scikit-learn` in 0.22 max_samples : int or float, default=None If bootstrap is True, the number of samples to draw from X to train each base estimator. - If None (default), then draw `X.shape[0]` samples. - If int, then draw `max_samples` samples. - If float, then draw `max_samples * X.shape[0]` samples. Thus, `max_samples` should be in the interval `(0, 1)`. Be aware that the final number samples used will be the minimum between the number of samples given in `max_samples` and the number of samples obtained after resampling. .. versionadded:: 0.6 Added in `scikit-learn` in 0.22 monotonic_cst : array-like of int of shape (n_features), default=None Indicates the monotonicity constraint to enforce on each feature. - 1: monotonic increase - 0: no constraint - -1: monotonic decrease If monotonic_cst is None, no constraints are applied. Monotonicity constraints are not supported for: - multiclass classifications (i.e. when `n_classes > 2`), - multioutput classifications (i.e. when `n_outputs_ > 1`), - classifications trained on data with missing values. The constraints hold over the probability of the positive class. .. versionadded:: 0.12 Only supported when scikit-learn >= 1.4 is installed. Otherwise, a `ValueError` is raised. Attributes ---------- estimator_ : :class:`~sklearn.tree.DecisionTreeClassifier` instance The child estimator template used to create the collection of fitted sub-estimators. .. versionadded:: 0.10 estimators_ : list of :class:`~sklearn.tree.DecisionTreeClassifier` The collection of fitted sub-estimators. base_sampler_ : :class:`~imblearn.under_sampling.RandomUnderSampler` The base sampler used to construct the subsequent list of samplers. samplers_ : list of :class:`~imblearn.under_sampling.RandomUnderSampler` The collection of fitted samplers. pipelines_ : list of Pipeline. The collection of fitted pipelines (samplers + trees). classes_ : ndarray of shape (n_classes,) or a list of such arrays The classes labels (single output problem), or a list of arrays of class labels (multi-output problem). n_classes_ : int or list The number of classes (single output problem), or a list containing the number of classes for each output (multi-output problem). n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.9 n_outputs_ : int The number of outputs when ``fit`` is performed. feature_importances_ : ndarray of shape (n_features,) The feature importances (the higher, the more important the feature). oob_score_ : float Score of the training dataset obtained using an out-of-bag estimate. oob_decision_function_ : ndarray of shape (n_samples, n_classes) Decision function computed with out-of-bag estimate on the training set. If n_estimators is small it might be possible that a data point was never left out during the bootstrap. In this case, `oob_decision_function_` might contain NaN. See Also -------- BalancedBaggingClassifier : Bagging classifier for which each base estimator is trained on a balanced bootstrap. EasyEnsembleClassifier : Ensemble of AdaBoost classifier trained on balanced bootstraps. RUSBoostClassifier : AdaBoost classifier were each bootstrap is balanced using random-under sampling at each round of boosting. References ---------- .. [1] Chen, Chao, Andy Liaw, and Leo Breiman. "Using random forest to learn imbalanced data." University of California, Berkeley 110 (2004): 1-12. Examples -------- >>> from imblearn.ensemble import BalancedRandomForestClassifier >>> from sklearn.datasets import make_classification >>> >>> X, y = make_classification(n_samples=1000, n_classes=3, ... n_informative=4, weights=[0.2, 0.3, 0.5], ... random_state=0) >>> clf = BalancedRandomForestClassifier( ... sampling_strategy="all", replacement=True, max_depth=2, random_state=0, ... bootstrap=False) >>> clf.fit(X, y) BalancedRandomForestClassifier(...) >>> print(clf.feature_importances_) [...] >>> print(clf.predict([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ... 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])) [1] """ # make a deepcopy to not modify the original dictionary if sklearn_version >= parse_version("1.4"): _parameter_constraints = deepcopy(RandomForestClassifier._parameter_constraints) else: _parameter_constraints = deepcopy( _random_forest_classifier_parameter_constraints ) _parameter_constraints.update( { "bootstrap": ["boolean", Hidden(StrOptions({"warn"}))], "sampling_strategy": [ Interval(numbers.Real, 0, 1, closed="right"), StrOptions({"auto", "majority", "not minority", "not majority", "all"}), dict, callable, Hidden(StrOptions({"warn"})), ], "replacement": ["boolean", Hidden(StrOptions({"warn"}))], } ) def __init__( self, n_estimators=100, *, criterion="gini", max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features="sqrt", max_leaf_nodes=None, min_impurity_decrease=0.0, bootstrap=False, oob_score=False, sampling_strategy="all", replacement=True, n_jobs=None, random_state=None, verbose=0, warm_start=False, class_weight=None, ccp_alpha=0.0, max_samples=None, monotonic_cst=None, ): params_random_forest = { "criterion": criterion, "max_depth": max_depth, "n_estimators": n_estimators, "bootstrap": bootstrap, "oob_score": oob_score, "n_jobs": n_jobs, "random_state": random_state, "verbose": verbose, "warm_start": warm_start, "class_weight": class_weight, "min_samples_split": min_samples_split, "min_samples_leaf": min_samples_leaf, "min_weight_fraction_leaf": min_weight_fraction_leaf, "max_features": max_features, "max_leaf_nodes": max_leaf_nodes, "min_impurity_decrease": min_impurity_decrease, "ccp_alpha": ccp_alpha, "max_samples": max_samples, "monotonic_cst": monotonic_cst, } super().__init__(**params_random_forest) self.sampling_strategy = sampling_strategy self.replacement = replacement def _validate_estimator(self, default=DecisionTreeClassifier()): """Check the estimator and the n_estimator attribute, set the `estimator_` attribute.""" if self.estimator is not None: self.estimator_ = clone(self.estimator) else: self.estimator_ = clone(default) self.base_sampler_ = RandomUnderSampler( sampling_strategy=self._sampling_strategy, replacement=self.replacement, ) def _make_sampler_estimator(self, random_state=None): """Make and configure a copy of the `base_estimator_` attribute. Warning: This method should be used to properly instantiate new sub-estimators. """ estimator = clone(self.estimator_) estimator.set_params(**{p: getattr(self, p) for p in self.estimator_params}) sampler = clone(self.base_sampler_) if random_state is not None: _set_random_states(estimator, random_state) _set_random_states(sampler, random_state) return estimator, sampler @_fit_context(prefer_skip_nested_validation=True) def fit(self, X, y, sample_weight=None): """Build a forest of trees from the training set (X, y). Parameters ---------- X : {array-like, sparse matrix} of shape (n_samples, n_features) The training input samples. Internally, its dtype will be converted to ``dtype=np.float32``. If a sparse matrix is provided, it will be converted into a sparse ``csc_matrix``. y : array-like of shape (n_samples,) or (n_samples, n_outputs) The target values (class labels in classification, real numbers in regression). sample_weight : array-like of shape (n_samples,) Sample weights. If None, then samples are equally weighted. Splits that would create child nodes with net zero or negative weight are ignored while searching for a split in each node. In the case of classification, splits are also ignored if they would result in any single class carrying a negative weight in either child node. Returns ------- self : object The fitted instance. """ self._validate_params() # Validate or convert input data if issparse(y): raise ValueError("sparse multilabel-indicator for y is not supported.") X, y = validate_data( self, X=X, y=y, multi_output=True, accept_sparse="csc", dtype=DTYPE, ensure_all_finite=False, ) # _compute_missing_values_in_feature_mask checks if X has missing values and # will raise an error if the underlying tree base estimator can't handle # missing values. Only the criterion is required to determine if the tree # supports missing values. estimator = type(self.estimator)(criterion=self.criterion) missing_values_in_feature_mask = ( estimator._compute_missing_values_in_feature_mask( X, estimator_name=self.__class__.__name__ ) ) if sample_weight is not None: sample_weight = _check_sample_weight(sample_weight, X) self._n_features = X.shape[1] if issparse(X): # Pre-sort indices to avoid that each individual tree of the # ensemble sorts the indices. X.sort_indices() y = np.atleast_1d(y) if y.ndim == 2 and y.shape[1] == 1: warn( ( "A column-vector y was passed when a 1d array was" " expected. Please change the shape of y to " "(n_samples,), for example using ravel()." ), DataConversionWarning, stacklevel=2, ) if y.ndim == 1: # reshape is necessary to preserve the data contiguity against vs # [:, np.newaxis] that does not. y = np.reshape(y, (-1, 1)) self.n_outputs_ = y.shape[1] if sklearn_version >= parse_version("1.9"): y_encoded, expanded_class_weight = self._validate_y_class_weight( y, sample_weight ) else: y_encoded, expanded_class_weight = self._validate_y_class_weight(y) if getattr(y, "dtype", None) != DOUBLE or not y.flags.contiguous: y_encoded = np.ascontiguousarray(y_encoded, dtype=DOUBLE) if isinstance(self.sampling_strategy, dict): self._sampling_strategy = { np.where(self.classes_[0] == key)[0][0]: value for key, value in check_sampling_strategy( self.sampling_strategy, y, "under-sampling", ).items() } else: self._sampling_strategy = self.sampling_strategy if expanded_class_weight is not None: if sample_weight is not None: sample_weight = sample_weight * expanded_class_weight else: sample_weight = expanded_class_weight # Get bootstrap sample size if sklearn_version >= parse_version("1.9"): n_samples_bootstrap = _get_n_samples_bootstrap( n_samples=X.shape[0], max_samples=self.max_samples, sample_weight=sample_weight, ) else: n_samples_bootstrap = _get_n_samples_bootstrap( n_samples=X.shape[0], max_samples=self.max_samples ) # Check parameters self._validate_estimator() if not self.bootstrap and self.oob_score: raise ValueError("Out of bag estimation only available if bootstrap=True") random_state = check_random_state(self.random_state) if not self.warm_start or not hasattr(self, "estimators_"): # Free allocated memory, if any self.estimators_ = [] self.samplers_ = [] self.pipelines_ = [] n_more_estimators = self.n_estimators - len(self.estimators_) if n_more_estimators < 0: raise ValueError( "n_estimators=%d must be larger or equal to " "len(estimators_)=%d when warm_start==True" % (self.n_estimators, len(self.estimators_)) ) elif n_more_estimators == 0: warn( "Warm-start fitting without increasing n_estimators does not " "fit new trees." ) else: if self.warm_start and len(self.estimators_) > 0: # We draw from the random state to get the random state we # would have got if we hadn't used a warm_start. random_state.randint(MAX_INT, size=len(self.estimators_)) trees = [] samplers = [] for _ in range(n_more_estimators): tree, sampler = self._make_sampler_estimator(random_state=random_state) trees.append(tree) samplers.append(sampler) # Parallel loop: we prefer the threading backend as the Cython code # for fitting the trees is internally releasing the Python GIL # making threading more efficient than multiprocessing in # that case. However, we respect any parallel_backend contexts set # at a higher level, since correctness does not rely on using # threads. samplers_trees = Parallel( n_jobs=self.n_jobs, verbose=self.verbose, prefer="threads", )( delayed(_local_parallel_build_trees)( s, t, self.bootstrap, X, y_encoded, sample_weight, i, len(trees), verbose=self.verbose, class_weight=self.class_weight, n_samples_bootstrap=n_samples_bootstrap, forest=self, missing_values_in_feature_mask=missing_values_in_feature_mask, ) for i, (s, t) in enumerate(zip(samplers, trees)) ) samplers, trees = zip(*samplers_trees) # Collect newly grown trees self.estimators_.extend(trees) self.samplers_.extend(samplers) # Create pipeline with the fitted samplers and trees self.pipelines_.extend( [ make_pipeline(deepcopy(s), deepcopy(t)) for s, t in zip(samplers, trees) ] ) if self.oob_score: y_type = type_of_target(y) if y_type in ("multiclass-multioutput", "unknown"): # FIXME: we could consider to support multiclass-multioutput if # we introduce or reuse a constructor parameter (e.g. # oob_score) allowing our user to pass a callable defining the # scoring strategy on OOB sample. raise ValueError( "The type of target cannot be used to compute OOB " f"estimates. Got {y_type} while only the following are " "supported: continuous, continuous-multioutput, binary, " "multiclass, multilabel-indicator." ) self._set_oob_score_and_attributes(X, y_encoded) # Decapsulate classes_ attributes if hasattr(self, "classes_") and self.n_outputs_ == 1: self.n_classes_ = self.n_classes_[0] self.classes_ = self.classes_[0] return self def _set_oob_score_and_attributes(self, X, y): """Compute and set the OOB score and attributes. Parameters ---------- X : array-like of shape (n_samples, n_features) The data matrix. y : ndarray of shape (n_samples, n_outputs) The target matrix. """ self.oob_decision_function_ = self._compute_oob_predictions(X, y) if self.oob_decision_function_.shape[-1] == 1: # drop the n_outputs axis if there is a single output self.oob_decision_function_ = self.oob_decision_function_.squeeze(axis=-1) from sklearn.metrics import accuracy_score self.oob_score_ = accuracy_score( y, np.argmax(self.oob_decision_function_, axis=1) ) def _compute_oob_predictions(self, X, y): """Compute and set the OOB score. Parameters ---------- X : array-like of shape (n_samples, n_features) The data matrix. y : ndarray of shape (n_samples, n_outputs) The target matrix. Returns ------- oob_pred : ndarray of shape (n_samples, n_classes, n_outputs) or \ (n_samples, 1, n_outputs) The OOB predictions. """ # Prediction requires X to be in CSR format if issparse(X): X = X.tocsr() n_samples = y.shape[0] n_outputs = self.n_outputs_ if is_classifier(self) and hasattr(self, "n_classes_"): # n_classes_ is a ndarray at this stage # all the supported type of target will have the same number of # classes in all outputs oob_pred_shape = (n_samples, self.n_classes_[0], n_outputs) else: # for regression, n_classes_ does not exist and we create an empty # axis to be consistent with the classification case and make # the array operations compatible with the 2 settings oob_pred_shape = (n_samples, 1, n_outputs) oob_pred = np.zeros(shape=oob_pred_shape, dtype=np.float64) n_oob_pred = np.zeros((n_samples, n_outputs), dtype=np.int64) for sampler, estimator in zip(self.samplers_, self.estimators_): X_resample = X[sampler.sample_indices_] y_resample = y[sampler.sample_indices_] n_sample_subset = y_resample.shape[0] if sklearn_version >= parse_version("1.9"): n_samples_bootstrap = _get_n_samples_bootstrap( n_sample_subset, self.max_samples, sample_weight=None ) else: n_samples_bootstrap = _get_n_samples_bootstrap( n_sample_subset, self.max_samples ) if sklearn_version >= parse_version("1.9"): unsampled_indices = _generate_unsampled_indices( estimator.random_state, n_sample_subset, n_samples_bootstrap, sample_weight=None, ) else: unsampled_indices = _generate_unsampled_indices( estimator.random_state, n_sample_subset, n_samples_bootstrap ) y_pred = self._get_oob_predictions( estimator, X_resample[unsampled_indices, :] ) indices = sampler.sample_indices_[unsampled_indices] oob_pred[indices, ...] += y_pred n_oob_pred[indices, :] += 1 for k in range(n_outputs): if (n_oob_pred == 0).any(): warn( ( "Some inputs do not have OOB scores. This probably means " "too few trees were used to compute any reliable OOB " "estimates." ), UserWarning, ) n_oob_pred[n_oob_pred == 0] = 1 oob_pred[..., k] /= n_oob_pred[..., [k]] return oob_pred def _more_tags(self): return {"multioutput": False, "multilabel": False} def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.target_tags.multi_output = False tags.classifier_tags.multi_label = False tags.input_tags.allow_nan = sklearn_version >= parse_version("1.4") return tags scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/ensemble/_weight_boosting.py000066400000000000000000000356201521136113700304730ustar00rootroot00000000000000import copy import numbers import warnings from copy import deepcopy import numpy as np from sklearn.base import clone from sklearn.ensemble import AdaBoostClassifier from sklearn.ensemble._base import _set_random_states from sklearn.tree import DecisionTreeClassifier from sklearn.utils import _safe_indexing from sklearn.utils._param_validation import Hidden, Interval, StrOptions from sklearn.utils.fixes import parse_version from sklearn.utils.validation import has_fit_parameter from sklearn_compat._sklearn_compat import sklearn_version from sklearn_compat.base import _fit_context from imblearn.ensemble._common import _adaboost_classifier_parameter_constraints from imblearn.pipeline import make_pipeline from imblearn.under_sampling import RandomUnderSampler from imblearn.under_sampling.base import BaseUnderSampler from imblearn.utils import Substitution, check_target_type from imblearn.utils._docstring import _random_state_docstring @Substitution( sampling_strategy=BaseUnderSampler._sampling_strategy_docstring, random_state=_random_state_docstring, ) class RUSBoostClassifier(AdaBoostClassifier): """Random under-sampling integrated in the learning of AdaBoost. During learning, the problem of class balancing is alleviated by random under-sampling the sample at each iteration of the boosting algorithm. Read more in the :ref:`User Guide `. .. versionadded:: 0.4 Parameters ---------- estimator : estimator object, default=None The base estimator from which the boosted ensemble is built. Support for sample weighting is required, as well as proper ``classes_`` and ``n_classes_`` attributes. If ``None``, then the base estimator is ``DecisionTreeClassifier(max_depth=1)``. .. versionadded:: 0.12 n_estimators : int, default=50 The maximum number of estimators at which boosting is terminated. In case of perfect fit, the learning procedure is stopped early. learning_rate : float, default=1.0 Learning rate shrinks the contribution of each classifier by ``learning_rate``. There is a trade-off between ``learning_rate`` and ``n_estimators``. algorithm : {{'SAMME', 'SAMME.R'}}, default='SAMME.R' If 'SAMME.R' then use the SAMME.R real boosting algorithm. ``base_estimator`` must support calculation of class probabilities. If 'SAMME' then use the SAMME discrete boosting algorithm. The SAMME.R algorithm typically converges faster than SAMME, achieving a lower test error with fewer boosting iterations. .. deprecated:: 0.12 `"SAMME.R"` is deprecated and will be removed in version 0.14. '"SAMME"' will become the default. {sampling_strategy} replacement : bool, default=False Whether or not to sample randomly with replacement or not. {random_state} Attributes ---------- estimator_ : estimator The base estimator from which the ensemble is grown. .. versionadded:: 0.10 estimators_ : list of classifiers The collection of fitted sub-estimators. base_sampler_ : :class:`~imblearn.under_sampling.RandomUnderSampler` The base sampler used to generate the subsequent samplers. samplers_ : list of :class:`~imblearn.under_sampling.RandomUnderSampler` The collection of fitted samplers. pipelines_ : list of Pipeline The collection of fitted pipelines (samplers + trees). classes_ : ndarray of shape (n_classes,) The classes labels. n_classes_ : int The number of classes. estimator_weights_ : ndarray of shape (n_estimator,) Weights for each estimator in the boosted ensemble. estimator_errors_ : ndarray of shape (n_estimator,) Classification error for each estimator in the boosted ensemble. feature_importances_ : ndarray of shape (n_features,) The feature importances if supported by the ``base_estimator``. n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.9 See Also -------- BalancedBaggingClassifier : Bagging classifier for which each base estimator is trained on a balanced bootstrap. BalancedRandomForestClassifier : Random forest applying random-under sampling to balance the different bootstraps. EasyEnsembleClassifier : Ensemble of AdaBoost classifier trained on balanced bootstraps. References ---------- .. [1] Seiffert, C., Khoshgoftaar, T. M., Van Hulse, J., & Napolitano, A. "RUSBoost: A hybrid approach to alleviating class imbalance." IEEE Transactions on Systems, Man, and Cybernetics-Part A: Systems and Humans 40.1 (2010): 185-197. Examples -------- >>> from imblearn.ensemble import RUSBoostClassifier >>> from sklearn.datasets import make_classification >>> >>> X, y = make_classification(n_samples=1000, n_classes=3, ... n_informative=4, weights=[0.2, 0.3, 0.5], ... random_state=0) >>> clf = RUSBoostClassifier(random_state=0) >>> clf.fit(X, y) RUSBoostClassifier(...) >>> clf.predict(X) array([...]) """ # make a deepcopy to not modify the original dictionary if sklearn_version >= parse_version("1.4"): _parameter_constraints = copy.deepcopy( AdaBoostClassifier._parameter_constraints ) else: _parameter_constraints = copy.deepcopy( _adaboost_classifier_parameter_constraints ) _parameter_constraints.update( { "algorithm": [ StrOptions({"SAMME", "SAMME.R"}), Hidden(StrOptions({"deprecated"})), ], "sampling_strategy": [ Interval(numbers.Real, 0, 1, closed="right"), StrOptions({"auto", "majority", "not minority", "not majority", "all"}), dict, callable, ], "replacement": ["boolean"], } ) # TODO: remove when minimum supported version of scikit-learn is 1.4 if "base_estimator" in _parameter_constraints: del _parameter_constraints["base_estimator"] def __init__( self, estimator=None, *, n_estimators=50, learning_rate=1.0, algorithm="deprecated", sampling_strategy="auto", replacement=False, random_state=None, ): super().__init__( n_estimators=n_estimators, learning_rate=learning_rate, random_state=random_state, ) self.algorithm = algorithm self.estimator = estimator self.sampling_strategy = sampling_strategy self.replacement = replacement @_fit_context(prefer_skip_nested_validation=False) def fit(self, X, y, sample_weight=None): """Build a boosted classifier from the training set (X, y). Parameters ---------- X : {array-like, sparse matrix} of shape (n_samples, n_features) The training input samples. Sparse matrix can be CSC, CSR, COO, DOK, or LIL. DOK and LIL are converted to CSR. y : array-like of shape (n_samples,) The target values (class labels). sample_weight : array-like of shape (n_samples,), default=None Sample weights. If None, the sample weights are initialized to ``1 / n_samples``. Returns ------- self : object Returns self. """ self._validate_params() check_target_type(y) self.samplers_ = [] self.pipelines_ = [] super().fit(X, y, sample_weight) return self def _validate_estimator(self): """Check the estimator and the n_estimator attribute. Sets the `estimator_` attributes. """ default = DecisionTreeClassifier(max_depth=1) if self.estimator is not None: self.estimator_ = clone(self.estimator) else: self.estimator_ = clone(default) # SAMME-R requires predict_proba-enabled estimators if self.algorithm == "SAMME.R": if not hasattr(self.estimator_, "predict_proba"): raise TypeError( "AdaBoostClassifier with algorithm='SAMME.R' requires " "that the weak learner supports the calculation of class " "probabilities with a predict_proba method.\n" "Please change the base estimator or set " "algorithm='SAMME' instead." ) if not has_fit_parameter(self.estimator_, "sample_weight"): raise ValueError( f"{self.estimator_.__class__.__name__} doesn't support sample_weight." ) self.base_sampler_ = RandomUnderSampler( sampling_strategy=self.sampling_strategy, replacement=self.replacement, ) def _make_sampler_estimator(self, append=True, random_state=None): """Make and configure a copy of the `base_estimator_` attribute. Warning: This method should be used to properly instantiate new sub-estimators. """ estimator = clone(self.estimator_) estimator.set_params(**{p: getattr(self, p) for p in self.estimator_params}) sampler = clone(self.base_sampler_) if random_state is not None: _set_random_states(estimator, random_state) _set_random_states(sampler, random_state) if append: self.estimators_.append(estimator) self.samplers_.append(sampler) self.pipelines_.append( make_pipeline(deepcopy(sampler), deepcopy(estimator)) ) return estimator, sampler def _boost_real(self, iboost, X, y, sample_weight, random_state): """Implement a single boost using the SAMME.R real algorithm.""" estimator, sampler = self._make_sampler_estimator(random_state=random_state) X_res, y_res = sampler.fit_resample(X, y) sample_weight_res = _safe_indexing(sample_weight, sampler.sample_indices_) estimator.fit(X_res, y_res, sample_weight=sample_weight_res) y_predict_proba = estimator.predict_proba(X) if iboost == 0: self.classes_ = getattr(estimator, "classes_", None) self.n_classes_ = len(self.classes_) y_predict = self.classes_.take(np.argmax(y_predict_proba, axis=1), axis=0) # Instances incorrectly classified incorrect = y_predict != y # Error fraction estimator_error = np.mean(np.average(incorrect, weights=sample_weight, axis=0)) # Stop if classification is perfect if estimator_error <= 0: return sample_weight, 1.0, 0.0 # Construct y coding as described in Zhu et al [2]: # # y_k = 1 if c == k else -1 / (K - 1) # # where K == n_classes_ and c, k in [0, K) are indices along the second # axis of the y coding with c being the index corresponding to the true # class label. n_classes = self.n_classes_ classes = self.classes_ y_codes = np.array([-1.0 / (n_classes - 1), 1.0]) y_coding = y_codes.take(classes == y[:, np.newaxis]) # Displace zero probabilities so the log is defined. # Also fix negative elements which may occur with # negative sample weights. proba = y_predict_proba # alias for readability np.clip(proba, np.finfo(proba.dtype).eps, None, out=proba) # Boost weight using multi-class AdaBoost SAMME.R alg estimator_weight = ( -1.0 * self.learning_rate * ((n_classes - 1.0) / n_classes) * (y_coding * np.log(y_predict_proba)).sum(axis=1) ) # Only boost the weights if it will fit again if not iboost == self.n_estimators - 1: # Only boost positive weights sample_weight *= np.exp( estimator_weight * ((sample_weight > 0) | (estimator_weight < 0)) ) return sample_weight, 1.0, estimator_error def _boost_discrete(self, iboost, X, y, sample_weight, random_state): """Implement a single boost using the SAMME discrete algorithm.""" estimator, sampler = self._make_sampler_estimator(random_state=random_state) X_res, y_res = sampler.fit_resample(X, y) sample_weight_res = _safe_indexing(sample_weight, sampler.sample_indices_) estimator.fit(X_res, y_res, sample_weight=sample_weight_res) y_predict = estimator.predict(X) if iboost == 0: self.classes_ = getattr(estimator, "classes_", None) self.n_classes_ = len(self.classes_) # Instances incorrectly classified incorrect = y_predict != y # Error fraction estimator_error = np.mean(np.average(incorrect, weights=sample_weight, axis=0)) # Stop if classification is perfect if estimator_error <= 0: return sample_weight, 1.0, 0.0 n_classes = self.n_classes_ # Stop if the error is at least as bad as random guessing if estimator_error >= 1.0 - (1.0 / n_classes): self.estimators_.pop(-1) self.samplers_.pop(-1) self.pipelines_.pop(-1) if len(self.estimators_) == 0: raise ValueError( "BaseClassifier in AdaBoostClassifier " "ensemble is worse than random, ensemble " "can not be fit." ) return None, None, None # Boost weight using multi-class AdaBoost SAMME alg estimator_weight = self.learning_rate * ( np.log((1.0 - estimator_error) / estimator_error) + np.log(n_classes - 1.0) ) # Only boost the weights if I will fit again if not iboost == self.n_estimators - 1: # Only boost positive weights sample_weight *= np.exp(estimator_weight * incorrect * (sample_weight > 0)) return sample_weight, estimator_weight, estimator_error # TODO(0.14): remove this method because algorithm is deprecated. def _boost(self, iboost, X, y, sample_weight, random_state): if self.algorithm != "deprecated": warnings.warn( ( "`algorithm` parameter is deprecated in 0.12 and will be removed in" " 0.14. In the future, the SAMME algorithm will always be used." ), FutureWarning, ) if self.algorithm == "SAMME.R": return self._boost_real(iboost, X, y, sample_weight, random_state) else: # elif self.algorithm == "SAMME": return self._boost_discrete(iboost, X, y, sample_weight, random_state) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/ensemble/tests/000077500000000000000000000000001521136113700257235ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/ensemble/tests/__init__.py000066400000000000000000000000001521136113700300220ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/ensemble/tests/test_bagging.py000066400000000000000000000445041521136113700307410ustar00rootroot00000000000000"""Test the module ensemble classifiers.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT from collections import Counter import numpy as np import pytest from sklearn.cluster import KMeans from sklearn.datasets import load_iris, make_classification, make_hastie_10_2 from sklearn.dummy import DummyClassifier from sklearn.feature_selection import SelectKBest from sklearn.linear_model import LogisticRegression, Perceptron from sklearn.model_selection import GridSearchCV, ParameterGrid, train_test_split from sklearn.neighbors import KNeighborsClassifier from sklearn.svm import SVC from sklearn.tree import DecisionTreeClassifier from sklearn.utils._testing import ( assert_allclose, assert_array_almost_equal, assert_array_equal, ) from imblearn import FunctionSampler from imblearn.datasets import make_imbalance from imblearn.ensemble import BalancedBaggingClassifier from imblearn.over_sampling import SMOTE, RandomOverSampler from imblearn.pipeline import make_pipeline from imblearn.under_sampling import ClusterCentroids, RandomUnderSampler iris = load_iris() @pytest.mark.parametrize( "estimator", [ None, DummyClassifier(strategy="prior"), Perceptron(max_iter=1000, tol=1e-3), DecisionTreeClassifier(), KNeighborsClassifier(), SVC(gamma="scale"), ], ) @pytest.mark.parametrize( "params", ParameterGrid( { "max_samples": [0.5, 1.0], "max_features": [1, 2, 4], "bootstrap": [True, False], "bootstrap_features": [True, False], } ), ) def test_balanced_bagging_classifier(estimator, params): # Check classification for various parameter settings. X, y = make_imbalance( iris.data, iris.target, sampling_strategy={0: 20, 1: 25, 2: 50}, random_state=0, ) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) bag = BalancedBaggingClassifier(estimator=estimator, random_state=0, **params).fit( X_train, y_train ) bag.predict(X_test) bag.predict_proba(X_test) bag.score(X_test, y_test) if hasattr(estimator, "decision_function"): bag.decision_function(X_test) def test_bootstrap_samples(): # Test that bootstrapping samples generate non-perfect base estimators. X, y = make_imbalance( iris.data, iris.target, sampling_strategy={0: 20, 1: 25, 2: 50}, random_state=0, ) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) estimator = DecisionTreeClassifier().fit(X_train, y_train) # without bootstrap, all trees are perfect on the training set # disable the resampling by passing an empty dictionary. ensemble = BalancedBaggingClassifier( estimator=DecisionTreeClassifier(), max_samples=1.0, bootstrap=False, n_estimators=10, sampling_strategy={}, random_state=0, ).fit(X_train, y_train) assert ensemble.score(X_train, y_train) == estimator.score(X_train, y_train) # with bootstrap, trees are no longer perfect on the training set ensemble = BalancedBaggingClassifier( estimator=DecisionTreeClassifier(), max_samples=1.0, bootstrap=True, random_state=0, ).fit(X_train, y_train) assert ensemble.score(X_train, y_train) < estimator.score(X_train, y_train) def test_bootstrap_features(): # Test that bootstrapping features may generate duplicate features. X, y = make_imbalance( iris.data, iris.target, sampling_strategy={0: 20, 1: 25, 2: 50}, random_state=0, ) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) ensemble = BalancedBaggingClassifier( estimator=DecisionTreeClassifier(), max_features=1.0, bootstrap_features=False, random_state=0, ).fit(X_train, y_train) for features in ensemble.estimators_features_: assert np.unique(features).shape[0] == X.shape[1] ensemble = BalancedBaggingClassifier( estimator=DecisionTreeClassifier(), max_features=1.0, bootstrap_features=True, random_state=0, ).fit(X_train, y_train) unique_features = [ np.unique(features).shape[0] for features in ensemble.estimators_features_ ] assert np.median(unique_features) < X.shape[1] def test_probability(): # Predict probabilities. X, y = make_imbalance( iris.data, iris.target, sampling_strategy={0: 20, 1: 25, 2: 50}, random_state=0, ) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) with np.errstate(divide="ignore", invalid="ignore"): # Normal case ensemble = BalancedBaggingClassifier( estimator=DecisionTreeClassifier(), random_state=0 ).fit(X_train, y_train) assert_array_almost_equal( np.sum(ensemble.predict_proba(X_test), axis=1), np.ones(len(X_test)), ) assert_array_almost_equal( ensemble.predict_proba(X_test), np.exp(ensemble.predict_log_proba(X_test)), ) # Degenerate case, where some classes are missing ensemble = BalancedBaggingClassifier( estimator=LogisticRegression(solver="lbfgs"), random_state=0, max_samples=5, ) ensemble.fit(X_train, y_train) assert_array_almost_equal( np.sum(ensemble.predict_proba(X_test), axis=1), np.ones(len(X_test)), ) assert_array_almost_equal( ensemble.predict_proba(X_test), np.exp(ensemble.predict_log_proba(X_test)), ) def test_oob_score_classification(): # Check that oob prediction is a good estimation of the generalization # error. X, y = make_imbalance( iris.data, iris.target, sampling_strategy={0: 20, 1: 25, 2: 50}, random_state=0, ) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) for estimator in [DecisionTreeClassifier(), SVC(gamma="scale")]: clf = BalancedBaggingClassifier( estimator=estimator, n_estimators=100, bootstrap=True, oob_score=True, random_state=0, ).fit(X_train, y_train) test_score = clf.score(X_test, y_test) assert abs(test_score - clf.oob_score_) < 0.1 # Test with few estimators with pytest.warns(UserWarning): BalancedBaggingClassifier( estimator=estimator, n_estimators=1, bootstrap=True, oob_score=True, random_state=0, ).fit(X_train, y_train) def test_single_estimator(): # Check singleton ensembles. X, y = make_imbalance( iris.data, iris.target, sampling_strategy={0: 20, 1: 25, 2: 50}, random_state=0, ) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) clf1 = BalancedBaggingClassifier( estimator=KNeighborsClassifier(), n_estimators=1, bootstrap=False, bootstrap_features=False, random_state=0, ).fit(X_train, y_train) clf2 = make_pipeline( RandomUnderSampler(random_state=clf1.estimators_[0].steps[0][1].random_state), KNeighborsClassifier(), ).fit(X_train, y_train) assert_array_equal(clf1.predict(X_test), clf2.predict(X_test)) def test_gridsearch(): # Check that bagging ensembles can be grid-searched. # Transform iris into a binary classification task X, y = iris.data, iris.target.copy() y[y == 2] = 1 # Grid search with scoring based on decision_function parameters = {"n_estimators": (1, 2), "estimator__C": (1, 2)} GridSearchCV( BalancedBaggingClassifier(SVC(gamma="scale")), parameters, cv=3, scoring="roc_auc", ).fit(X, y) def test_estimator(): # Check estimator and its default values. X, y = make_imbalance( iris.data, iris.target, sampling_strategy={0: 20, 1: 25, 2: 50}, random_state=0, ) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) ensemble = BalancedBaggingClassifier(None, n_jobs=3, random_state=0).fit( X_train, y_train ) assert isinstance(ensemble.estimator_.steps[-1][1], DecisionTreeClassifier) ensemble = BalancedBaggingClassifier( DecisionTreeClassifier(), n_jobs=3, random_state=0 ).fit(X_train, y_train) assert isinstance(ensemble.estimator_.steps[-1][1], DecisionTreeClassifier) ensemble = BalancedBaggingClassifier( Perceptron(max_iter=1000, tol=1e-3), n_jobs=3, random_state=0 ).fit(X_train, y_train) assert isinstance(ensemble.estimator_.steps[-1][1], Perceptron) def test_bagging_with_pipeline(): X, y = make_imbalance( iris.data, iris.target, sampling_strategy={0: 20, 1: 25, 2: 50}, random_state=0, ) estimator = BalancedBaggingClassifier( make_pipeline(SelectKBest(k=1), DecisionTreeClassifier()), max_features=2, ) estimator.fit(X, y).predict(X) def test_warm_start(random_state=42): # Test if fitting incrementally with warm start gives a forest of the # right size and the same results as a normal fit. X, y = make_hastie_10_2(n_samples=20, random_state=1) clf_ws = None for n_estimators in [5, 10]: if clf_ws is None: clf_ws = BalancedBaggingClassifier( n_estimators=n_estimators, random_state=random_state, warm_start=True, ) else: clf_ws.set_params(n_estimators=n_estimators) clf_ws.fit(X, y) assert len(clf_ws) == n_estimators clf_no_ws = BalancedBaggingClassifier( n_estimators=10, random_state=random_state, warm_start=False ) clf_no_ws.fit(X, y) assert {pipe.steps[-1][1].random_state for pipe in clf_ws} == { pipe.steps[-1][1].random_state for pipe in clf_no_ws } def test_warm_start_smaller_n_estimators(): # Test if warm start'ed second fit with smaller n_estimators raises error. X, y = make_hastie_10_2(n_samples=20, random_state=1) clf = BalancedBaggingClassifier(n_estimators=5, warm_start=True) clf.fit(X, y) clf.set_params(n_estimators=4) with pytest.raises(ValueError): clf.fit(X, y) def test_warm_start_equal_n_estimators(): # Test that nothing happens when fitting without increasing n_estimators X, y = make_hastie_10_2(n_samples=20, random_state=1) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=43) clf = BalancedBaggingClassifier(n_estimators=5, warm_start=True, random_state=83) clf.fit(X_train, y_train) y_pred = clf.predict(X_test) # modify X to nonsense values, this should not change anything X_train += 1.0 warn_msg = "Warm-start fitting without increasing n_estimators does not" with pytest.warns(UserWarning, match=warn_msg): clf.fit(X_train, y_train) assert_array_equal(y_pred, clf.predict(X_test)) def test_warm_start_equivalence(): # warm started classifier with 5+5 estimators should be equivalent to # one classifier with 10 estimators X, y = make_hastie_10_2(n_samples=20, random_state=1) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=43) clf_ws = BalancedBaggingClassifier( n_estimators=5, warm_start=True, random_state=3141 ) clf_ws.fit(X_train, y_train) clf_ws.set_params(n_estimators=10) clf_ws.fit(X_train, y_train) y1 = clf_ws.predict(X_test) clf = BalancedBaggingClassifier( n_estimators=10, warm_start=False, random_state=3141 ) clf.fit(X_train, y_train) y2 = clf.predict(X_test) assert_array_almost_equal(y1, y2) def test_warm_start_with_oob_score_fails(): # Check using oob_score and warm_start simultaneously fails X, y = make_hastie_10_2(n_samples=20, random_state=1) clf = BalancedBaggingClassifier(n_estimators=5, warm_start=True, oob_score=True) with pytest.raises(ValueError): clf.fit(X, y) def test_oob_score_removed_on_warm_start(): X, y = make_hastie_10_2(n_samples=2000, random_state=1) clf = BalancedBaggingClassifier(n_estimators=50, oob_score=True) clf.fit(X, y) clf.set_params(warm_start=True, oob_score=False, n_estimators=100) clf.fit(X, y) with pytest.raises(AttributeError): getattr(clf, "oob_score_") def test_oob_score_consistency(): # Make sure OOB scores are identical when random_state, estimator, and # training data are fixed and fitting is done twice X, y = make_hastie_10_2(n_samples=200, random_state=1) bagging = BalancedBaggingClassifier( KNeighborsClassifier(), max_samples=0.5, max_features=0.5, oob_score=True, random_state=1, ) assert bagging.fit(X, y).oob_score_ == bagging.fit(X, y).oob_score_ def test_estimators_samples(): # Check that format of estimators_samples_ is correct and that results # generated at fit time can be identically reproduced at a later time # using data saved in object attributes. X, y = make_hastie_10_2(n_samples=200, random_state=1) # remap the y outside of the BalancedBaggingclassifier # _, y = np.unique(y, return_inverse=True) bagging = BalancedBaggingClassifier( LogisticRegression(), max_samples=0.5, max_features=0.5, random_state=1, bootstrap=False, ) bagging.fit(X, y) # Get relevant attributes estimators_samples = bagging.estimators_samples_ estimators_features = bagging.estimators_features_ estimators = bagging.estimators_ # Test for correct formatting assert len(estimators_samples) == len(estimators) assert len(estimators_samples[0]) == len(X) // 2 assert estimators_samples[0].dtype.kind == "i" # Re-fit single estimator to test for consistent sampling estimator_index = 0 estimator_samples = estimators_samples[estimator_index] estimator_features = estimators_features[estimator_index] estimator = estimators[estimator_index] X_train = (X[estimator_samples])[:, estimator_features] y_train = y[estimator_samples] orig_coefs = estimator.steps[-1][1].coef_ estimator.fit(X_train, y_train) new_coefs = estimator.steps[-1][1].coef_ assert_allclose(orig_coefs, new_coefs) def test_max_samples_consistency(): # Make sure validated max_samples and original max_samples are identical # when valid integer max_samples supplied by user max_samples = 100 X, y = make_hastie_10_2(n_samples=2 * max_samples, random_state=1) bagging = BalancedBaggingClassifier( KNeighborsClassifier(), max_samples=max_samples, max_features=0.5, random_state=1, ) bagging.fit(X, y) assert bagging._max_samples == max_samples class CountDecisionTreeClassifier(DecisionTreeClassifier): """DecisionTreeClassifier that will memorize the number of samples seen at fit.""" def fit(self, X, y, sample_weight=None): self.class_counts_ = Counter(y) return super().fit(X, y, sample_weight=sample_weight) @pytest.mark.filterwarnings("ignore:Number of distinct clusters") @pytest.mark.parametrize( "sampler, n_samples_bootstrap", [ (None, 15), (RandomUnderSampler(), 15), # under-sampling with sample_indices_ ( ClusterCentroids(estimator=KMeans(n_init=1)), 15, ), # under-sampling without sample_indices_ (RandomOverSampler(), 40), # over-sampling with sample_indices_ (SMOTE(), 40), # over-sampling without sample_indices_ ], ) def test_balanced_bagging_classifier_samplers(sampler, n_samples_bootstrap): # check that we can pass any kind of sampler to a bagging classifier X, y = make_imbalance( iris.data, iris.target, sampling_strategy={0: 20, 1: 25, 2: 50}, random_state=0, ) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) clf = BalancedBaggingClassifier( estimator=CountDecisionTreeClassifier(), n_estimators=2, sampler=sampler, random_state=0, ) clf.fit(X_train, y_train) clf.predict(X_test) # check that we have balanced class with the right counts of class # sample depending on the sampling strategy assert_array_equal( list(clf.estimators_[0][-1].class_counts_.values()), n_samples_bootstrap ) @pytest.mark.parametrize("replace", [True, False]) def test_balanced_bagging_classifier_with_function_sampler(replace): # check that we can provide a FunctionSampler in BalancedBaggingClassifier X, y = make_classification( n_samples=1_000, n_features=10, n_classes=2, weights=[0.3, 0.7], random_state=0, ) def roughly_balanced_bagging(X, y, replace=False): """Implementation of Roughly Balanced Bagging for binary problem.""" # find the minority and majority classes class_counts = Counter(y) majority_class = max(class_counts, key=class_counts.get) minority_class = min(class_counts, key=class_counts.get) # compute the number of sample to draw from the majority class using # a negative binomial distribution n_minority_class = class_counts[minority_class] n_majority_resampled = np.random.negative_binomial(n=n_minority_class, p=0.5) # draw randomly with or without replacement majority_indices = np.random.choice( np.flatnonzero(y == majority_class), size=n_majority_resampled, replace=replace, ) minority_indices = np.random.choice( np.flatnonzero(y == minority_class), size=n_minority_class, replace=replace, ) indices = np.hstack([majority_indices, minority_indices]) return X[indices], y[indices] # Roughly Balanced Bagging rbb = BalancedBaggingClassifier( estimator=CountDecisionTreeClassifier(random_state=0), n_estimators=2, sampler=FunctionSampler( func=roughly_balanced_bagging, kw_args={"replace": replace} ), random_state=0, ) rbb.fit(X, y) for estimator in rbb.estimators_: class_counts = estimator[-1].class_counts_ assert (class_counts[0] / class_counts[1]) > 0.78 scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/ensemble/tests/test_easy_ensemble.py000066400000000000000000000155711521136113700321600ustar00rootroot00000000000000"""Test the module easy ensemble.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numpy as np import pytest from sklearn.datasets import load_iris, make_hastie_10_2 from sklearn.ensemble import AdaBoostClassifier, GradientBoostingClassifier from sklearn.feature_selection import SelectKBest from sklearn.model_selection import GridSearchCV, train_test_split from sklearn.utils._testing import assert_allclose, assert_array_equal from imblearn.datasets import make_imbalance from imblearn.ensemble import EasyEnsembleClassifier from imblearn.pipeline import make_pipeline from imblearn.under_sampling import RandomUnderSampler iris = load_iris() # Generate a global dataset to use RND_SEED = 0 X = np.array( [ [0.5220963, 0.11349303], [0.59091459, 0.40692742], [1.10915364, 0.05718352], [0.22039505, 0.26469445], [1.35269503, 0.44812421], [0.85117925, 1.0185556], [-2.10724436, 0.70263997], [-0.23627356, 0.30254174], [-1.23195149, 0.15427291], [-0.58539673, 0.62515052], ] ) Y = np.array([1, 2, 2, 2, 1, 0, 1, 1, 1, 0]) @pytest.mark.parametrize("n_estimators", [10, 20]) @pytest.mark.parametrize( "estimator", [ GradientBoostingClassifier(n_estimators=5), GradientBoostingClassifier(n_estimators=10), ], ) def test_easy_ensemble_classifier(n_estimators, estimator): # Check classification for various parameter settings. X, y = make_imbalance( iris.data, iris.target, sampling_strategy={0: 20, 1: 25, 2: 50}, random_state=0, ) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) eec = EasyEnsembleClassifier( n_estimators=n_estimators, estimator=estimator, n_jobs=-1, random_state=RND_SEED, ) eec.fit(X_train, y_train).score(X_test, y_test) assert len(eec.estimators_) == n_estimators for est in eec.estimators_: assert len(est.named_steps["classifier"]) == estimator.n_estimators # test the different prediction function eec.predict(X_test) eec.predict_proba(X_test) eec.predict_log_proba(X_test) eec.decision_function(X_test) def test_estimator(): # Check estimator and its default values. X, y = make_imbalance( iris.data, iris.target, sampling_strategy={0: 20, 1: 25, 2: 50}, random_state=0, ) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) ensemble = EasyEnsembleClassifier(2, None, n_jobs=-1, random_state=0).fit( X_train, y_train ) assert isinstance(ensemble.estimator_.steps[-1][1], AdaBoostClassifier) ensemble = EasyEnsembleClassifier( 2, GradientBoostingClassifier(), n_jobs=-1, random_state=0 ).fit(X_train, y_train) assert isinstance(ensemble.estimator_.steps[-1][1], GradientBoostingClassifier) def test_bagging_with_pipeline(): X, y = make_imbalance( iris.data, iris.target, sampling_strategy={0: 20, 1: 25, 2: 50}, random_state=0, ) estimator = EasyEnsembleClassifier( n_estimators=2, estimator=make_pipeline(SelectKBest(k=1), GradientBoostingClassifier()), ) estimator.fit(X, y).predict(X) def test_warm_start(random_state=42): # Test if fitting incrementally with warm start gives a forest of the # right size and the same results as a normal fit. X, y = make_hastie_10_2(n_samples=20, random_state=1) clf_ws = None for n_estimators in [5, 10]: if clf_ws is None: clf_ws = EasyEnsembleClassifier( n_estimators=n_estimators, random_state=random_state, warm_start=True, ) else: clf_ws.set_params(n_estimators=n_estimators) clf_ws.fit(X, y) assert len(clf_ws) == n_estimators clf_no_ws = EasyEnsembleClassifier( n_estimators=10, random_state=random_state, warm_start=False ) clf_no_ws.fit(X, y) assert {pipe.steps[-1][1].random_state for pipe in clf_ws} == { pipe.steps[-1][1].random_state for pipe in clf_no_ws } def test_warm_start_smaller_n_estimators(): # Test if warm start'ed second fit with smaller n_estimators raises error. X, y = make_hastie_10_2(n_samples=20, random_state=1) clf = EasyEnsembleClassifier(n_estimators=5, warm_start=True) clf.fit(X, y) clf.set_params(n_estimators=4) with pytest.raises(ValueError): clf.fit(X, y) def test_warm_start_equal_n_estimators(): # Test that nothing happens when fitting without increasing n_estimators X, y = make_hastie_10_2(n_samples=20, random_state=1) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=43) clf = EasyEnsembleClassifier(n_estimators=5, warm_start=True, random_state=83) clf.fit(X_train, y_train) y_pred = clf.predict(X_test) # modify X to nonsense values, this should not change anything X_train += 1.0 warn_msg = "Warm-start fitting without increasing n_estimators" with pytest.warns(UserWarning, match=warn_msg): clf.fit(X_train, y_train) assert_array_equal(y_pred, clf.predict(X_test)) def test_warm_start_equivalence(): # warm started classifier with 5+5 estimators should be equivalent to # one classifier with 10 estimators X, y = make_hastie_10_2(n_samples=20, random_state=1) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=43) clf_ws = EasyEnsembleClassifier(n_estimators=5, warm_start=True, random_state=3141) clf_ws.fit(X_train, y_train) clf_ws.set_params(n_estimators=10) clf_ws.fit(X_train, y_train) y1 = clf_ws.predict(X_test) clf = EasyEnsembleClassifier(n_estimators=10, warm_start=False, random_state=3141) clf.fit(X_train, y_train) y2 = clf.predict(X_test) assert_allclose(y1, y2) def test_easy_ensemble_classifier_single_estimator(): X, y = make_imbalance( iris.data, iris.target, sampling_strategy={0: 20, 1: 25, 2: 50}, random_state=0, ) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) clf1 = EasyEnsembleClassifier(n_estimators=1, random_state=0).fit(X_train, y_train) clf2 = make_pipeline( RandomUnderSampler(random_state=0), GradientBoostingClassifier(random_state=0), ).fit(X_train, y_train) assert_array_equal(clf1.predict(X_test), clf2.predict(X_test)) def test_easy_ensemble_classifier_grid_search(): X, y = make_imbalance( iris.data, iris.target, sampling_strategy={0: 20, 1: 25, 2: 50}, random_state=0, ) parameters = { "n_estimators": [1, 2], "estimator__n_estimators": [3, 4], } grid_search = GridSearchCV( EasyEnsembleClassifier(estimator=GradientBoostingClassifier()), parameters, cv=5, ) grid_search.fit(X, y) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/ensemble/tests/test_forest.py000066400000000000000000000234711521136113700306450ustar00rootroot00000000000000import numpy as np import pytest from sklearn.datasets import make_classification from sklearn.model_selection import GridSearchCV, train_test_split from sklearn.utils._testing import assert_allclose, assert_array_equal from sklearn.utils.fixes import parse_version from sklearn_compat._sklearn_compat import sklearn_version from imblearn.ensemble import BalancedRandomForestClassifier @pytest.fixture def imbalanced_dataset(): return make_classification( n_samples=10000, n_features=2, n_informative=2, n_redundant=0, n_repeated=0, n_classes=3, n_clusters_per_class=1, weights=[0.01, 0.05, 0.94], class_sep=0.8, random_state=0, ) def test_balanced_random_forest_error_warning_warm_start(imbalanced_dataset): brf = BalancedRandomForestClassifier( n_estimators=5, sampling_strategy="all", replacement=True, bootstrap=False ) brf.fit(*imbalanced_dataset) with pytest.raises(ValueError, match="must be larger or equal to"): brf.set_params(warm_start=True, n_estimators=2) brf.fit(*imbalanced_dataset) brf.set_params(n_estimators=10) brf.fit(*imbalanced_dataset) with pytest.warns(UserWarning, match="Warm-start fitting without"): brf.fit(*imbalanced_dataset) def test_balanced_random_forest(imbalanced_dataset): n_estimators = 10 brf = BalancedRandomForestClassifier( n_estimators=n_estimators, random_state=0, sampling_strategy="all", replacement=True, bootstrap=False, ) brf.fit(*imbalanced_dataset) assert len(brf.samplers_) == n_estimators assert len(brf.estimators_) == n_estimators assert len(brf.pipelines_) == n_estimators assert len(brf.feature_importances_) == imbalanced_dataset[0].shape[1] def test_balanced_random_forest_attributes(imbalanced_dataset): X, y = imbalanced_dataset n_estimators = 10 brf = BalancedRandomForestClassifier( n_estimators=n_estimators, random_state=0, sampling_strategy="all", replacement=True, bootstrap=False, ) brf.fit(X, y) for idx in range(n_estimators): X_res, y_res = brf.samplers_[idx].fit_resample(X, y) X_res_2, y_res_2 = ( brf.pipelines_[idx].named_steps["randomundersampler"].fit_resample(X, y) ) assert_allclose(X_res, X_res_2) assert_array_equal(y_res, y_res_2) y_pred = brf.estimators_[idx].fit(X_res, y_res).predict(X) y_pred_2 = brf.pipelines_[idx].fit(X, y).predict(X) assert_array_equal(y_pred, y_pred_2) y_pred = brf.estimators_[idx].fit(X_res, y_res).predict_proba(X) y_pred_2 = brf.pipelines_[idx].fit(X, y).predict_proba(X) assert_array_equal(y_pred, y_pred_2) def test_balanced_random_forest_sample_weight(imbalanced_dataset): rng = np.random.RandomState(42) X, y = imbalanced_dataset sample_weight = rng.rand(y.shape[0]) brf = BalancedRandomForestClassifier( n_estimators=5, random_state=0, sampling_strategy="all", replacement=True, bootstrap=False, ) brf.fit(X, y, sample_weight) @pytest.mark.filterwarnings("ignore:Some inputs do not have OOB scores") def test_balanced_random_forest_oob(imbalanced_dataset): X, y = imbalanced_dataset X_train, X_test, y_train, y_test = train_test_split( X, y, random_state=42, stratify=y ) est = BalancedRandomForestClassifier( oob_score=True, random_state=0, n_estimators=1000, min_samples_leaf=2, sampling_strategy="all", replacement=True, bootstrap=True, ) est.fit(X_train, y_train) test_score = est.score(X_test, y_test) assert abs(test_score - est.oob_score_) < 0.1 # Check warning if not enough estimators est = BalancedRandomForestClassifier( oob_score=True, random_state=0, n_estimators=1, bootstrap=True, sampling_strategy="all", replacement=True, ) with pytest.warns(UserWarning) and np.errstate(divide="ignore", invalid="ignore"): est.fit(X, y) def test_balanced_random_forest_grid_search(imbalanced_dataset): brf = BalancedRandomForestClassifier( sampling_strategy="all", replacement=True, bootstrap=False ) grid = GridSearchCV(brf, {"n_estimators": (1, 2), "max_depth": (1, 2)}, cv=3) grid.fit(*imbalanced_dataset) def test_little_tree_with_small_max_samples(): rng = np.random.RandomState(1) X = rng.randn(10000, 2) y = rng.randn(10000) > 0 # First fit with no restriction on max samples est1 = BalancedRandomForestClassifier( n_estimators=1, random_state=rng, max_samples=None, sampling_strategy="all", replacement=True, bootstrap=True, ) # Second fit with max samples restricted to just 2 est2 = BalancedRandomForestClassifier( n_estimators=1, random_state=rng, max_samples=2, sampling_strategy="all", replacement=True, bootstrap=True, ) est1.fit(X, y) est2.fit(X, y) tree1 = est1.estimators_[0].tree_ tree2 = est2.estimators_[0].tree_ msg = "Tree without `max_samples` restriction should have more nodes" assert tree1.node_count > tree2.node_count, msg def test_balanced_random_forest_pruning(imbalanced_dataset): brf = BalancedRandomForestClassifier( sampling_strategy="all", replacement=True, bootstrap=False ) brf.fit(*imbalanced_dataset) n_nodes_no_pruning = brf.estimators_[0].tree_.node_count brf_pruned = BalancedRandomForestClassifier( ccp_alpha=0.015, sampling_strategy="all", replacement=True, bootstrap=False ) brf_pruned.fit(*imbalanced_dataset) n_nodes_pruning = brf_pruned.estimators_[0].tree_.node_count assert n_nodes_no_pruning > n_nodes_pruning @pytest.mark.parametrize("ratio", [0.5, 0.1]) @pytest.mark.filterwarnings("ignore:Some inputs do not have OOB scores") def test_balanced_random_forest_oob_binomial(ratio): # Regression test for #655: check that the oob score is closed to 0.5 # a binomial experiment. rng = np.random.RandomState(42) n_samples = 1000 X = np.arange(n_samples).reshape(-1, 1) y = rng.binomial(1, ratio, size=n_samples) erf = BalancedRandomForestClassifier( oob_score=True, random_state=42, sampling_strategy="not minority", replacement=False, bootstrap=True, ) erf.fit(X, y) assert np.abs(erf.oob_score_ - 0.5) < 0.1 @pytest.mark.skipif( parse_version(sklearn_version.base_version) < parse_version("1.4"), reason="scikit-learn should be >= 1.4", ) def test_missing_values_is_resilient(): """Check that forest can deal with missing values and has decent performance.""" rng = np.random.RandomState(0) n_samples, n_features = 1000, 10 X, y = make_classification( n_samples=n_samples, n_features=n_features, random_state=rng ) # Create dataset with missing values X_missing = X.copy() X_missing[rng.choice([False, True], size=X.shape, p=[0.95, 0.05])] = np.nan assert np.isnan(X_missing).any() X_missing_train, X_missing_test, y_train, y_test = train_test_split( X_missing, y, random_state=0 ) # Train forest with missing values forest_with_missing = BalancedRandomForestClassifier( sampling_strategy="all", replacement=True, bootstrap=False, random_state=rng, n_estimators=50, ) forest_with_missing.fit(X_missing_train, y_train) score_with_missing = forest_with_missing.score(X_missing_test, y_test) # Train forest without missing values X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0) forest = BalancedRandomForestClassifier( sampling_strategy="all", replacement=True, bootstrap=False, random_state=rng, n_estimators=50, ) forest.fit(X_train, y_train) score_without_missing = forest.score(X_test, y_test) # Score is still 80 percent of the forest's score that had no missing values assert score_with_missing >= 0.80 * score_without_missing @pytest.mark.skipif( parse_version(sklearn_version.base_version) < parse_version("1.4"), reason="scikit-learn should be >= 1.4", ) def test_missing_value_is_predictive(): """Check that the forest learns when missing values are only present for a predictive feature.""" rng = np.random.RandomState(0) n_samples = 300 X_non_predictive = rng.standard_normal(size=(n_samples, 10)) y = rng.randint(0, high=2, size=n_samples) # Create a predictive feature using `y` and with some noise X_random_mask = rng.choice([False, True], size=n_samples, p=[0.95, 0.05]) y_mask = y.astype(bool) y_mask[X_random_mask] = ~y_mask[X_random_mask] predictive_feature = rng.standard_normal(size=n_samples) predictive_feature[y_mask] = np.nan assert np.isnan(predictive_feature).any() X_predictive = X_non_predictive.copy() X_predictive[:, 5] = predictive_feature ( X_predictive_train, X_predictive_test, X_non_predictive_train, X_non_predictive_test, y_train, y_test, ) = train_test_split(X_predictive, X_non_predictive, y, random_state=0) forest_predictive = BalancedRandomForestClassifier( sampling_strategy="all", replacement=True, bootstrap=False, random_state=0 ).fit(X_predictive_train, y_train) forest_non_predictive = BalancedRandomForestClassifier( sampling_strategy="all", replacement=True, bootstrap=False, random_state=0 ).fit(X_non_predictive_train, y_train) predictive_test_score = forest_predictive.score(X_predictive_test, y_test) assert predictive_test_score >= 0.75 assert predictive_test_score >= forest_non_predictive.score( X_non_predictive_test, y_test ) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/ensemble/tests/test_weight_boosting.py000066400000000000000000000061731521136113700325360ustar00rootroot00000000000000import numpy as np import pytest from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.utils._testing import assert_array_equal from imblearn.ensemble import RUSBoostClassifier @pytest.fixture def imbalanced_dataset(): return make_classification( n_samples=10000, n_features=3, n_informative=2, n_redundant=0, n_repeated=0, n_classes=3, n_clusters_per_class=1, weights=[0.01, 0.05, 0.94], class_sep=0.8, random_state=0, ) def test_rusboost(imbalanced_dataset): X, y = imbalanced_dataset X_train, X_test, y_train, y_test = train_test_split( X, y, stratify=y, random_state=1 ) classes = np.unique(y) n_estimators = 500 rusboost = RUSBoostClassifier(n_estimators=n_estimators, random_state=0) rusboost.fit(X_train, y_train) assert_array_equal(classes, rusboost.classes_) # check that we have an ensemble of samplers and estimators with a # consistent size assert len(rusboost.estimators_) > 1 assert len(rusboost.estimators_) == len(rusboost.samplers_) assert len(rusboost.pipelines_) == len(rusboost.samplers_) # each sampler in the ensemble should have different random state assert len({sampler.random_state for sampler in rusboost.samplers_}) == len( rusboost.samplers_ ) # each estimator in the ensemble should have different random state assert len({est.random_state for est in rusboost.estimators_}) == len( rusboost.estimators_ ) # check the consistency of the feature importances assert len(rusboost.feature_importances_) == imbalanced_dataset[0].shape[1] # check the consistency of the prediction outpus y_pred = rusboost.predict_proba(X_test) assert y_pred.shape[1] == len(classes) assert rusboost.decision_function(X_test).shape[1] == len(classes) score = rusboost.score(X_test, y_test) assert score > 0.6, f"Failed with score {score}" y_pred = rusboost.predict(X_test) assert y_pred.shape == y_test.shape def test_rusboost_sample_weight(imbalanced_dataset): X, y = imbalanced_dataset sample_weight = np.ones_like(y) rusboost = RUSBoostClassifier(random_state=0) # Predictions should be the same when sample_weight are all ones y_pred_sample_weight = rusboost.fit(X, y, sample_weight).predict(X) y_pred_no_sample_weight = rusboost.fit(X, y).predict(X) assert_array_equal(y_pred_sample_weight, y_pred_no_sample_weight) rng = np.random.RandomState(42) sample_weight = rng.rand(y.shape[0]) y_pred_sample_weight = rusboost.fit(X, y, sample_weight).predict(X) with pytest.raises(AssertionError): assert_array_equal(y_pred_no_sample_weight, y_pred_sample_weight) @pytest.mark.parametrize("algorithm", ["SAMME", "SAMME.R"]) def test_rusboost_algorithm(imbalanced_dataset, algorithm): X, y = imbalanced_dataset rusboost = RUSBoostClassifier(algorithm=algorithm) warn_msg = "`algorithm` parameter is deprecated in 0.12 and will be removed" with pytest.warns(FutureWarning, match=warn_msg): rusboost.fit(X, y) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/exceptions.py000066400000000000000000000014211521136113700255200ustar00rootroot00000000000000""" The :mod:`imblearn.exceptions` module includes all custom warnings and error classes and functions used across imbalanced-learn. """ # Authors: Guillaume Lemaitre # License: MIT def raise_isinstance_error(variable_name, possible_type, variable): """Raise consistent error message for isinstance() function. Parameters ---------- variable_name : str The name of the variable. possible_type : type The possible type of the variable. variable : object The variable to check. Raises ------ ValueError If the instance is not of the possible type. """ raise ValueError( f"{variable_name} has to be one of {possible_type}. " f"Got {type(variable)} instead." ) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/keras/000077500000000000000000000000001521136113700240745ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/keras/__init__.py000066400000000000000000000003671521136113700262130ustar00rootroot00000000000000"""The :mod:`imblearn.keras` provides utilities to deal with imbalanced dataset in keras.""" from imblearn.keras._generator import BalancedBatchGenerator, balanced_batch_generator __all__ = ["BalancedBatchGenerator", "balanced_batch_generator"] scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/keras/_generator.py000066400000000000000000000240541521136113700266000ustar00rootroot00000000000000"""Implement generators for ``keras`` which will balance the data.""" # This is a trick to avoid an error during tests collection with pytest. We # avoid the error when importing the package raise the error at the moment of # creating the instance. # This is a trick to avoid an error during tests collection with pytest. We # avoid the error when importing the package raise the error at the moment of # creating the instance. def import_keras(): """Try to import keras from keras and tensorflow. This is possible to import the sequence from keras or tensorflow. """ def import_from_keras(): try: import keras # noqa if hasattr(keras.utils, "Sequence"): return (keras.utils.Sequence,), True else: return (keras.utils.PyDataset,), True except ImportError: return tuple(), False def import_from_tensforflow(): try: from tensorflow import keras if hasattr(keras.utils, "Sequence"): return (keras.utils.Sequence,), True else: return (keras.utils.PyDataset,), True except ImportError: return tuple(), False ParentClassKeras, has_keras_k = import_from_keras() ParentClassTensorflow, has_keras_tf = import_from_tensforflow() has_keras = has_keras_k or has_keras_tf if has_keras: if has_keras_k: ParentClass = ParentClassKeras else: ParentClass = ParentClassTensorflow else: ParentClass = (object,) return ParentClass, has_keras ParentClass, HAS_KERAS = import_keras() from scipy.sparse import issparse # noqa from sklearn.base import clone # noqa from sklearn.utils import _safe_indexing # noqa from sklearn.utils import check_random_state # noqa from imblearn.tensorflow import balanced_batch_generator as tf_bbg # noqa from imblearn.under_sampling import RandomUnderSampler # noqa from imblearn.utils import Substitution # noqa from imblearn.utils._docstring import _random_state_docstring # noqa class BalancedBatchGenerator(*ParentClass): # type: ignore """Create balanced batches when training a keras model. Create a keras ``Sequence`` which is given to ``fit``. The sampler defines the sampling strategy used to balance the dataset ahead of creating the batch. The sampler should have an attribute ``sample_indices_``. .. versionadded:: 0.4 Parameters ---------- X : ndarray of shape (n_samples, n_features) Original imbalanced dataset. y : ndarray of shape (n_samples,) or (n_samples, n_classes) Associated targets. sample_weight : ndarray of shape (n_samples,) Sample weight. sampler : sampler object, default=None A sampler instance which has an attribute ``sample_indices_``. By default, the sampler used is a :class:`~imblearn.under_sampling.RandomUnderSampler`. batch_size : int, default=32 Number of samples per gradient update. keep_sparse : bool, default=False Either or not to conserve or not the sparsity of the input (i.e. ``X``, ``y``, ``sample_weight``). By default, the returned batches will be dense. random_state : int, RandomState instance or None, default=None Control the randomization of the algorithm: - If int, ``random_state`` is the seed used by the random number generator; - If ``RandomState`` instance, random_state is the random number generator; - If ``None``, the random number generator is the ``RandomState`` instance used by ``np.random``. Attributes ---------- sampler_ : sampler object The sampler used to balance the dataset. indices_ : ndarray of shape (n_samples, n_features) The indices of the samples selected during sampling. Examples -------- >>> from sklearn.datasets import load_iris >>> iris = load_iris() >>> from imblearn.datasets import make_imbalance >>> class_dict = dict() >>> class_dict[0] = 30; class_dict[1] = 50; class_dict[2] = 40 >>> X, y = make_imbalance(iris.data, iris.target, sampling_strategy=class_dict) >>> import tensorflow >>> y = tensorflow.keras.utils.to_categorical(y, 3) >>> model = tensorflow.keras.models.Sequential() >>> model.add( ... tensorflow.keras.layers.Dense( ... y.shape[1], input_dim=X.shape[1], activation='softmax' ... ) ... ) >>> model.compile(optimizer='sgd', loss='categorical_crossentropy', ... metrics=['accuracy']) >>> from imblearn.keras import BalancedBatchGenerator >>> from imblearn.under_sampling import NearMiss >>> training_generator = BalancedBatchGenerator( ... X, y, sampler=NearMiss(), batch_size=10, random_state=42) >>> callback_history = model.fit(training_generator, epochs=10, verbose=0) """ # flag for keras sequence duck-typing use_sequence_api = True def __init__( self, X, y, *, sample_weight=None, sampler=None, batch_size=32, keep_sparse=False, random_state=None, ): if not HAS_KERAS: raise ImportError("'No module named 'keras'") self.X = X self.y = y self.sample_weight = sample_weight self.sampler = sampler self.batch_size = batch_size self.keep_sparse = keep_sparse self.random_state = random_state self._sample() def _sample(self): random_state = check_random_state(self.random_state) if self.sampler is None: self.sampler_ = RandomUnderSampler(random_state=random_state) else: self.sampler_ = clone(self.sampler) self.sampler_.fit_resample(self.X, self.y) if not hasattr(self.sampler_, "sample_indices_"): raise ValueError("'sampler' needs to have an attribute 'sample_indices_'.") self.indices_ = self.sampler_.sample_indices_ # shuffle the indices since the sampler are packing them by class random_state.shuffle(self.indices_) def __len__(self): return int(self.indices_.size // self.batch_size) def __getitem__(self, index): X_resampled = _safe_indexing( self.X, self.indices_[index * self.batch_size : (index + 1) * self.batch_size], ) y_resampled = _safe_indexing( self.y, self.indices_[index * self.batch_size : (index + 1) * self.batch_size], ) if issparse(X_resampled) and not self.keep_sparse: X_resampled = X_resampled.toarray() if self.sample_weight is not None: sample_weight_resampled = _safe_indexing( self.sample_weight, self.indices_[index * self.batch_size : (index + 1) * self.batch_size], ) if self.sample_weight is None: return X_resampled, y_resampled else: return X_resampled, y_resampled, sample_weight_resampled @Substitution(random_state=_random_state_docstring) def balanced_batch_generator( X, y, *, sample_weight=None, sampler=None, batch_size=32, keep_sparse=False, random_state=None, ): """Create a balanced batch generator to train keras model. Returns a generator --- as well as the number of step per epoch --- which is given to ``fit``. The sampler defines the sampling strategy used to balance the dataset ahead of creating the batch. The sampler should have an attribute ``sample_indices_``. Parameters ---------- X : ndarray of shape (n_samples, n_features) Original imbalanced dataset. y : ndarray of shape (n_samples,) or (n_samples, n_classes) Associated targets. sample_weight : ndarray of shape (n_samples,), default=None Sample weight. sampler : sampler object, default=None A sampler instance which has an attribute ``sample_indices_``. By default, the sampler used is a :class:`~imblearn.under_sampling.RandomUnderSampler`. batch_size : int, default=32 Number of samples per gradient update. keep_sparse : bool, default=False Either or not to conserve or not the sparsity of the input (i.e. ``X``, ``y``, ``sample_weight``). By default, the returned batches will be dense. {random_state} Returns ------- generator : generator of tuple Generate batch of data. The tuple generated are either (X_batch, y_batch) or (X_batch, y_batch, sampler_weight_batch). steps_per_epoch : int The number of samples per epoch. Required by ``fit_generator`` in keras. Examples -------- >>> from sklearn.datasets import load_iris >>> X, y = load_iris(return_X_y=True) >>> from imblearn.datasets import make_imbalance >>> class_dict = dict() >>> class_dict[0] = 30; class_dict[1] = 50; class_dict[2] = 40 >>> from imblearn.datasets import make_imbalance >>> X, y = make_imbalance(X, y, sampling_strategy=class_dict) >>> import tensorflow >>> y = tensorflow.keras.utils.to_categorical(y, 3) >>> model = tensorflow.keras.models.Sequential() >>> model.add( ... tensorflow.keras.layers.Dense( ... y.shape[1], input_dim=X.shape[1], activation='softmax' ... ) ... ) >>> model.compile(optimizer='sgd', loss='categorical_crossentropy', ... metrics=['accuracy']) >>> from imblearn.keras import balanced_batch_generator >>> from imblearn.under_sampling import NearMiss >>> training_generator, steps_per_epoch = balanced_batch_generator( ... X, y, sampler=NearMiss(), batch_size=10, random_state=42) >>> callback_history = model.fit(training_generator, ... steps_per_epoch=steps_per_epoch, ... epochs=10, verbose=0) """ return tf_bbg( X=X, y=y, sample_weight=sample_weight, sampler=sampler, batch_size=batch_size, keep_sparse=keep_sparse, random_state=random_state, ) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/keras/tests/000077500000000000000000000000001521136113700252365ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/keras/tests/__init__.py000066400000000000000000000000001521136113700273350ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/keras/tests/test_generator.py000066400000000000000000000103221521136113700306330ustar00rootroot00000000000000import numpy as np import pytest from scipy import sparse from sklearn.cluster import KMeans from sklearn.datasets import load_iris from sklearn.preprocessing import LabelBinarizer keras = pytest.importorskip("keras") from keras.layers import Dense # noqa: E402 from keras.models import Sequential # noqa: E402 from imblearn.datasets import make_imbalance # noqa: E402 from imblearn.keras import ( # noqa: E402 BalancedBatchGenerator, balanced_batch_generator, ) from imblearn.over_sampling import RandomOverSampler # noqa: E402 from imblearn.under_sampling import ClusterCentroids, NearMiss # noqa: E402 @pytest.fixture def data(): iris = load_iris() X, y = make_imbalance( iris.data, iris.target, sampling_strategy={0: 30, 1: 50, 2: 40} ) X = X.astype(np.float32) y = LabelBinarizer().fit_transform(y).astype(np.int32) return X, y def _build_keras_model(n_classes, n_features): model = Sequential() model.add(Dense(n_classes, input_dim=n_features, activation="softmax")) model.compile( optimizer="sgd", loss="categorical_crossentropy", metrics=["accuracy"] ) return model def test_balanced_batch_generator_class_no_return_indices(data): with pytest.raises(ValueError, match="needs to have an attribute"): BalancedBatchGenerator( *data, sampler=ClusterCentroids(estimator=KMeans(n_init=1)), batch_size=10 ) @pytest.mark.filterwarnings("ignore:`wait_time` is not used") # keras 2.2.4 @pytest.mark.parametrize( "sampler, sample_weight", [ (None, None), (RandomOverSampler(), None), (NearMiss(), None), (None, np.random.uniform(size=120)), ], ) def test_balanced_batch_generator_class(data, sampler, sample_weight): X, y = data model = _build_keras_model(y.shape[1], X.shape[1]) training_generator = BalancedBatchGenerator( X, y, sample_weight=sample_weight, sampler=sampler, batch_size=10, random_state=42, ) model.fit(training_generator, epochs=10) @pytest.mark.parametrize("keep_sparse", [True, False]) def test_balanced_batch_generator_class_sparse(data, keep_sparse): X, y = data training_generator = BalancedBatchGenerator( sparse.csr_matrix(X), y, batch_size=10, keep_sparse=keep_sparse, random_state=42, ) for idx in range(len(training_generator)): X_batch, _ = training_generator.__getitem__(idx) if keep_sparse: assert sparse.issparse(X_batch) else: assert not sparse.issparse(X_batch) def test_balanced_batch_generator_function_no_return_indices(data): with pytest.raises(ValueError, match="needs to have an attribute"): balanced_batch_generator( *data, sampler=ClusterCentroids(estimator=KMeans(n_init=10)), batch_size=10, random_state=42, ) @pytest.mark.filterwarnings("ignore:`wait_time` is not used") # keras 2.2.4 @pytest.mark.parametrize( "sampler, sample_weight", [ (None, None), (RandomOverSampler(), None), (NearMiss(), None), (None, np.random.uniform(size=120).astype(np.float32)), ], ) def test_balanced_batch_generator_function(data, sampler, sample_weight): X, y = data model = _build_keras_model(y.shape[1], X.shape[1]) training_generator, steps_per_epoch = balanced_batch_generator( X, y, sample_weight=sample_weight, sampler=sampler, batch_size=10, random_state=42, ) print(next(training_generator)) model.fit( training_generator, steps_per_epoch=steps_per_epoch, epochs=10, ) @pytest.mark.parametrize("keep_sparse", [True, False]) def test_balanced_batch_generator_function_sparse(data, keep_sparse): X, y = data training_generator, steps_per_epoch = balanced_batch_generator( sparse.csr_matrix(X), y, keep_sparse=keep_sparse, batch_size=10, random_state=42, ) for _ in range(steps_per_epoch): X_batch, _ = next(training_generator) if keep_sparse: assert sparse.issparse(X_batch) else: assert not sparse.issparse(X_batch) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/metrics/000077500000000000000000000000001521136113700244355ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/metrics/__init__.py000066400000000000000000000012221521136113700265430ustar00rootroot00000000000000""" The :mod:`imblearn.metrics` module includes score functions, performance metrics and pairwise metrics and distance computations. """ from imblearn.metrics._classification import ( classification_report_imbalanced, geometric_mean_score, macro_averaged_mean_absolute_error, make_index_balanced_accuracy, sensitivity_score, sensitivity_specificity_support, specificity_score, ) __all__ = [ "sensitivity_specificity_support", "sensitivity_score", "specificity_score", "geometric_mean_score", "make_index_balanced_accuracy", "classification_report_imbalanced", "macro_averaged_mean_absolute_error", ] scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/metrics/_classification.py000066400000000000000000001163461521136113700301540ustar00rootroot00000000000000"""Metrics to assess performance on a classification task given class predictions. The available metrics are complementary from the metrics available in scikit-learn. Functions named as ``*_score`` return a scalar value to maximize: the higher the better Function named as ``*_error`` or ``*_loss`` return a scalar value to minimize: the lower the better """ # Authors: Guillaume Lemaitre # Dariusz Brzezinski # License: MIT import functools import numbers import warnings from inspect import signature import numpy as np import scipy as sp from sklearn.metrics import mean_absolute_error, precision_recall_fscore_support from sklearn.metrics._classification import _prf_divide from sklearn.preprocessing import LabelEncoder from sklearn.utils._param_validation import Interval, StrOptions from sklearn.utils.multiclass import unique_labels from sklearn.utils.validation import check_consistent_length, column_or_1d from sklearn_compat.metrics._classification import _check_targets from sklearn_compat.utils._param_validation import validate_params @validate_params( { "y_true": ["array-like"], "y_pred": ["array-like"], "labels": ["array-like", None], "pos_label": [str, numbers.Integral, None], "average": [ None, StrOptions({"binary", "micro", "macro", "weighted", "samples"}), ], "warn_for": ["array-like"], "sample_weight": ["array-like", None], }, prefer_skip_nested_validation=True, ) def sensitivity_specificity_support( y_true, y_pred, *, labels=None, pos_label=1, average=None, warn_for=("sensitivity", "specificity"), sample_weight=None, ): """Compute sensitivity, specificity, and support for each class. The sensitivity is the ratio ``tp / (tp + fn)`` where ``tp`` is the number of true positives and ``fn`` the number of false negatives. The sensitivity quantifies the ability to avoid false negatives_[1]. The specificity is the ratio ``tn / (tn + fp)`` where ``tn`` is the number of true negatives and ``fn`` the number of false negatives. The specificity quantifies the ability to avoid false positives_[1]. The support is the number of occurrences of each class in ``y_true``. If ``pos_label is None`` and in binary classification, this function returns the average sensitivity and specificity if ``average`` is one of ``'weighted'``. Read more in the :ref:`User Guide `. Parameters ---------- y_true : array-like of shape (n_samples,) Ground truth (correct) target values. y_pred : array-like of shape (n_samples,) Estimated targets as returned by a classifier. labels : array-like, default=None The set of labels to include when ``average != 'binary'``, and their order if ``average is None``. Labels present in the data can be excluded, for example to calculate a multiclass average ignoring a majority negative class, while labels not present in the data will result in 0 components in a macro average. For multilabel targets, labels are column indices. By default, all labels in ``y_true`` and ``y_pred`` are used in sorted order. pos_label : str, int or None, default=1 The class to report if ``average='binary'`` and the data is binary. If ``pos_label is None`` and in binary classification, this function returns the average sensitivity and specificity if ``average`` is one of ``'weighted'``. If the data are multiclass, this will be ignored; setting ``labels=[pos_label]`` and ``average != 'binary'`` will report scores for that label only. average : str, default=None If ``None``, the scores for each class are returned. Otherwise, this determines the type of averaging performed on the data: ``'binary'``: Only report results for the class specified by ``pos_label``. This is applicable only if targets (``y_{true,pred}``) are binary. ``'micro'``: Calculate metrics globally by counting the total true positives, false negatives and false positives. ``'macro'``: Calculate metrics for each label, and find their unweighted mean. This does not take label imbalance into account. ``'weighted'``: Calculate metrics for each label, and find their average, weighted by support (the number of true instances for each label). This alters 'macro' to account for label imbalance; it can result in an F-score that is not between precision and recall. ``'samples'``: Calculate metrics for each instance, and find their average (only meaningful for multilabel classification where this differs from :func:`accuracy_score`). warn_for : tuple or set of {{"sensitivity", "specificity"}}, for internal use This determines which warnings will be made in the case that this function is being used to return only one of its metrics. sample_weight : array-like of shape (n_samples,), default=None Sample weights. Returns ------- sensitivity : float (if `average is None`) or ndarray of \ shape (n_unique_labels,) The sensitivity metric. specificity : float (if `average is None`) or ndarray of \ shape (n_unique_labels,) The specificity metric. support : int (if `average is None`) or ndarray of \ shape (n_unique_labels,) The number of occurrences of each label in ``y_true``. References ---------- .. [1] `Wikipedia entry for the Sensitivity and specificity `_ Examples -------- >>> import numpy as np >>> from imblearn.metrics import sensitivity_specificity_support >>> y_true = np.array(['cat', 'dog', 'pig', 'cat', 'dog', 'pig']) >>> y_pred = np.array(['cat', 'pig', 'dog', 'cat', 'cat', 'dog']) >>> sensitivity_specificity_support(y_true, y_pred, average='macro') (0.33..., 0.66..., None) >>> sensitivity_specificity_support(y_true, y_pred, average='micro') (0.33..., 0.66..., None) >>> sensitivity_specificity_support(y_true, y_pred, average='weighted') (0.33..., 0.66..., None) """ average_options = (None, "micro", "macro", "weighted", "samples") if average not in average_options and average != "binary": raise ValueError("average has to be one of " + str(average_options)) y_type, _, y_true, y_pred, sample_weight = _check_targets( y_true, y_pred, sample_weight=sample_weight ) present_labels = unique_labels(y_true, y_pred) if average == "binary": if y_type == "binary": if pos_label not in present_labels: if len(present_labels) < 2: # Only negative labels return (0.0, 0.0, 0) else: raise ValueError( f"pos_label={pos_label!r} is not a valid label:" f" {present_labels!r}" ) labels = [pos_label] else: raise ValueError( f"Target is {y_type} but average='binary'. Please " "choose another average setting." ) elif pos_label not in (None, 1): warnings.warn( ( f"Note that pos_label (set to {pos_label!r}) is ignored when " f"average != 'binary' (got {average!r}). You may use " "labels=[pos_label] to specify a single positive class." ), UserWarning, ) if labels is None: labels = present_labels n_labels = None else: n_labels = len(labels) labels = np.hstack( [labels, np.setdiff1d(present_labels, labels, assume_unique=True)] ) # Calculate tp_sum, pred_sum, true_sum ### if y_type.startswith("multilabel"): raise ValueError("imblearn does not support multilabel") elif average == "samples": raise ValueError( "Sample-based precision, recall, fscore is " "not meaningful outside multilabel " "classification. See the accuracy_score instead." ) else: le = LabelEncoder() le.fit(labels) y_true = le.transform(y_true) y_pred = le.transform(y_pred) sorted_labels = le.classes_ # labels are now from 0 to len(labels) - 1 -> use bincount tp = y_true == y_pred tp_bins = y_true[tp] if sample_weight is not None: tp_bins_weights = np.asarray(sample_weight)[tp] else: tp_bins_weights = None if len(tp_bins): tp_sum = np.bincount( tp_bins, weights=tp_bins_weights, minlength=len(labels) ) else: # Pathological case true_sum = pred_sum = tp_sum = np.zeros(len(labels)) if len(y_pred): pred_sum = np.bincount(y_pred, weights=sample_weight, minlength=len(labels)) if len(y_true): true_sum = np.bincount(y_true, weights=sample_weight, minlength=len(labels)) # Compute the true negative tn_sum = y_true.size - (pred_sum + true_sum - tp_sum) # Retain only selected labels indices = np.searchsorted(sorted_labels, labels[:n_labels]) tp_sum = tp_sum[indices] true_sum = true_sum[indices] pred_sum = pred_sum[indices] tn_sum = tn_sum[indices] if average == "micro": tp_sum = np.array([tp_sum.sum()]) pred_sum = np.array([pred_sum.sum()]) true_sum = np.array([true_sum.sum()]) tn_sum = np.array([tn_sum.sum()]) # Finally, we have all our sufficient statistics. Divide! # with np.errstate(divide="ignore", invalid="ignore"): # Divide, and on zero-division, set scores to 0 and warn: # Oddly, we may get an "invalid" rather than a "divide" error # here. specificity = _prf_divide( tn_sum, tn_sum + pred_sum - tp_sum, "specificity", "predicted", average, warn_for, ) sensitivity = _prf_divide( tp_sum, true_sum, "sensitivity", "true", average, warn_for ) # Average the results if average == "weighted": weights = true_sum if weights.sum() == 0: return 0, 0, None elif average == "samples": weights = sample_weight else: weights = None if average is not None: assert average != "binary" or len(specificity) == 1 specificity = np.average(specificity, weights=weights) sensitivity = np.average(sensitivity, weights=weights) true_sum = None # return no support return sensitivity, specificity, true_sum @validate_params( { "y_true": ["array-like"], "y_pred": ["array-like"], "labels": ["array-like", None], "pos_label": [str, numbers.Integral, None], "average": [ None, StrOptions({"binary", "micro", "macro", "weighted", "samples"}), ], "sample_weight": ["array-like", None], }, prefer_skip_nested_validation=True, ) def sensitivity_score( y_true, y_pred, *, labels=None, pos_label=1, average="binary", sample_weight=None, ): """Compute the sensitivity. The sensitivity is the ratio ``tp / (tp + fn)`` where ``tp`` is the number of true positives and ``fn`` the number of false negatives. The sensitivity quantifies the ability to avoid false negatives. The best value is 1 and the worst value is 0. Read more in the :ref:`User Guide `. Parameters ---------- y_true : array-like of shape (n_samples,) Ground truth (correct) target values. y_pred : array-like of shape (n_samples,) Estimated targets as returned by a classifier. labels : array-like, default=None The set of labels to include when ``average != 'binary'``, and their order if ``average is None``. Labels present in the data can be excluded, for example to calculate a multiclass average ignoring a majority negative class, while labels not present in the data will result in 0 components in a macro average. pos_label : str, int or None, default=1 The class to report if ``average='binary'`` and the data is binary. If ``pos_label is None`` and in binary classification, this function returns the average sensitivity if ``average`` is one of ``'weighted'``. If the data are multiclass, this will be ignored; setting ``labels=[pos_label]`` and ``average != 'binary'`` will report scores for that label only. average : str, default=None If ``None``, the scores for each class are returned. Otherwise, this determines the type of averaging performed on the data: ``'binary'``: Only report results for the class specified by ``pos_label``. This is applicable only if targets (``y_{true,pred}``) are binary. ``'micro'``: Calculate metrics globally by counting the total true positives, false negatives and false positives. ``'macro'``: Calculate metrics for each label, and find their unweighted mean. This does not take label imbalance into account. ``'weighted'``: Calculate metrics for each label, and find their average, weighted by support (the number of true instances for each label). This alters 'macro' to account for label imbalance; it can result in an F-score that is not between precision and recall. ``'samples'``: Calculate metrics for each instance, and find their average (only meaningful for multilabel classification where this differs from :func:`accuracy_score`). sample_weight : array-like of shape (n_samples,), default=None Sample weights. Returns ------- specificity : float (if `average is None`) or ndarray of \ shape (n_unique_labels,) The specifcity metric. Examples -------- >>> import numpy as np >>> from imblearn.metrics import sensitivity_score >>> y_true = [0, 1, 2, 0, 1, 2] >>> y_pred = [0, 2, 1, 0, 0, 1] >>> sensitivity_score(y_true, y_pred, average='macro') 0.33... >>> sensitivity_score(y_true, y_pred, average='micro') 0.33... >>> sensitivity_score(y_true, y_pred, average='weighted') 0.33... >>> sensitivity_score(y_true, y_pred, average=None) array([1., 0., 0.]) """ s, _, _ = sensitivity_specificity_support( y_true, y_pred, labels=labels, pos_label=pos_label, average=average, warn_for=("sensitivity",), sample_weight=sample_weight, ) return s @validate_params( { "y_true": ["array-like"], "y_pred": ["array-like"], "labels": ["array-like", None], "pos_label": [str, numbers.Integral, None], "average": [ None, StrOptions({"binary", "micro", "macro", "weighted", "samples"}), ], "sample_weight": ["array-like", None], }, prefer_skip_nested_validation=True, ) def specificity_score( y_true, y_pred, *, labels=None, pos_label=1, average="binary", sample_weight=None, ): """Compute the specificity. The specificity is the ratio ``tn / (tn + fp)`` where ``tn`` is the number of true negatives and ``fp`` the number of false positives. The specificity quantifies the ability to avoid false positives. The best value is 1 and the worst value is 0. Read more in the :ref:`User Guide `. Parameters ---------- y_true : array-like of shape (n_samples,) Ground truth (correct) target values. y_pred : array-like of shape (n_samples,) Estimated targets as returned by a classifier. labels : array-like, default=None The set of labels to include when ``average != 'binary'``, and their order if ``average is None``. Labels present in the data can be excluded, for example to calculate a multiclass average ignoring a majority negative class, while labels not present in the data will result in 0 components in a macro average. pos_label : str, int or None, default=1 The class to report if ``average='binary'`` and the data is binary. If ``pos_label is None`` and in binary classification, this function returns the average specificity if ``average`` is one of ``'weighted'``. If the data are multiclass, this will be ignored; setting ``labels=[pos_label]`` and ``average != 'binary'`` will report scores for that label only. average : str, default=None If ``None``, the scores for each class are returned. Otherwise, this determines the type of averaging performed on the data: ``'binary'``: Only report results for the class specified by ``pos_label``. This is applicable only if targets (``y_{true,pred}``) are binary. ``'micro'``: Calculate metrics globally by counting the total true positives, false negatives and false positives. ``'macro'``: Calculate metrics for each label, and find their unweighted mean. This does not take label imbalance into account. ``'weighted'``: Calculate metrics for each label, and find their average, weighted by support (the number of true instances for each label). This alters 'macro' to account for label imbalance; it can result in an F-score that is not between precision and recall. ``'samples'``: Calculate metrics for each instance, and find their average (only meaningful for multilabel classification where this differs from :func:`accuracy_score`). sample_weight : array-like of shape (n_samples,), default=None Sample weights. Returns ------- specificity : float (if `average is None`) or ndarray of \ shape (n_unique_labels,) The specificity metric. Examples -------- >>> import numpy as np >>> from imblearn.metrics import specificity_score >>> y_true = [0, 1, 2, 0, 1, 2] >>> y_pred = [0, 2, 1, 0, 0, 1] >>> specificity_score(y_true, y_pred, average='macro') 0.66... >>> specificity_score(y_true, y_pred, average='micro') 0.66... >>> specificity_score(y_true, y_pred, average='weighted') 0.66... >>> specificity_score(y_true, y_pred, average=None) array([0.75, 0.5 , 0.75]) """ _, s, _ = sensitivity_specificity_support( y_true, y_pred, labels=labels, pos_label=pos_label, average=average, warn_for=("specificity",), sample_weight=sample_weight, ) return s @validate_params( { "y_true": ["array-like"], "y_pred": ["array-like"], "labels": ["array-like", None], "pos_label": [str, numbers.Integral, None], "average": [ None, StrOptions( {"binary", "micro", "macro", "weighted", "samples", "multiclass"} ), ], "sample_weight": ["array-like", None], "correction": [Interval(numbers.Real, 0, None, closed="left")], }, prefer_skip_nested_validation=True, ) def geometric_mean_score( y_true, y_pred, *, labels=None, pos_label=1, average="multiclass", sample_weight=None, correction=0.0, ): """Compute the geometric mean. The geometric mean (G-mean) is the root of the product of class-wise sensitivity. This measure tries to maximize the accuracy on each of the classes while keeping these accuracies balanced. For binary classification G-mean is the squared root of the product of the sensitivity and specificity. For multi-class problems it is a higher root of the product of sensitivity for each class. For compatibility with other imbalance performance measures, G-mean can be calculated for each class separately on a one-vs-rest basis when ``average != 'multiclass'``. The best value is 1 and the worst value is 0. Traditionally if at least one class is unrecognized by the classifier, G-mean resolves to zero. To alleviate this property, for highly multi-class the sensitivity of unrecognized classes can be "corrected" to be a user specified value (instead of zero). This option works only if ``average == 'multiclass'``. Read more in the :ref:`User Guide `. Parameters ---------- y_true : array-like of shape (n_samples,) Ground truth (correct) target values. y_pred : array-like of shape (n_samples,) Estimated targets as returned by a classifier. labels : array-like, default=None The set of labels to include when ``average != 'binary'``, and their order if ``average is None``. Labels present in the data can be excluded, for example to calculate a multiclass average ignoring a majority negative class, while labels not present in the data will result in 0 components in a macro average. pos_label : str, int or None, default=1 The class to report if ``average='binary'`` and the data is binary. If ``pos_label is None`` and in binary classification, this function returns the average geometric mean if ``average`` is one of ``'weighted'``. If the data are multiclass, this will be ignored; setting ``labels=[pos_label]`` and ``average != 'binary'`` will report scores for that label only. average : str or None, default='multiclass' If ``None``, the scores for each class are returned. Otherwise, this determines the type of averaging performed on the data: ``'binary'``: Only report results for the class specified by ``pos_label``. This is applicable only if targets (``y_{true,pred}``) are binary. ``'micro'``: Calculate metrics globally by counting the total true positives, false negatives and false positives. ``'macro'``: Calculate metrics for each label, and find their unweighted mean. This does not take label imbalance into account. ``'multiclass'``: No average is taken. ``'weighted'``: Calculate metrics for each label, and find their average, weighted by support (the number of true instances for each label). This alters 'macro' to account for label imbalance; it can result in an F-score that is not between precision and recall. ``'samples'``: Calculate metrics for each instance, and find their average (only meaningful for multilabel classification where this differs from :func:`accuracy_score`). sample_weight : array-like of shape (n_samples,), default=None Sample weights. correction : float, default=0.0 Substitutes sensitivity of unrecognized classes from zero to a given value. Returns ------- geometric_mean : float Returns the geometric mean. Notes ----- See :ref:`sphx_glr_auto_examples_evaluation_plot_metrics.py`. References ---------- .. [1] Kubat, M. and Matwin, S. "Addressing the curse of imbalanced training sets: one-sided selection" ICML (1997) .. [2] Barandela, R., Sánchez, J. S., Garcıa, V., & Rangel, E. "Strategies for learning in class imbalance problems", Pattern Recognition, 36(3), (2003), pp 849-851. Examples -------- >>> from imblearn.metrics import geometric_mean_score >>> y_true = [0, 1, 2, 0, 1, 2] >>> y_pred = [0, 2, 1, 0, 0, 1] >>> geometric_mean_score(y_true, y_pred) 0.0 >>> geometric_mean_score(y_true, y_pred, correction=0.001) 0.010... >>> geometric_mean_score(y_true, y_pred, average='macro') 0.471... >>> geometric_mean_score(y_true, y_pred, average='micro') 0.471... >>> geometric_mean_score(y_true, y_pred, average='weighted') 0.471... >>> geometric_mean_score(y_true, y_pred, average=None) array([0.866..., 0. , 0. ]) """ if average is None or average != "multiclass": sen, spe, _ = sensitivity_specificity_support( y_true, y_pred, labels=labels, pos_label=pos_label, average=average, warn_for=("specificity", "specificity"), sample_weight=sample_weight, ) return np.sqrt(sen * spe) else: present_labels = unique_labels(y_true, y_pred) if labels is None: labels = present_labels n_labels = None else: n_labels = len(labels) labels = np.hstack( [labels, np.setdiff1d(present_labels, labels, assume_unique=True)] ) le = LabelEncoder() le.fit(labels) y_true = le.transform(y_true) y_pred = le.transform(y_pred) sorted_labels = le.classes_ # labels are now from 0 to len(labels) - 1 -> use bincount tp = y_true == y_pred tp_bins = y_true[tp] if sample_weight is not None: tp_bins_weights = np.asarray(sample_weight)[tp] else: tp_bins_weights = None if len(tp_bins): tp_sum = np.bincount( tp_bins, weights=tp_bins_weights, minlength=len(labels) ) else: # Pathological case true_sum = tp_sum = np.zeros(len(labels)) if len(y_true): true_sum = np.bincount(y_true, weights=sample_weight, minlength=len(labels)) # Retain only selected labels indices = np.searchsorted(sorted_labels, labels[:n_labels]) tp_sum = tp_sum[indices] true_sum = true_sum[indices] with np.errstate(divide="ignore", invalid="ignore"): recall = _prf_divide(tp_sum, true_sum, "recall", "true", None, "recall") recall[recall == 0] = correction with np.errstate(divide="ignore", invalid="ignore"): gmean = sp.stats.gmean(recall) # old version of scipy return MaskedConstant instead of 0.0 if isinstance(gmean, np.ma.core.MaskedConstant): return 0.0 return gmean @validate_params( {"alpha": [numbers.Real], "squared": ["boolean"]}, prefer_skip_nested_validation=True, ) def make_index_balanced_accuracy(*, alpha=0.1, squared=True): """Balance any scoring function using the index balanced accuracy. This factory function wraps scoring function to express it as the index balanced accuracy (IBA). You need to use this function to decorate any scoring function. Only metrics requiring ``y_pred`` can be corrected with the index balanced accuracy. ``y_score`` cannot be used since the dominance cannot be computed. Read more in the :ref:`User Guide `. Parameters ---------- alpha : float, default=0.1 Weighting factor. squared : bool, default=True If ``squared`` is True, then the metric computed will be squared before to be weighted. Returns ------- iba_scoring_func : callable, Returns the scoring metric decorated which will automatically compute the index balanced accuracy. Notes ----- See :ref:`sphx_glr_auto_examples_evaluation_plot_metrics.py`. References ---------- .. [1] García, Vicente, Javier Salvador Sánchez, and Ramón Alberto Mollineda. "On the effectiveness of preprocessing methods when dealing with different levels of class imbalance." Knowledge-Based Systems 25.1 (2012): 13-21. Examples -------- >>> from imblearn.metrics import geometric_mean_score as gmean >>> from imblearn.metrics import make_index_balanced_accuracy as iba >>> gmean = iba(alpha=0.1, squared=True)(gmean) >>> y_true = [1, 0, 0, 1, 0, 1] >>> y_pred = [0, 0, 1, 1, 0, 1] >>> print(gmean(y_true, y_pred, average=None)) [0.44... 0.44...] """ def decorate(scoring_func): @functools.wraps(scoring_func) def compute_score(*args, **kwargs): signature_scoring_func = signature(scoring_func) params_scoring_func = set(signature_scoring_func.parameters.keys()) # check that the scoring function does not need a score # and only a prediction prohibitied_y_pred = set(["y_score", "y_prob", "y2"]) if prohibitied_y_pred.intersection(params_scoring_func): raise AttributeError( f"The function {scoring_func.__name__} has an unsupported" " attribute. Metric with`y_pred` are the" " only supported metrics is the only" " supported." ) args_scoring_func = signature_scoring_func.bind(*args, **kwargs) args_scoring_func.apply_defaults() _score = scoring_func(*args_scoring_func.args, **args_scoring_func.kwargs) if squared: _score = np.power(_score, 2) signature_sens_spec = signature(sensitivity_specificity_support) params_sens_spec = set(signature_sens_spec.parameters.keys()) common_params = params_sens_spec.intersection( set(args_scoring_func.arguments.keys()) ) args_sens_spec = {k: args_scoring_func.arguments[k] for k in common_params} if scoring_func.__name__ == "geometric_mean_score": if "average" in args_sens_spec: if args_sens_spec["average"] == "multiclass": args_sens_spec["average"] = "macro" elif ( scoring_func.__name__ == "accuracy_score" or scoring_func.__name__ == "jaccard_score" ): # We do not support multilabel so the only average supported # is binary args_sens_spec["average"] = "binary" sensitivity, specificity, _ = sensitivity_specificity_support( **args_sens_spec ) dominance = sensitivity - specificity return (1.0 + alpha * dominance) * _score return compute_score return decorate @validate_params( { "y_true": ["array-like"], "y_pred": ["array-like"], "labels": ["array-like", None], "target_names": ["array-like", None], "sample_weight": ["array-like", None], "digits": [Interval(numbers.Integral, 0, None, closed="left")], "alpha": [numbers.Real], "output_dict": ["boolean"], "zero_division": [ StrOptions({"warn"}), Interval(numbers.Integral, 0, 1, closed="both"), ], }, prefer_skip_nested_validation=True, ) def classification_report_imbalanced( y_true, y_pred, *, labels=None, target_names=None, sample_weight=None, digits=2, alpha=0.1, output_dict=False, zero_division="warn", ): """Build a classification report based on metrics used with imbalanced dataset. Specific metrics have been proposed to evaluate the classification performed on imbalanced dataset. This report compiles the state-of-the-art metrics: precision/recall/specificity, geometric mean, and index balanced accuracy of the geometric mean. Read more in the :ref:`User Guide `. Parameters ---------- y_true : 1d array-like, or label indicator array / sparse matrix Ground truth (correct) target values. y_pred : 1d array-like, or label indicator array / sparse matrix Estimated targets as returned by a classifier. labels : array-like of shape (n_labels,), default=None Optional list of label indices to include in the report. target_names : list of str of shape (n_labels,), default=None Optional display names matching the labels (same order). sample_weight : array-like of shape (n_samples,), default=None Sample weights. digits : int, default=2 Number of digits for formatting output floating point values. When ``output_dict`` is ``True``, this will be ignored and the returned values will not be rounded. alpha : float, default=0.1 Weighting factor. output_dict : bool, default=False If True, return output as dict. .. versionadded:: 0.8 zero_division : "warn" or {0, 1}, default="warn" Sets the value to return when there is a zero division. If set to "warn", this acts as 0, but warnings are also raised. .. versionadded:: 0.8 Returns ------- report : string / dict Text summary of the precision, recall, specificity, geometric mean, and index balanced accuracy. Dictionary returned if output_dict is True. Dictionary has the following structure:: {'label 1': {'pre':0.5, 'rec':1.0, ... }, 'label 2': { ... }, ... } Examples -------- >>> import numpy as np >>> from imblearn.metrics import classification_report_imbalanced >>> y_true = [0, 1, 2, 2, 2] >>> y_pred = [0, 0, 2, 2, 1] >>> target_names = ['class 0', 'class 1', 'class 2'] >>> print(classification_report_imbalanced(y_true, y_pred, \ target_names=target_names)) pre rec spe f1 geo iba\ sup class 0 0.50 1.00 0.75 0.67 0.87 0.77\ 1 class 1 0.00 0.00 0.75 0.00 0.00 0.00\ 1 class 2 1.00 0.67 1.00 0.80 0.82 0.64\ 3 avg / total 0.70 0.60 0.90 0.61 0.66 0.54\ 5 """ if labels is None: labels = unique_labels(y_true, y_pred) else: labels = np.asarray(labels) last_line_heading = "avg / total" if target_names is None: target_names = [f"{label}" for label in labels] name_width = max(len(cn) for cn in target_names) width = max(name_width, len(last_line_heading), digits) headers = ["pre", "rec", "spe", "f1", "geo", "iba", "sup"] fmt = "%% %ds" % width # first column: class name fmt += " " fmt += " ".join(["% 9s" for _ in headers]) fmt += "\n" headers = [""] + headers report = fmt % tuple(headers) report += "\n" # Compute the different metrics # Precision/recall/f1 precision, recall, f1, support = precision_recall_fscore_support( y_true, y_pred, labels=labels, average=None, sample_weight=sample_weight, zero_division=zero_division, ) # Specificity specificity = specificity_score( y_true, y_pred, labels=labels, average=None, sample_weight=sample_weight, ) # Geometric mean geo_mean = geometric_mean_score( y_true, y_pred, labels=labels, average=None, sample_weight=sample_weight, ) # Index balanced accuracy iba_gmean = make_index_balanced_accuracy(alpha=alpha, squared=True)( geometric_mean_score ) iba = iba_gmean( y_true, y_pred, labels=labels, average=None, sample_weight=sample_weight, ) report_dict = {} for i, label in enumerate(labels): report_dict_label = {} values = [target_names[i]] for score_name, score_value in zip( headers[1:-1], [ precision[i], recall[i], specificity[i], f1[i], geo_mean[i], iba[i], ], ): values += ["{0:0.{1}f}".format(score_value, digits)] report_dict_label[score_name] = score_value values += [f"{support[i]}"] report_dict_label[headers[-1]] = support[i] report += fmt % tuple(values) report_dict[target_names[i]] = report_dict_label report += "\n" # compute averages values = [last_line_heading] for score_name, score_value in zip( headers[1:-1], [ np.average(precision, weights=support), np.average(recall, weights=support), np.average(specificity, weights=support), np.average(f1, weights=support), np.average(geo_mean, weights=support), np.average(iba, weights=support), ], ): values += ["{0:0.{1}f}".format(score_value, digits)] report_dict[f"avg_{score_name}"] = score_value values += [f"{np.sum(support)}"] report += fmt % tuple(values) report_dict["total_support"] = np.sum(support) if output_dict: return report_dict return report @validate_params( { "y_true": ["array-like"], "y_pred": ["array-like"], "sample_weight": ["array-like", None], }, prefer_skip_nested_validation=True, ) def macro_averaged_mean_absolute_error(y_true, y_pred, *, sample_weight=None): """Compute Macro-Averaged MAE for imbalanced ordinal classification. This function computes each MAE for each class and average them, giving an equal weight to each class. Read more in the :ref:`User Guide `. .. versionadded:: 0.8 Parameters ---------- y_true : array-like of shape (n_samples,) or (n_samples, n_outputs) Ground truth (correct) target values. y_pred : array-like of shape (n_samples,) or (n_samples, n_outputs) Estimated targets as returned by a classifier. sample_weight : array-like of shape (n_samples,), default=None Sample weights. Returns ------- loss : float or ndarray of floats Macro-Averaged MAE output is non-negative floating point. The best value is 0.0. Examples -------- >>> import numpy as np >>> from sklearn.metrics import mean_absolute_error >>> from imblearn.metrics import macro_averaged_mean_absolute_error >>> y_true_balanced = [1, 1, 2, 2] >>> y_true_imbalanced = [1, 2, 2, 2] >>> y_pred = [1, 2, 1, 2] >>> mean_absolute_error(y_true_balanced, y_pred) 0.5 >>> mean_absolute_error(y_true_imbalanced, y_pred) 0.25 >>> macro_averaged_mean_absolute_error(y_true_balanced, y_pred) 0.5 >>> macro_averaged_mean_absolute_error(y_true_imbalanced, y_pred) 0.16... """ _, _, y_true, y_pred, sample_weight = _check_targets(y_true, y_pred, sample_weight) if sample_weight is not None: sample_weight = column_or_1d(sample_weight) else: sample_weight = np.ones(y_true.shape) check_consistent_length(y_true, y_pred, sample_weight) labels = unique_labels(y_true, y_pred) mae = [] for possible_class in labels: indices = np.flatnonzero(y_true == possible_class) mae.append( mean_absolute_error( y_true[indices], y_pred[indices], sample_weight=sample_weight[indices], ) ) return np.sum(mae) / len(mae) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/metrics/pairwise.py000066400000000000000000000214171521136113700266370ustar00rootroot00000000000000"""Metrics to perform pairwise computation.""" # Authors: Guillaume Lemaitre # License: MIT import numbers import numpy as np from scipy.spatial import distance_matrix from sklearn.base import BaseEstimator from sklearn.utils import check_consistent_length from sklearn.utils._param_validation import StrOptions from sklearn.utils.multiclass import unique_labels from sklearn.utils.validation import check_is_fitted from sklearn_compat.base import _fit_context from sklearn_compat.utils.validation import check_array, validate_data class ValueDifferenceMetric(BaseEstimator): r"""Class implementing the Value Difference Metric. This metric computes the distance between samples containing only categorical features. The distance between feature values of two samples is defined as: .. math:: \delta(x, y) = \sum_{c=1}^{C} |p(c|x_{f}) - p(c|y_{f})|^{k} \ , where :math:`x` and :math:`y` are two samples and :math:`f` a given feature, :math:`C` is the number of classes, :math:`p(c|x_{f})` is the conditional probability that the output class is :math:`c` given that the feature value :math:`f` has the value :math:`x` and :math:`k` an exponent usually defined to 1 or 2. The distance for the feature vectors :math:`X` and :math:`Y` is subsequently defined as: .. math:: \Delta(X, Y) = \sum_{f=1}^{F} \delta(X_{f}, Y_{f})^{r} \ , where :math:`F` is the number of feature and :math:`r` an exponent usually defined equal to 1 or 2. The definition of this distance was propoed in [1]_. Read more in the :ref:`User Guide `. .. versionadded:: 0.8 Parameters ---------- n_categories : "auto" or array-like of shape (n_features,), default="auto" The number of unique categories per features. If `"auto"`, the number of categories will be computed from `X` at `fit`. Otherwise, you can provide an array-like of such counts to avoid computation. You can use the fitted attribute `categories_` of the :class:`~sklearn.preprocesssing.OrdinalEncoder` to deduce these counts. k : int, default=1 Exponent used to compute the distance between feature value. r : int, default=2 Exponent used to compute the distance between the feature vector. Attributes ---------- n_categories_ : ndarray of shape (n_features,) The number of categories per features. proba_per_class_ : list of ndarray of shape (n_categories, n_classes) List of length `n_features` containing the conditional probabilities for each category given a class. n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.10 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- sklearn.neighbors.DistanceMetric : Interface for fast metric computation. Notes ----- The input data `X` are expected to be encoded by an :class:`~sklearn.preprocessing.OrdinalEncoder` and the data type is used should be `np.int32`. If other data types are given, `X` will be converted to `np.int32`. References ---------- .. [1] Stanfill, Craig, and David Waltz. "Toward memory-based reasoning." Communications of the ACM 29.12 (1986): 1213-1228. Examples -------- >>> import numpy as np >>> X = np.array(["green"] * 10 + ["red"] * 10 + ["blue"] * 10).reshape(-1, 1) >>> y = [1] * 8 + [0] * 5 + [1] * 7 + [0] * 9 + [1] >>> from sklearn.preprocessing import OrdinalEncoder >>> encoder = OrdinalEncoder(dtype=np.int32) >>> X_encoded = encoder.fit_transform(X) >>> from imblearn.metrics.pairwise import ValueDifferenceMetric >>> vdm = ValueDifferenceMetric().fit(X_encoded, y) >>> pairwise_distance = vdm.pairwise(X_encoded) >>> pairwise_distance.shape (30, 30) >>> X_test = np.array(["green", "red", "blue"]).reshape(-1, 1) >>> X_test_encoded = encoder.transform(X_test) >>> vdm.pairwise(X_test_encoded) array([[0. , 0.04, 1.96], [0.04, 0. , 1.44], [1.96, 1.44, 0. ]]) """ _parameter_constraints: dict = { "n_categories": [StrOptions({"auto"}), "array-like"], "k": [numbers.Integral], "r": [numbers.Integral], } def __init__(self, *, n_categories="auto", k=1, r=2): self.n_categories = n_categories self.k = k self.r = r @_fit_context(prefer_skip_nested_validation=True) def fit(self, X, y): """Compute the necessary statistics from the training set. Parameters ---------- X : ndarray of shape (n_samples, n_features), dtype=np.int32 The input data. The data are expected to be encoded with a :class:`~sklearn.preprocessing.OrdinalEncoder`. y : ndarray of shape (n_features,) The target. Returns ------- self : object Return the instance itself. """ self._validate_params() check_consistent_length(X, y) X, y = validate_data(self, X=X, y=y, reset=True, dtype=np.int32) X = check_array(X, ensure_non_negative=True) if isinstance(self.n_categories, str) and self.n_categories == "auto": # categories are expected to be encoded from 0 to n_categories - 1 self.n_categories_ = X.max(axis=0) + 1 else: if len(self.n_categories) != self.n_features_in_: raise ValueError( "The length of n_categories is not consistent with the " f"number of feature in X. Got {len(self.n_categories)} " f"elements in n_categories and {self.n_features_in_} in " "X." ) self.n_categories_ = np.asarray(self.n_categories) classes = unique_labels(y) # list of length n_features of ndarray (n_categories, n_classes) # compute the counts self.proba_per_class_ = [ np.empty(shape=(n_cat, len(classes)), dtype=np.float64) for n_cat in self.n_categories_ ] for feature_idx in range(self.n_features_in_): for klass_idx, klass in enumerate(classes): self.proba_per_class_[feature_idx][:, klass_idx] = np.bincount( X[y == klass, feature_idx], minlength=self.n_categories_[feature_idx], ) # normalize by the summing over the classes with np.errstate(invalid="ignore"): # silence potential warning due to in-place division by zero for feature_idx in range(self.n_features_in_): self.proba_per_class_[feature_idx] /= ( self.proba_per_class_[feature_idx].sum(axis=1).reshape(-1, 1) ) np.nan_to_num(self.proba_per_class_[feature_idx], copy=False) return self def pairwise(self, X, Y=None): """Compute the VDM distance pairwise. Parameters ---------- X : ndarray of shape (n_samples, n_features), dtype=np.int32 The input data. The data are expected to be encoded with a :class:`~sklearn.preprocessing.OrdinalEncoder`. Y : ndarray of shape (n_samples, n_features), dtype=np.int32 The input data. The data are expected to be encoded with a :class:`~sklearn.preprocessing.OrdinalEncoder`. Returns ------- distance_matrix : ndarray of shape (n_samples, n_samples) The VDM pairwise distance. """ check_is_fitted(self) X = check_array(X, ensure_non_negative=True, dtype=np.int32) n_samples_X = X.shape[0] if Y is not None: Y = check_array(Y, ensure_non_negative=True, dtype=np.int32) n_samples_Y = Y.shape[0] else: n_samples_Y = n_samples_X distance = np.zeros(shape=(n_samples_X, n_samples_Y), dtype=np.float64) for feature_idx in range(self.n_features_in_): proba_feature_X = self.proba_per_class_[feature_idx][X[:, feature_idx]] if Y is not None: proba_feature_Y = self.proba_per_class_[feature_idx][Y[:, feature_idx]] else: proba_feature_Y = proba_feature_X distance += ( distance_matrix(proba_feature_X, proba_feature_Y, p=self.k) ** self.r ) return distance def _more_tags(self): return { "requires_positive_X": True, # X should be encoded with OrdinalEncoder } def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.input_tags.positive_only = True return tags scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/metrics/tests/000077500000000000000000000000001521136113700255775ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/metrics/tests/__init__.py000066400000000000000000000000001521136113700276760ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/metrics/tests/test_classification.py000066400000000000000000000426211521136113700322100ustar00rootroot00000000000000"""Testing the metric for classification with imbalanced dataset""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT from functools import partial import numpy as np import pytest from sklearn import datasets from sklearn.linear_model import LogisticRegression from sklearn.metrics import ( accuracy_score, average_precision_score, brier_score_loss, cohen_kappa_score, jaccard_score, precision_score, recall_score, roc_auc_score, ) from sklearn.preprocessing import label_binarize from sklearn.utils._testing import ( assert_allclose, assert_array_equal, ) from sklearn.utils.validation import check_random_state from imblearn.metrics import ( classification_report_imbalanced, geometric_mean_score, macro_averaged_mean_absolute_error, make_index_balanced_accuracy, sensitivity_score, sensitivity_specificity_support, specificity_score, ) RND_SEED = 42 R_TOL = 1e-2 ############################################################################### # Utilities for testing def make_prediction(dataset=None, binary=False): """Make some classification predictions on a toy dataset using a classifier. If binary is True restrict to a binary classification problem instead of a multiclass classification problem """ if dataset is None: # import some data to play with dataset = datasets.load_iris() X = dataset.data y = dataset.target if binary: # restrict to a binary classification task X, y = X[y < 2], y[y < 2] n_samples, n_features = X.shape p = np.arange(n_samples) rng = check_random_state(37) rng.shuffle(p) X, y = X[p], y[p] half = int(n_samples / 2) # add noisy features to make the problem harder and avoid perfect results rng = np.random.RandomState(0) X = np.c_[X, rng.randn(n_samples, 200 * n_features)] # run classifier, get class probabilities and label predictions clf = LogisticRegression() probas_pred = clf.fit(X[:half], y[:half]).predict_proba(X[half:]) if binary: # only interested in probabilities of the positive case # XXX: do we really want a special API for the binary case? probas_pred = probas_pred[:, 1] y_pred = clf.predict(X[half:]) y_true = y[half:] return y_true, y_pred, probas_pred ############################################################################### # Tests def test_sensitivity_specificity_score_binary(): y_true, y_pred, _ = make_prediction(binary=True) # detailed measures for each class sen, spe, sup = sensitivity_specificity_support(y_true, y_pred, average=None) assert_allclose(sen, [0.88, 0.68], rtol=R_TOL) assert_allclose(spe, [0.68, 0.88], rtol=R_TOL) assert_array_equal(sup, [25, 25]) # individual scoring function that can be used for grid search: in the # binary class case the score is the value of the measure for the positive # class (e.g. label == 1). This is deprecated for average != 'binary'. for kwargs in ({}, {"average": "binary"}): sen = sensitivity_score(y_true, y_pred, **kwargs) assert sen == pytest.approx(0.68, rel=R_TOL) spe = specificity_score(y_true, y_pred, **kwargs) assert spe == pytest.approx(0.88, rel=R_TOL) @pytest.mark.filterwarnings("ignore:Specificity is ill-defined") @pytest.mark.parametrize( "y_pred, expected_sensitivity, expected_specificity", [(([1, 1], [1, 1]), 1.0, 0.0), (([-1, -1], [-1, -1]), 0.0, 0.0)], ) def test_sensitivity_specificity_f_binary_single_class( y_pred, expected_sensitivity, expected_specificity ): # Such a case may occur with non-stratified cross-validation assert sensitivity_score(*y_pred) == expected_sensitivity assert specificity_score(*y_pred) == expected_specificity @pytest.mark.parametrize( "average, expected_specificty", [ (None, [1.0, 0.67, 1.0, 1.0, 1.0]), ("macro", np.mean([1.0, 0.67, 1.0, 1.0, 1.0])), ("micro", 15 / 16), ], ) def test_sensitivity_specificity_extra_labels(average, expected_specificty): y_true = [1, 3, 3, 2] y_pred = [1, 1, 3, 2] actual = specificity_score(y_true, y_pred, labels=[0, 1, 2, 3, 4], average=average) assert_allclose(expected_specificty, actual, rtol=R_TOL) def test_sensitivity_specificity_ignored_labels(): y_true = [1, 1, 2, 3] y_pred = [1, 3, 3, 3] specificity_13 = partial(specificity_score, y_true, y_pred, labels=[1, 3]) specificity_all = partial(specificity_score, y_true, y_pred, labels=None) assert_allclose([1.0, 0.33], specificity_13(average=None), rtol=R_TOL) assert_allclose(np.mean([1.0, 0.33]), specificity_13(average="macro"), rtol=R_TOL) assert_allclose( np.average([1.0, 0.33], weights=[2.0, 1.0]), specificity_13(average="weighted"), rtol=R_TOL, ) assert_allclose(3.0 / (3.0 + 2.0), specificity_13(average="micro"), rtol=R_TOL) # ensure the above were meaningful tests: for each in ["macro", "weighted", "micro"]: assert specificity_13(average=each) != specificity_all(average=each) def test_sensitivity_specificity_error_multilabels(): y_true = [1, 3, 3, 2] y_pred = [1, 1, 3, 2] y_true_bin = label_binarize(y_true, classes=np.arange(5)) y_pred_bin = label_binarize(y_pred, classes=np.arange(5)) with pytest.raises(ValueError): sensitivity_score(y_true_bin, y_pred_bin) def test_sensitivity_specificity_support_errors(): y_true, y_pred, _ = make_prediction(binary=True) # Bad pos_label with pytest.raises(ValueError): sensitivity_specificity_support(y_true, y_pred, pos_label=2, average="binary") # Bad average option with pytest.raises(ValueError): sensitivity_specificity_support([0, 1, 2], [1, 2, 0], average="mega") def test_sensitivity_specificity_unused_pos_label(): # but average != 'binary'; even if data is binary msg = r"use labels=\[pos_label\] to specify a single" with pytest.warns(UserWarning, match=msg): sensitivity_specificity_support( [1, 2, 1], [1, 2, 2], pos_label=2, average="macro" ) def test_geometric_mean_support_binary(): y_true, y_pred, _ = make_prediction(binary=True) # compute the geometric mean for the binary problem geo_mean = geometric_mean_score(y_true, y_pred) assert_allclose(geo_mean, 0.77, rtol=R_TOL) @pytest.mark.filterwarnings("ignore:Recall is ill-defined") @pytest.mark.parametrize( "y_true, y_pred, correction, expected_gmean", [ ([0, 0, 1, 1], [0, 0, 1, 1], 0.0, 1.0), ([0, 0, 0, 0], [1, 1, 1, 1], 0.0, 0.0), ([0, 0, 0, 0], [0, 0, 0, 0], 0.001, 1.0), ([0, 0, 0, 0], [1, 1, 1, 1], 0.001, 0.001), ([0, 0, 1, 1], [0, 1, 1, 0], 0.001, 0.5), ( [0, 1, 2, 0, 1, 2], [0, 2, 1, 0, 0, 1], 0.001, (0.001**2) ** (1 / 3), ), ([0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], 0.001, 1), ([0, 1, 1, 1, 1, 0], [0, 0, 1, 1, 1, 1], 0.001, (0.5 * 0.75) ** 0.5), ], ) def test_geometric_mean_multiclass(y_true, y_pred, correction, expected_gmean): gmean = geometric_mean_score(y_true, y_pred, correction=correction) assert gmean == pytest.approx(expected_gmean, rel=R_TOL) @pytest.mark.filterwarnings("ignore:Recall is ill-defined") @pytest.mark.parametrize( "y_true, y_pred, average, expected_gmean", [ ([0, 1, 2, 0, 1, 2], [0, 2, 1, 0, 0, 1], "macro", 0.471), ([0, 1, 2, 0, 1, 2], [0, 2, 1, 0, 0, 1], "micro", 0.471), ([0, 1, 2, 0, 1, 2], [0, 2, 1, 0, 0, 1], "weighted", 0.471), ([0, 1, 2, 0, 1, 2], [0, 2, 1, 0, 0, 1], None, [0.8660254, 0.0, 0.0]), ], ) def test_geometric_mean_average(y_true, y_pred, average, expected_gmean): gmean = geometric_mean_score(y_true, y_pred, average=average) assert gmean == pytest.approx(expected_gmean, rel=R_TOL) @pytest.mark.parametrize( "y_true, y_pred, sample_weight, average, expected_gmean", [ ([0, 1, 2, 0, 1, 2], [0, 1, 1, 0, 0, 1], None, "multiclass", 0.707), ( [0, 1, 2, 0, 1, 2], [0, 1, 1, 0, 0, 1], [1, 2, 1, 1, 2, 1], "multiclass", 0.707, ), ( [0, 1, 2, 0, 1, 2], [0, 1, 1, 0, 0, 1], [1, 2, 1, 1, 2, 1], "weighted", 0.333, ), ], ) def test_geometric_mean_sample_weight( y_true, y_pred, sample_weight, average, expected_gmean ): gmean = geometric_mean_score( y_true, y_pred, labels=[0, 1], sample_weight=sample_weight, average=average, ) assert gmean == pytest.approx(expected_gmean, rel=R_TOL) @pytest.mark.parametrize( "average, expected_gmean", [ ("multiclass", 0.36), (None, [0.82, 0.24, 0.72]), ("macro", 0.67), ("weighted", 0.64), ], ) def test_geometric_mean_score_prediction(average, expected_gmean): y_true, y_pred, _ = make_prediction(binary=False) gmean = geometric_mean_score(y_true, y_pred, average=average) assert gmean == pytest.approx(expected_gmean, rel=R_TOL) def test_iba_geo_mean_binary(): y_true, y_pred, _ = make_prediction(binary=True) iba_gmean = make_index_balanced_accuracy(alpha=0.5, squared=True)( geometric_mean_score ) iba = iba_gmean(y_true, y_pred) assert_allclose(iba, 0.5948, rtol=R_TOL) def _format_report(report): return " ".join(report.split()) def test_classification_report_imbalanced_multiclass(): iris = datasets.load_iris() y_true, y_pred, _ = make_prediction(dataset=iris, binary=False) # print classification report with class names expected_report = ( "pre rec spe f1 geo iba sup setosa 0.70 0.79 0.84 " "0.75 0.82 0.66 24 versicolor 0.29 0.06 0.89 0.11 " "0.24 0.05 31 virginica 0.44 0.90 0.58 0.59 0.72 " "0.54 20 avg / total 0.46 0.52 0.79 0.44 0.55 0.38 75" ) report = classification_report_imbalanced( y_true, y_pred, labels=np.arange(len(iris.target_names)), target_names=iris.target_names, ) assert _format_report(report) == expected_report # print classification report with label detection expected_report = ( "pre rec spe f1 geo iba sup 0 0.70 0.79 0.84 0.75 " "0.82 0.66 24 1 0.29 0.06 0.89 0.11 0.24 0.05 31 " "2 0.44 0.90 0.58 0.59 0.72 0.54 20 avg / total " "0.46 0.52 0.79 0.44 0.55 0.38 75" ) report = classification_report_imbalanced(y_true, y_pred) assert _format_report(report) == expected_report def test_classification_report_imbalanced_multiclass_with_digits(): iris = datasets.load_iris() y_true, y_pred, _ = make_prediction(dataset=iris, binary=False) # print classification report with class names expected_report = ( "pre rec spe f1 geo iba sup setosa 0.70370 0.79167 " "0.84314 0.74510 0.81700 0.66405 24 versicolor " "0.28571 0.06452 0.88636 0.10526 0.23913 0.05249 " "31 virginica 0.43902 0.90000 0.58182 0.59016 0.72363 " "0.54030 20 avg / total 0.46035 0.52000 0.79132 " "0.43932 0.55325 0.37827 75" ) report = classification_report_imbalanced( y_true, y_pred, labels=np.arange(len(iris.target_names)), target_names=iris.target_names, digits=5, ) assert _format_report(report) == expected_report # print classification report with label detection expected_report = ( "pre rec spe f1 geo iba sup 0 0.70 0.79 0.84 0.75 " "0.82 0.66 24 1 0.29 0.06 0.89 0.11 0.24 0.05 31 " "2 0.44 0.90 0.58 0.59 0.72 0.54 20 avg / total 0.46 " "0.52 0.79 0.44 0.55 0.38 75" ) report = classification_report_imbalanced(y_true, y_pred) assert _format_report(report) == expected_report def test_classification_report_imbalanced_multiclass_with_string_label(): y_true, y_pred, _ = make_prediction(binary=False) y_true = np.array(["blue", "green", "red"])[y_true] y_pred = np.array(["blue", "green", "red"])[y_pred] expected_report = ( "pre rec spe f1 geo iba sup blue 0.70 0.79 0.84 0.75 " "0.82 0.66 24 green 0.29 0.06 0.89 0.11 0.24 0.05 31 " "red 0.44 0.90 0.58 0.59 0.72 0.54 20 avg / total " "0.46 0.52 0.79 0.44 0.55 0.38 75" ) report = classification_report_imbalanced(y_true, y_pred) assert _format_report(report) == expected_report expected_report = ( "pre rec spe f1 geo iba sup a 0.70 0.79 0.84 0.75 0.82 " "0.66 24 b 0.29 0.06 0.89 0.11 0.24 0.05 31 c 0.44 " "0.90 0.58 0.59 0.72 0.54 20 avg / total 0.46 0.52 " "0.79 0.44 0.55 0.38 75" ) report = classification_report_imbalanced( y_true, y_pred, target_names=["a", "b", "c"] ) assert _format_report(report) == expected_report def test_classification_report_imbalanced_multiclass_with_unicode_label(): y_true, y_pred, _ = make_prediction(binary=False) labels = np.array(["blue\xa2", "green\xa2", "red\xa2"]) y_true = labels[y_true] y_pred = labels[y_pred] expected_report = ( "pre rec spe f1 geo iba sup blue¢ 0.70 0.79 0.84 0.75 " "0.82 0.66 24 green¢ 0.29 0.06 0.89 0.11 0.24 0.05 31 " "red¢ 0.44 0.90 0.58 0.59 0.72 0.54 20 avg / total " "0.46 0.52 0.79 0.44 0.55 0.38 75" ) report = classification_report_imbalanced(y_true, y_pred) assert _format_report(report) == expected_report def test_classification_report_imbalanced_multiclass_with_long_string_label(): y_true, y_pred, _ = make_prediction(binary=False) labels = np.array(["blue", "green" * 5, "red"]) y_true = labels[y_true] y_pred = labels[y_pred] expected_report = ( "pre rec spe f1 geo iba sup blue 0.70 0.79 0.84 0.75 " "0.82 0.66 24 greengreengreengreengreen 0.29 0.06 " "0.89 0.11 0.24 0.05 31 red 0.44 0.90 0.58 0.59 0.72 " "0.54 20 avg / total 0.46 0.52 0.79 0.44 0.55 0.38 75" ) report = classification_report_imbalanced(y_true, y_pred) assert _format_report(report) == expected_report @pytest.mark.parametrize( "score, expected_score", [ (accuracy_score, 0.54756), (jaccard_score, 0.33176), (precision_score, 0.65025), (recall_score, 0.41616), ], ) def test_iba_sklearn_metrics(score, expected_score): y_true, y_pred, _ = make_prediction(binary=True) score_iba = make_index_balanced_accuracy(alpha=0.5, squared=True)(score) score = score_iba(y_true, y_pred) assert score == pytest.approx(expected_score) @pytest.mark.parametrize( "score_loss", [average_precision_score, brier_score_loss, cohen_kappa_score, roc_auc_score], ) def test_iba_error_y_score_prob_error(score_loss): y_true, y_pred, _ = make_prediction(binary=True) aps = make_index_balanced_accuracy(alpha=0.5, squared=True)(score_loss) with pytest.raises((AttributeError, TypeError)): aps(y_true, y_pred) def test_classification_report_imbalanced_dict_with_target_names(): iris = datasets.load_iris() y_true, y_pred, _ = make_prediction(dataset=iris, binary=False) report = classification_report_imbalanced( y_true, y_pred, labels=np.arange(len(iris.target_names)), target_names=iris.target_names, output_dict=True, ) outer_keys = set(report.keys()) inner_keys = set(report["setosa"].keys()) expected_outer_keys = { "setosa", "versicolor", "virginica", "avg_pre", "avg_rec", "avg_spe", "avg_f1", "avg_geo", "avg_iba", "total_support", } expected_inner_keys = {"spe", "f1", "sup", "rec", "geo", "iba", "pre"} assert outer_keys == expected_outer_keys assert inner_keys == expected_inner_keys def test_classification_report_imbalanced_dict_without_target_names(): iris = datasets.load_iris() y_true, y_pred, _ = make_prediction(dataset=iris, binary=False) report = classification_report_imbalanced( y_true, y_pred, labels=np.arange(len(iris.target_names)), output_dict=True, ) outer_keys = set(report.keys()) inner_keys = set(report["0"].keys()) expected_outer_keys = { "0", "1", "2", "avg_pre", "avg_rec", "avg_spe", "avg_f1", "avg_geo", "avg_iba", "total_support", } expected_inner_keys = {"spe", "f1", "sup", "rec", "geo", "iba", "pre"} assert outer_keys == expected_outer_keys assert inner_keys == expected_inner_keys @pytest.mark.parametrize( "y_true, y_pred, expected_ma_mae", [ ([1, 1, 1, 2, 2, 2], [1, 2, 1, 2, 1, 2], 0.333), ([1, 1, 1, 1, 1, 2], [1, 2, 1, 2, 1, 2], 0.2), ([1, 1, 1, 2, 2, 2, 3, 3, 3], [1, 3, 1, 2, 1, 1, 2, 3, 3], 0.555), ([1, 1, 1, 1, 1, 1, 2, 3, 3], [1, 3, 1, 2, 1, 1, 2, 3, 3], 0.166), ], ) def test_macro_averaged_mean_absolute_error(y_true, y_pred, expected_ma_mae): ma_mae = macro_averaged_mean_absolute_error(y_true, y_pred) assert ma_mae == pytest.approx(expected_ma_mae, rel=R_TOL) def test_macro_averaged_mean_absolute_error_sample_weight(): y_true = [1, 1, 1, 2, 2, 2] y_pred = [1, 2, 1, 2, 1, 2] ma_mae_no_weights = macro_averaged_mean_absolute_error(y_true, y_pred) sample_weight = [1, 1, 1, 1, 1, 1] ma_mae_unit_weights = macro_averaged_mean_absolute_error( y_true, y_pred, sample_weight=sample_weight, ) assert ma_mae_unit_weights == pytest.approx(ma_mae_no_weights) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/metrics/tests/test_pairwise.py000066400000000000000000000144021521136113700310340ustar00rootroot00000000000000"""Test for the metrics that perform pairwise distance computation.""" # Authors: Guillaume Lemaitre # License: MIT import numpy as np import pytest from sklearn.exceptions import NotFittedError from sklearn.preprocessing import LabelEncoder, OrdinalEncoder from sklearn_compat.utils._testing import _convert_container from imblearn.metrics.pairwise import ValueDifferenceMetric @pytest.fixture def data(): rng = np.random.RandomState(0) feature_1 = ["A"] * 10 + ["B"] * 20 + ["C"] * 30 feature_2 = ["A"] * 40 + ["B"] * 20 feature_3 = ["A"] * 20 + ["B"] * 20 + ["C"] * 10 + ["D"] * 10 X = np.array([feature_1, feature_2, feature_3], dtype=object).T rng.shuffle(X) y = rng.randint(low=0, high=2, size=X.shape[0]) y_labels = np.array(["not apple", "apple"], dtype=object) y = y_labels[y] return X, y @pytest.mark.parametrize("dtype", [np.int32, np.int64, np.float32, np.float64]) @pytest.mark.parametrize("k, r", [(1, 1), (1, 2), (2, 1), (2, 2)]) @pytest.mark.parametrize("y_type", ["list", "array"]) @pytest.mark.parametrize("encode_label", [True, False]) def test_value_difference_metric(data, dtype, k, r, y_type, encode_label): # Check basic feature of the metric: # * the shape of the distance matrix is (n_samples, n_samples) # * computing pairwise distance of X is the same than explicitely between # X and X. X, y = data y = _convert_container(y, y_type) if encode_label: y = LabelEncoder().fit_transform(y) encoder = OrdinalEncoder(dtype=dtype) X_encoded = encoder.fit_transform(X) vdm = ValueDifferenceMetric(k=k, r=r) vdm.fit(X_encoded, y) dist_1 = vdm.pairwise(X_encoded) dist_2 = vdm.pairwise(X_encoded, X_encoded) np.testing.assert_allclose(dist_1, dist_2) assert dist_1.shape == (X.shape[0], X.shape[0]) assert dist_2.shape == (X.shape[0], X.shape[0]) @pytest.mark.parametrize("dtype", [np.int32, np.int64, np.float32, np.float64]) @pytest.mark.parametrize("k, r", [(1, 1), (1, 2), (2, 1), (2, 2)]) @pytest.mark.parametrize("y_type", ["list", "array"]) @pytest.mark.parametrize("encode_label", [True, False]) def test_value_difference_metric_property(dtype, k, r, y_type, encode_label): # Check the property of the vdm distance. Let's check the property # described in "Improved Heterogeneous Distance Functions", D.R. Wilson and # T.R. Martinez, Journal of Artificial Intelligence Research 6 (1997) 1-34 # https://arxiv.org/pdf/cs/9701101.pdf # # "if an attribute color has three values red, green and blue, and the # application is to identify whether or not an object is an apple, red and # green would be considered closer than red and blue because the former two # both have similar correlations with the output class apple." # defined our feature X = np.array(["green"] * 10 + ["red"] * 10 + ["blue"] * 10).reshape(-1, 1) # 0 - not an apple / 1 - an apple y = np.array([1] * 8 + [0] * 5 + [1] * 7 + [0] * 9 + [1]) y_labels = np.array(["not apple", "apple"], dtype=object) y = y_labels[y] y = _convert_container(y, y_type) if encode_label: y = LabelEncoder().fit_transform(y) encoder = OrdinalEncoder(dtype=dtype) X_encoded = encoder.fit_transform(X) vdm = ValueDifferenceMetric(k=k, r=r) vdm.fit(X_encoded, y) sample_green = encoder.transform([["green"]]) sample_red = encoder.transform([["red"]]) sample_blue = encoder.transform([["blue"]]) for sample in (sample_green, sample_red, sample_blue): # computing the distance between a sample of the same category should # give a null distance dist = vdm.pairwise(sample).squeeze() assert dist == pytest.approx(0) # check the property explained in the introduction example dist_1 = vdm.pairwise(sample_green, sample_red).squeeze() dist_2 = vdm.pairwise(sample_blue, sample_red).squeeze() dist_3 = vdm.pairwise(sample_blue, sample_green).squeeze() # green and red are very close # blue is closer to red than green assert dist_1 < dist_2 assert dist_1 < dist_3 assert dist_2 < dist_3 def test_value_difference_metric_categories(data): # Check that "auto" is equivalent to provide the number categories # beforehand X, y = data encoder = OrdinalEncoder(dtype=np.int32) X_encoded = encoder.fit_transform(X) n_categories = np.array([len(cat) for cat in encoder.categories_]) vdm_auto = ValueDifferenceMetric().fit(X_encoded, y) vdm_categories = ValueDifferenceMetric(n_categories=n_categories) vdm_categories.fit(X_encoded, y) np.testing.assert_array_equal(vdm_auto.n_categories_, n_categories) np.testing.assert_array_equal(vdm_auto.n_categories_, vdm_categories.n_categories_) def test_value_difference_metric_categories_error(data): # Check that we raise an error if n_categories is inconsistent with the # number of features in X X, y = data encoder = OrdinalEncoder(dtype=np.int32) X_encoded = encoder.fit_transform(X) n_categories = [1, 2] vdm = ValueDifferenceMetric(n_categories=n_categories) err_msg = "The length of n_categories is not consistent with the number" with pytest.raises(ValueError, match=err_msg): vdm.fit(X_encoded, y) def test_value_difference_metric_missing_categories(data): # Check that we don't get issue when a category is missing between 0 # n_categories - 1 X, y = data encoder = OrdinalEncoder(dtype=np.int32) X_encoded = encoder.fit_transform(X) n_categories = np.array([len(cat) for cat in encoder.categories_]) # remove a categories that could be between 0 and n_categories X_encoded[X_encoded[:, -1] == 1] = 0 np.testing.assert_array_equal(np.unique(X_encoded[:, -1]), [0, 2, 3]) vdm = ValueDifferenceMetric(n_categories=n_categories) vdm.fit(X_encoded, y) for n_cats, proba in zip(n_categories, vdm.proba_per_class_): assert proba.shape == (n_cats, len(np.unique(y))) def test_value_difference_value_unfitted(data): # Check that we raise a NotFittedError when `fit` is not not called before # pairwise. X, y = data encoder = OrdinalEncoder(dtype=np.int32) X_encoded = encoder.fit_transform(X) with pytest.raises(NotFittedError): ValueDifferenceMetric().pairwise(X_encoded) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/metrics/tests/test_score_objects.py000066400000000000000000000040531521136113700320360ustar00rootroot00000000000000"""Test for score""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import pytest from sklearn.datasets import make_blobs from sklearn.linear_model import LogisticRegression from sklearn.metrics import make_scorer from sklearn.model_selection import GridSearchCV, train_test_split from imblearn.metrics import ( geometric_mean_score, make_index_balanced_accuracy, sensitivity_score, specificity_score, ) R_TOL = 1e-2 @pytest.fixture def data(): X, y = make_blobs(random_state=0, centers=2) return train_test_split(X, y, random_state=0) @pytest.mark.parametrize( "score, expected_score", [ (sensitivity_score, 0.90), (specificity_score, 0.90), (geometric_mean_score, 0.90), (make_index_balanced_accuracy()(geometric_mean_score), 0.82), ], ) @pytest.mark.parametrize("average", ["macro", "weighted", "micro"]) def test_scorer_common_average(data, score, expected_score, average): X_train, X_test, y_train, _ = data scorer = make_scorer(score, pos_label=None, average=average) grid = GridSearchCV( LogisticRegression(), param_grid={"C": [1, 10]}, scoring=scorer, cv=3, ) grid.fit(X_train, y_train).predict(X_test) assert grid.best_score_ >= expected_score @pytest.mark.parametrize( "score, average, expected_score", [ (sensitivity_score, "binary", 0.94), (specificity_score, "binary", 0.89), (geometric_mean_score, "multiclass", 0.90), ( make_index_balanced_accuracy()(geometric_mean_score), "multiclass", 0.82, ), ], ) def test_scorer_default_average(data, score, average, expected_score): X_train, X_test, y_train, _ = data scorer = make_scorer(score, pos_label=1, average=average) grid = GridSearchCV( LogisticRegression(), param_grid={"C": [1, 10]}, scoring=scorer, cv=3, ) grid.fit(X_train, y_train).predict(X_test) assert grid.best_score_ >= expected_score scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/model_selection/000077500000000000000000000000001521136113700261345ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/model_selection/__init__.py000066400000000000000000000003211521136113700302410ustar00rootroot00000000000000""" The :mod:`imblearn.model_selection` provides methods to split the dataset into training and test sets. """ from imblearn.model_selection._split import InstanceHardnessCV __all__ = ["InstanceHardnessCV"] scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/model_selection/_split.py000066400000000000000000000104621521136113700300030ustar00rootroot00000000000000import warnings import numpy as np from sklearn.base import clone from sklearn.model_selection import LeaveOneGroupOut, cross_val_predict from sklearn.model_selection._split import BaseCrossValidator from sklearn.utils.multiclass import type_of_target from sklearn.utils.validation import _num_samples class InstanceHardnessCV(BaseCrossValidator): """Instance-hardness cross-validation splitter. Cross-validation splitter that distributes samples with large instance hardness equally over the folds. The instance hardness is internally estimated by using `estimator` and stratified cross-validation. Read more in the :ref:`User Guide `. Parameters ---------- estimator : estimator object Classifier to be used to estimate instance hardness of the samples. This classifier should implement `predict_proba`. n_splits : int, default=5 Number of folds. Must be at least 2. pos_label : int, float, bool or str, default=None The class considered the positive class when selecting the probability representing the instance hardness. If None, the positive class is automatically inferred by the estimator as `estimator.classes_[1]`. Examples -------- >>> from imblearn.model_selection import InstanceHardnessCV >>> from sklearn.datasets import make_classification >>> from sklearn.model_selection import cross_validate >>> from sklearn.linear_model import LogisticRegression >>> X, y = make_classification(weights=[0.9, 0.1], class_sep=2, ... n_informative=3, n_redundant=1, flip_y=0.05, n_samples=1000, random_state=10) >>> estimator = LogisticRegression() >>> ih_cv = InstanceHardnessCV(estimator) >>> cv_result = cross_validate(estimator, X, y, cv=ih_cv) >>> print(f"Standard deviation of test_scores: {cv_result['test_score'].std():.3f}") Standard deviation of test_scores: 0.00... """ def __init__(self, estimator, *, n_splits=5, pos_label=None): self.estimator = estimator self.n_splits = n_splits self.pos_label = pos_label def split(self, X, y, groups=None): """Generate indices to split data into training and test set. Parameters ---------- X : array-like of shape (n_samples, n_features) Training data, where `n_samples` is the number of samples and `n_features` is the number of features. y : array-like of shape (n_samples,) The target variable for supervised learning problems. groups : object Always ignored, exists for compatibility. Yields ------ train : ndarray The training set indices for that split. test : ndarray The testing set indices for that split. """ if groups is not None: warnings.warn( f"The groups parameter is ignored by {self.__class__.__name__}", UserWarning, ) classes = np.unique(y) y_type = type_of_target(y) if y_type != "binary": raise ValueError("InstanceHardnessCV only supports binary classification.") if self.pos_label is None: pos_label = 1 else: pos_label = np.flatnonzero(classes == self.pos_label)[0] y_proba = cross_val_predict( clone(self.estimator), X, y, cv=self.n_splits, method="predict_proba" ) # sorting first on y and then by the instance hardness sorted_indices = np.lexsort((y_proba[:, pos_label], y)) groups = np.empty(_num_samples(X), dtype=int) groups[sorted_indices] = np.arange(_num_samples(X)) % self.n_splits cv = LeaveOneGroupOut() yield from cv.split(X, y, groups) def get_n_splits(self, X=None, y=None, groups=None): """Returns the number of splitting iterations in the cross-validator. Parameters ---------- X: object Always ignored, exists for compatibility. y: object Always ignored, exists for compatibility. groups: object Always ignored, exists for compatibility. Returns ------- n_splits: int Returns the number of splitting iterations in the cross-validator. """ return self.n_splits scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/model_selection/tests/000077500000000000000000000000001521136113700272765ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/model_selection/tests/__init__.py000066400000000000000000000000001521136113700313750ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/model_selection/tests/test_split.py000066400000000000000000000064201521136113700320440ustar00rootroot00000000000000import numpy as np import pytest from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.metrics import make_scorer, precision_score from sklearn.model_selection import cross_validate from sklearn.utils._testing import assert_allclose from imblearn.model_selection import InstanceHardnessCV @pytest.fixture def data(): return make_classification( weights=[0.5, 0.5], class_sep=0.5, n_informative=3, n_redundant=1, flip_y=0.05, n_samples=50, random_state=10, ) def test_groups_parameter_warning(data): """Test that a warning is raised when groups parameter is provided.""" X, y = data ih_cv = InstanceHardnessCV(estimator=LogisticRegression(), n_splits=3) warning_msg = "The groups parameter is ignored by InstanceHardnessCV" with pytest.warns(UserWarning, match=warning_msg): list(ih_cv.split(X, y, groups=np.ones_like(y))) def test_error_on_multiclass(): """Test that an error is raised when the target is not binary.""" X, y = make_classification(n_classes=3, n_clusters_per_class=1) err_msg = "InstanceHardnessCV only supports binary classification." with pytest.raises(ValueError, match=err_msg): next(InstanceHardnessCV(estimator=LogisticRegression()).split(X, y)) def test_default_params(data): """Test that the default parameters are used.""" X, y = data ih_cv = InstanceHardnessCV(estimator=LogisticRegression(), n_splits=3) cv_result = cross_validate( LogisticRegression(), X, y, cv=ih_cv, scoring="precision" ) assert_allclose(cv_result["test_score"], [0.625, 0.6, 0.625], atol=1e-6, rtol=1e-6) @pytest.mark.parametrize("dtype_target", [None, object]) def test_target_string_labels(data, dtype_target): """Test that the target can be a string array.""" X, y = data labels = np.array(["a", "b"], dtype=dtype_target) y = labels[y] ih_cv = InstanceHardnessCV(estimator=LogisticRegression(), n_splits=3) cv_result = cross_validate( LogisticRegression(), X, y, cv=ih_cv, scoring=make_scorer(precision_score, pos_label="b"), ) assert_allclose(cv_result["test_score"], [0.625, 0.6, 0.625], atol=1e-6, rtol=1e-6) @pytest.mark.parametrize("dtype_target", [None, object]) def test_target_string_pos_label(data, dtype_target): """Test that the `pos_label` parameter can be used to select the positive class. Here, changing the `pos_label` will change the instance hardness and thus the `cv_result`. """ X, y = data labels = np.array(["a", "b"], dtype=dtype_target) y = labels[y] ih_cv = InstanceHardnessCV( estimator=LogisticRegression(), pos_label="a", n_splits=3 ) cv_result = cross_validate( LogisticRegression(), X, y, cv=ih_cv, scoring=make_scorer(precision_score, pos_label="a"), ) assert_allclose( cv_result["test_score"], [0.666667, 0.666667, 0.4], atol=1e-6, rtol=1e-6 ) @pytest.mark.parametrize("n_splits", [2, 3, 4]) def test_n_splits(n_splits): """Test that the number of splits is correctly set.""" ih_cv = InstanceHardnessCV(estimator=LogisticRegression(), n_splits=n_splits) assert ih_cv.get_n_splits() == n_splits scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/000077500000000000000000000000001521136113700256345ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/__init__.py000066400000000000000000000007721521136113700277530ustar00rootroot00000000000000""" The :mod:`imblearn.over_sampling` provides a set of method to perform over-sampling. """ from imblearn.over_sampling._adasyn import ADASYN from imblearn.over_sampling._random_over_sampler import RandomOverSampler from imblearn.over_sampling._smote import ( SMOTE, SMOTEN, SMOTENC, SVMSMOTE, BorderlineSMOTE, KMeansSMOTE, ) __all__ = [ "ADASYN", "RandomOverSampler", "KMeansSMOTE", "SMOTE", "BorderlineSMOTE", "SVMSMOTE", "SMOTENC", "SMOTEN", ] scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/_adasyn.py000066400000000000000000000172531521136113700276340ustar00rootroot00000000000000"""Class to perform over-sampling using ADASYN.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numbers import numpy as np from scipy import sparse from sklearn.utils import _safe_indexing, check_random_state from sklearn.utils._param_validation import HasMethods, Interval from imblearn.over_sampling.base import BaseOverSampler from imblearn.utils import Substitution, check_neighbors_object from imblearn.utils._docstring import _random_state_docstring @Substitution( sampling_strategy=BaseOverSampler._sampling_strategy_docstring, random_state=_random_state_docstring, ) class ADASYN(BaseOverSampler): """Oversample using Adaptive Synthetic (ADASYN) algorithm. This method is similar to SMOTE but it generates different number of samples depending on an estimate of the local distribution of the class to be oversampled. Read more in the :ref:`User Guide `. Parameters ---------- {sampling_strategy} {random_state} n_neighbors : int or estimator object, default=5 The nearest neighbors used to define the neighborhood of samples to use to generate the synthetic samples. You can pass: - an `int` corresponding to the number of neighbors to use. A `~sklearn.neighbors.NearestNeighbors` instance will be fitted in this case. - an instance of a compatible nearest neighbors algorithm that should implement both methods `kneighbors` and `kneighbors_graph`. For instance, it could correspond to a :class:`~sklearn.neighbors.NearestNeighbors` but could be extended to any compatible class. Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. nn_ : estimator object Validated K-nearest Neighbours estimator linked to the parameter `n_neighbors`. n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- SMOTE : Over-sample using SMOTE. SMOTENC : Over-sample using SMOTE for continuous and categorical features. SMOTEN : Over-sample using the SMOTE variant specifically for categorical features only. SVMSMOTE : Over-sample using SVM-SMOTE variant. BorderlineSMOTE : Over-sample using Borderline-SMOTE variant. Notes ----- The implementation is based on [1]_. Supports multi-class resampling. A one-vs.-rest scheme is used. References ---------- .. [1] He, Haibo, Yang Bai, Edwardo A. Garcia, and Shutao Li. "ADASYN: Adaptive synthetic sampling approach for imbalanced learning," In IEEE International Joint Conference on Neural Networks (IEEE World Congress on Computational Intelligence), pp. 1322-1328, 2008. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from imblearn.over_sampling import ADASYN >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, ... random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> ada = ADASYN(random_state=42) >>> X_res, y_res = ada.fit_resample(X, y) >>> print('Resampled dataset shape %s' % Counter(y_res)) Resampled dataset shape Counter({{0: 904, 1: 900}}) """ _parameter_constraints: dict = { **BaseOverSampler._parameter_constraints, "n_neighbors": [ Interval(numbers.Integral, 1, None, closed="left"), HasMethods(["kneighbors", "kneighbors_graph"]), ], } def __init__( self, *, sampling_strategy="auto", random_state=None, n_neighbors=5, ): super().__init__(sampling_strategy=sampling_strategy) self.random_state = random_state self.n_neighbors = n_neighbors def _validate_estimator(self): """Create the necessary objects for ADASYN""" self.nn_ = check_neighbors_object( "n_neighbors", self.n_neighbors, additional_neighbor=1 ) def _fit_resample(self, X, y): self._validate_estimator() random_state = check_random_state(self.random_state) X_resampled = [X.copy()] y_resampled = [y.copy()] for class_sample, n_samples in self.sampling_strategy_.items(): if n_samples == 0: continue target_class_indices = np.flatnonzero(y == class_sample) X_class = _safe_indexing(X, target_class_indices) self.nn_.fit(X) nns = self.nn_.kneighbors(X_class, return_distance=False)[:, 1:] # The ratio is computed using a one-vs-rest manner. Using majority # in multi-class would lead to slightly different results at the # cost of introducing a new parameter. n_neighbors = self.nn_.n_neighbors - 1 ratio_nn = np.sum(y[nns] != class_sample, axis=1) / n_neighbors if not np.sum(ratio_nn): raise RuntimeError( "Not any neigbours belong to the majority" " class. This case will induce a NaN case" " with a division by zero. ADASYN is not" " suited for this specific dataset." " Use SMOTE instead." ) ratio_nn /= np.sum(ratio_nn) n_samples_generate = np.rint(ratio_nn * n_samples).astype(int) # rounding may cause new amount for n_samples n_samples = np.sum(n_samples_generate) if not n_samples: raise ValueError( "No samples will be generated with the provided ratio settings." ) # the nearest neighbors need to be fitted only on the current class # to find the class NN to generate new samples self.nn_.fit(X_class) nns = self.nn_.kneighbors(X_class, return_distance=False)[:, 1:] enumerated_class_indices = np.arange(len(target_class_indices)) rows = np.repeat(enumerated_class_indices, n_samples_generate) cols = random_state.choice(n_neighbors, size=n_samples) diffs = X_class[nns[rows, cols]] - X_class[rows] steps = random_state.uniform(size=(n_samples, 1)) if sparse.issparse(X): sparse_func = type(X).__name__ steps = getattr(sparse, sparse_func)(steps) X_new = X_class[rows] + steps.multiply(diffs) else: X_new = X_class[rows] + steps * diffs X_new = X_new.astype(X.dtype) y_new = np.full(n_samples, fill_value=class_sample, dtype=y.dtype) X_resampled.append(X_new) y_resampled.append(y_new) if sparse.issparse(X): X_resampled = sparse.vstack(X_resampled, format=X.format) else: X_resampled = np.vstack(X_resampled) y_resampled = np.hstack(y_resampled) return X_resampled, y_resampled def _more_tags(self): return { "X_types": ["2darray"], } def __sklearn_tags__(self): tags = super().__sklearn_tags__() return tags scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/_random_over_sampler.py000066400000000000000000000231771521136113700324150ustar00rootroot00000000000000"""Class to perform random over-sampling.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT from collections.abc import Mapping from numbers import Real import numpy as np from scipy import sparse from sklearn.utils import _safe_indexing, check_array, check_random_state from sklearn.utils._param_validation import Interval from sklearn.utils.sparsefuncs import mean_variance_axis from sklearn_compat.utils.validation import validate_data from imblearn.over_sampling.base import BaseOverSampler from imblearn.utils import Substitution, check_target_type from imblearn.utils._docstring import _random_state_docstring from imblearn.utils._validation import _check_X @Substitution( sampling_strategy=BaseOverSampler._sampling_strategy_docstring, random_state=_random_state_docstring, ) class RandomOverSampler(BaseOverSampler): """Class to perform random over-sampling. Object to over-sample the minority class(es) by picking samples at random with replacement. The bootstrap can be generated in a smoothed manner. Read more in the :ref:`User Guide `. Parameters ---------- {sampling_strategy} {random_state} shrinkage : float or dict, default=None Parameter controlling the shrinkage applied to the covariance matrix. when a smoothed bootstrap is generated. The options are: - if `None`, a normal bootstrap will be generated without perturbation. It is equivalent to `shrinkage=0` as well; - if a `float` is given, the shrinkage factor will be used for all classes to generate the smoothed bootstrap; - if a `dict` is given, the shrinkage factor will specific for each class. The key correspond to the targeted class and the value is the shrinkage factor. The value needs of the shrinkage parameter needs to be higher or equal to 0. .. versionadded:: 0.8 Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. sample_indices_ : ndarray of shape (n_new_samples,) Indices of the samples selected. .. versionadded:: 0.4 shrinkage_ : dict or None The per-class shrinkage factor used to generate the smoothed bootstrap sample. When `shrinkage=None` a normal bootstrap will be generated. .. versionadded:: 0.8 n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- BorderlineSMOTE : Over-sample using the borderline-SMOTE variant. SMOTE : Over-sample using SMOTE. SMOTENC : Over-sample using SMOTE for continuous and categorical features. SMOTEN : Over-sample using the SMOTE variant specifically for categorical features only. SVMSMOTE : Over-sample using SVM-SMOTE variant. ADASYN : Over-sample using ADASYN. KMeansSMOTE : Over-sample applying a clustering before to oversample using SMOTE. Notes ----- Supports multi-class resampling by sampling each class independently. Supports heterogeneous data as object array containing string and numeric data. When generating a smoothed bootstrap, this method is also known as Random Over-Sampling Examples (ROSE) [1]_. .. warning:: Since smoothed bootstrap are generated by adding a small perturbation to the drawn samples, this method is not adequate when working with sparse matrices. References ---------- .. [1] G Menardi, N. Torelli, "Training and assessing classification rules with imbalanced data," Data Mining and Knowledge Discovery, 28(1), pp.92-122, 2014. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from imblearn.over_sampling import RandomOverSampler >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> ros = RandomOverSampler(random_state=42) >>> X_res, y_res = ros.fit_resample(X, y) >>> print('Resampled dataset shape %s' % Counter(y_res)) Resampled dataset shape Counter({{0: 900, 1: 900}}) """ _parameter_constraints: dict = { **BaseOverSampler._parameter_constraints, "shrinkage": [Interval(Real, 0, None, closed="left"), dict, None], } def __init__( self, *, sampling_strategy="auto", random_state=None, shrinkage=None, ): super().__init__(sampling_strategy=sampling_strategy) self.random_state = random_state self.shrinkage = shrinkage def _check_X_y(self, X, y): y, binarize_y = check_target_type(y, indicate_one_vs_all=True) X = _check_X(X) validate_data(self, X=X, y=y, reset=True, skip_check_array=True) return X, y, binarize_y def _fit_resample(self, X, y): random_state = check_random_state(self.random_state) if isinstance(self.shrinkage, Real): self.shrinkage_ = { klass: self.shrinkage for klass in self.sampling_strategy_ } elif self.shrinkage is None or isinstance(self.shrinkage, Mapping): self.shrinkage_ = self.shrinkage if self.shrinkage_ is not None: missing_shrinkage_keys = ( self.sampling_strategy_.keys() - self.shrinkage_.keys() ) if missing_shrinkage_keys: raise ValueError( "`shrinkage` should contain a shrinkage factor for " "each class that will be resampled. The missing " f"classes are: {repr(missing_shrinkage_keys)}" ) for klass, shrink_factor in self.shrinkage_.items(): if shrink_factor < 0: raise ValueError( "The shrinkage factor needs to be >= 0. " f"Got {shrink_factor} for class {klass}." ) # smoothed bootstrap imposes to make numerical operation; we need # to be sure to have only numerical data in X try: X = check_array(X, accept_sparse=["csr", "csc"], dtype="numeric") except ValueError as exc: raise ValueError( "When shrinkage is not None, X needs to contain only " "numerical data to later generate a smoothed bootstrap " "sample." ) from exc X_resampled = [X.copy()] y_resampled = [y.copy()] sample_indices = range(X.shape[0]) for class_sample, num_samples in self.sampling_strategy_.items(): target_class_indices = np.flatnonzero(y == class_sample) bootstrap_indices = random_state.choice( target_class_indices, size=num_samples, replace=True, ) sample_indices = np.append(sample_indices, bootstrap_indices) if self.shrinkage_ is not None: # generate a smoothed bootstrap with a perturbation n_samples, n_features = X.shape smoothing_constant = (4 / ((n_features + 2) * n_samples)) ** ( 1 / (n_features + 4) ) if sparse.issparse(X): _, X_class_variance = mean_variance_axis( X[target_class_indices, :], axis=0, ) X_class_scale = np.sqrt(X_class_variance, out=X_class_variance) else: X_class_scale = np.std(X[target_class_indices, :], axis=0) smoothing_matrix = np.diagflat( self.shrinkage_[class_sample] * smoothing_constant * X_class_scale ) X_new = random_state.randn(num_samples, n_features) X_new = X_new.dot(smoothing_matrix) + X[bootstrap_indices, :] if sparse.issparse(X): X_new = sparse.csr_matrix(X_new, dtype=X.dtype) X_resampled.append(X_new) else: # generate a bootstrap X_resampled.append(_safe_indexing(X, bootstrap_indices)) y_resampled.append(_safe_indexing(y, bootstrap_indices)) self.sample_indices_ = np.array(sample_indices) if sparse.issparse(X): X_resampled = sparse.vstack(X_resampled, format=X.format) else: X_resampled = np.vstack(X_resampled) y_resampled = np.hstack(y_resampled) return X_resampled, y_resampled def _more_tags(self): return { "X_types": ["2darray", "string", "sparse", "dataframe"], "sample_indices": True, "allow_nan": True, "_xfail_checks": { "check_complex_data": "Robust to this type of data.", }, } def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.input_tags.allow_nan = True tags.input_tags.string = True tags.sampler_tags.sample_indices = True return tags scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/_smote/000077500000000000000000000000001521136113700271225ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/_smote/__init__.py000066400000000000000000000005021521136113700312300ustar00rootroot00000000000000from imblearn.over_sampling._smote.base import SMOTE, SMOTEN, SMOTENC from imblearn.over_sampling._smote.cluster import KMeansSMOTE from imblearn.over_sampling._smote.filter import SVMSMOTE, BorderlineSMOTE __all__ = [ "SMOTE", "SMOTEN", "SMOTENC", "KMeansSMOTE", "BorderlineSMOTE", "SVMSMOTE", ] scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/_smote/base.py000066400000000000000000001122631521136113700304130ustar00rootroot00000000000000"""Base class and original SMOTE methods for over-sampling""" # Authors: Guillaume Lemaitre # Fernando Nogueira # Christos Aridas # Dzianis Dudnik # License: MIT import math import numbers import warnings import numpy as np from scipy import sparse from scipy.stats import mode from sklearn.base import clone from sklearn.exceptions import DataConversionWarning from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder from sklearn.utils import ( _safe_indexing, check_array, check_random_state, ) from sklearn.utils._param_validation import HasMethods, Interval, StrOptions from sklearn.utils.sparsefuncs_fast import ( csr_mean_variance_axis0, ) from sklearn.utils.validation import _num_features from sklearn_compat.utils._dataframe import is_pandas_df from sklearn_compat.utils._indexing import _get_column_indices from sklearn_compat.utils.validation import validate_data from imblearn.metrics.pairwise import ValueDifferenceMetric from imblearn.over_sampling.base import BaseOverSampler from imblearn.utils import Substitution, check_neighbors_object, check_target_type from imblearn.utils._docstring import _random_state_docstring from imblearn.utils._validation import _check_X class BaseSMOTE(BaseOverSampler): """Base class for the different SMOTE algorithms.""" _parameter_constraints: dict = { **BaseOverSampler._parameter_constraints, "k_neighbors": [ Interval(numbers.Integral, 1, None, closed="left"), HasMethods(["kneighbors", "kneighbors_graph"]), ], } def __init__( self, sampling_strategy="auto", random_state=None, k_neighbors=5, ): super().__init__(sampling_strategy=sampling_strategy) self.random_state = random_state self.k_neighbors = k_neighbors def _validate_estimator(self): """Check the NN estimators shared across the different SMOTE algorithms. """ self.nn_k_ = check_neighbors_object( "k_neighbors", self.k_neighbors, additional_neighbor=1 ) def _make_samples( self, X, y_dtype, y_type, nn_data, nn_num, n_samples, step_size=1.0, y=None ): """A support function that returns artificial samples constructed along the line connecting nearest neighbours. Parameters ---------- X : {array-like, sparse matrix} of shape (n_samples, n_features) Points from which the points will be created. y_dtype : dtype The data type of the targets. y_type : str or int The minority target value, just so the function can return the target values for the synthetic variables with correct length in a clear format. nn_data : ndarray of shape (n_samples_all, n_features) Data set carrying all the neighbours to be used nn_num : ndarray of shape (n_samples_all, k_nearest_neighbours) The nearest neighbours of each sample in `nn_data`. n_samples : int The number of samples to generate. step_size : float, default=1.0 The step size to create samples. y : ndarray of shape (n_samples_all,), default=None The true target associated with `nn_data`. Used by Borderline SMOTE-2 to weight the distances in the sample generation process. Returns ------- X_new : {ndarray, sparse matrix} of shape (n_samples_new, n_features) Synthetically generated samples. y_new : ndarray of shape (n_samples_new,) Target values for synthetic samples. """ random_state = check_random_state(self.random_state) samples_indices = random_state.randint(low=0, high=nn_num.size, size=n_samples) # np.newaxis for backwards compatability with random_state steps = step_size * random_state.uniform(size=n_samples)[:, np.newaxis] rows = np.floor_divide(samples_indices, nn_num.shape[1]) cols = np.mod(samples_indices, nn_num.shape[1]) X_new = self._generate_samples(X, nn_data, nn_num, rows, cols, steps, y_type, y) y_new = np.full(n_samples, fill_value=y_type, dtype=y_dtype) return X_new, y_new def _generate_samples( self, X, nn_data, nn_num, rows, cols, steps, y_type=None, y=None ): r"""Generate a synthetic sample. The rule for the generation is: .. math:: \mathbf{s_{s}} = \mathbf{s_{i}} + \mathcal{u}(0, 1) \times (\mathbf{s_{i}} - \mathbf{s_{nn}}) \, where \mathbf{s_{s}} is the new synthetic samples, \mathbf{s_{i}} is the current sample, \mathbf{s_{nn}} is a randomly selected neighbors of \mathbf{s_{i}} and \mathcal{u}(0, 1) is a random number between [0, 1). Parameters ---------- X : {array-like, sparse matrix} of shape (n_samples, n_features) Points from which the points will be created. nn_data : ndarray of shape (n_samples_all, n_features) Data set carrying all the neighbours to be used. nn_num : ndarray of shape (n_samples_all, k_nearest_neighbours) The nearest neighbours of each sample in `nn_data`. rows : ndarray of shape (n_samples,), dtype=int Indices pointing at feature vector in X which will be used as a base for creating new samples. cols : ndarray of shape (n_samples,), dtype=int Indices pointing at which nearest neighbor of base feature vector will be used when creating new samples. steps : ndarray of shape (n_samples,), dtype=float Step sizes for new samples. y_type : str, int or None, default=None Class label of the current target classes for which we want to generate samples. y : ndarray of shape (n_samples_all,), default=None The true target associated with `nn_data`. Used by Borderline SMOTE-2 to weight the distances in the sample generation process. Returns ------- X_new : {ndarray, sparse matrix} of shape (n_samples, n_features) Synthetically generated samples. """ diffs = nn_data[nn_num[rows, cols]] - X[rows] if y is not None: # only entering for BorderlineSMOTE-2 random_state = check_random_state(self.random_state) mask_pair_samples = y[nn_num[rows, cols]] != y_type diffs[mask_pair_samples] *= random_state.uniform( low=0.0, high=0.5, size=(mask_pair_samples.sum(), 1) ) if sparse.issparse(X): sparse_func = type(X).__name__ steps = getattr(sparse, sparse_func)(steps) X_new = X[rows] + steps.multiply(diffs) else: X_new = X[rows] + steps * diffs return X_new.astype(X.dtype) def _in_danger_noise(self, nn_estimator, samples, target_class, y, kind="danger"): """Estimate if a set of sample are in danger or noise. Used by BorderlineSMOTE and SVMSMOTE. Parameters ---------- nn_estimator : estimator object An estimator that inherits from :class:`~sklearn.neighbors.base.KNeighborsMixin` use to determine if a sample is in danger/noise. samples : {array-like, sparse matrix} of shape (n_samples, n_features) The samples to check if either they are in danger or not. target_class : int or str The target corresponding class being over-sampled. y : array-like of shape (n_samples,) The true label in order to check the neighbour labels. kind : {'danger', 'noise'}, default='danger' The type of classification to use. Can be either: - If 'danger', check if samples are in danger, - If 'noise', check if samples are noise. Returns ------- output : ndarray of shape (n_samples,) A boolean array where True refer to samples in danger or noise. """ x = nn_estimator.kneighbors(samples, return_distance=False)[:, 1:] nn_label = (y[x] != target_class).astype(int) n_maj = np.sum(nn_label, axis=1) if kind == "danger": # Samples are in danger for m/2 <= m' < m return np.bitwise_and( n_maj >= (nn_estimator.n_neighbors - 1) / 2, n_maj < nn_estimator.n_neighbors - 1, ) else: # kind == "noise": # Samples are noise for m = m' return n_maj == nn_estimator.n_neighbors - 1 @Substitution( sampling_strategy=BaseOverSampler._sampling_strategy_docstring, random_state=_random_state_docstring, ) class SMOTE(BaseSMOTE): """Class to perform over-sampling using SMOTE. This object is an implementation of SMOTE - Synthetic Minority Over-sampling Technique as presented in [1]_. Read more in the :ref:`User Guide `. Parameters ---------- {sampling_strategy} {random_state} k_neighbors : int or object, default=5 The nearest neighbors used to define the neighborhood of samples to use to generate the synthetic samples. You can pass: - an `int` corresponding to the number of neighbors to use. A `~sklearn.neighbors.NearestNeighbors` instance will be fitted in this case. - an instance of a compatible nearest neighbors algorithm that should implement both methods `kneighbors` and `kneighbors_graph`. For instance, it could correspond to a :class:`~sklearn.neighbors.NearestNeighbors` but could be extended to any compatible class. Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. nn_k_ : estimator object Validated k-nearest neighbours created from the `k_neighbors` parameter. n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- SMOTENC : Over-sample using SMOTE for continuous and categorical features. SMOTEN : Over-sample using the SMOTE variant specifically for categorical features only. BorderlineSMOTE : Over-sample using the borderline-SMOTE variant. SVMSMOTE : Over-sample using the SVM-SMOTE variant. ADASYN : Over-sample using ADASYN. KMeansSMOTE : Over-sample applying a clustering before to oversample using SMOTE. Notes ----- See the original papers: [1]_ for more details. Supports multi-class resampling. A one-vs.-rest scheme is used as originally proposed in [1]_. References ---------- .. [1] N. V. Chawla, K. W. Bowyer, L. O.Hall, W. P. Kegelmeyer, "SMOTE: synthetic minority over-sampling technique," Journal of artificial intelligence research, 321-357, 2002. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from imblearn.over_sampling import SMOTE >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> sm = SMOTE(random_state=42) >>> X_res, y_res = sm.fit_resample(X, y) >>> print('Resampled dataset shape %s' % Counter(y_res)) Resampled dataset shape Counter({{0: 900, 1: 900}}) """ def __init__( self, *, sampling_strategy="auto", random_state=None, k_neighbors=5, ): super().__init__( sampling_strategy=sampling_strategy, random_state=random_state, k_neighbors=k_neighbors, ) def _fit_resample(self, X, y): self._validate_estimator() X_resampled = [X.copy()] y_resampled = [y.copy()] for class_sample, n_samples in self.sampling_strategy_.items(): if n_samples == 0: continue target_class_indices = np.flatnonzero(y == class_sample) X_class = _safe_indexing(X, target_class_indices) self.nn_k_.fit(X_class) nns = self.nn_k_.kneighbors(X_class, return_distance=False)[:, 1:] X_new, y_new = self._make_samples( X_class, y.dtype, class_sample, X_class, nns, n_samples, 1.0 ) X_resampled.append(X_new) y_resampled.append(y_new) if sparse.issparse(X): X_resampled = sparse.vstack(X_resampled, format=X.format) else: X_resampled = np.vstack(X_resampled) y_resampled = np.hstack(y_resampled) return X_resampled, y_resampled @Substitution( sampling_strategy=BaseOverSampler._sampling_strategy_docstring, random_state=_random_state_docstring, ) class SMOTENC(SMOTE): """Synthetic Minority Over-sampling Technique for Nominal and Continuous. Unlike :class:`SMOTE`, SMOTE-NC for dataset containing numerical and categorical features. However, it is not designed to work with only categorical features. Read more in the :ref:`User Guide `. .. versionadded:: 0.4 Parameters ---------- categorical_features : "infer" or array-like of shape (n_cat_features,) or \ (n_features,), dtype={{bool, int, str}} Specified which features are categorical. Can either be: - "auto" (default) to automatically detect categorical features. Only supported when `X` is a :class:`pandas.DataFrame` and it corresponds to columns that have a :class:`pandas.CategoricalDtype`; - array of `int` corresponding to the indices specifying the categorical features; - array of `str` corresponding to the feature names. `X` should be a pandas :class:`pandas.DataFrame` in this case. - mask array of shape (n_features, ) and ``bool`` dtype for which ``True`` indicates the categorical features. categorical_encoder : estimator, default=None One-hot encoder used to encode the categorical features. If `None`, a :class:`~sklearn.preprocessing.OneHotEncoder` is used with default parameters apart from `handle_unknown` which is set to 'ignore'. {sampling_strategy} {random_state} k_neighbors : int or object, default=5 The nearest neighbors used to define the neighborhood of samples to use to generate the synthetic samples. You can pass: - an `int` corresponding to the number of neighbors to use. A `~sklearn.neighbors.NearestNeighbors` instance will be fitted in this case. - an instance of a compatible nearest neighbors algorithm that should implement both methods `kneighbors` and `kneighbors_graph`. For instance, it could correspond to a :class:`~sklearn.neighbors.NearestNeighbors` but could be extended to any compatible class. Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. nn_k_ : estimator object Validated k-nearest neighbours created from the `k_neighbors` parameter. categorical_encoder_ : estimator The encoder used to encode the categorical features. categorical_features_ : ndarray of shape (n_cat_features,), dtype=np.int64 Indices of the categorical features. continuous_features_ : ndarray of shape (n_cont_features,), dtype=np.int64 Indices of the continuous features. median_std_ : dict of int -> float Median of the standard deviation of the continuous features for each class to be over-sampled. n_features_ : int Number of features observed at `fit`. n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- SMOTE : Over-sample using SMOTE. SMOTEN : Over-sample using the SMOTE variant specifically for categorical features only. SVMSMOTE : Over-sample using SVM-SMOTE variant. BorderlineSMOTE : Over-sample using Borderline-SMOTE variant. ADASYN : Over-sample using ADASYN. KMeansSMOTE : Over-sample applying a clustering before to oversample using SMOTE. Notes ----- See the original paper [1]_ for more details. Supports multi-class resampling. A one-vs.-rest scheme is used as originally proposed in [1]_. See :ref:`sphx_glr_auto_examples_over-sampling_plot_comparison_over_sampling.py`, and :ref:`sphx_glr_auto_examples_over-sampling_plot_illustration_generation_sample.py`. References ---------- .. [1] N. V. Chawla, K. W. Bowyer, L. O.Hall, W. P. Kegelmeyer, "SMOTE: synthetic minority over-sampling technique," Journal of artificial intelligence research, 321-357, 2002. Examples -------- >>> from collections import Counter >>> from numpy.random import RandomState >>> from sklearn.datasets import make_classification >>> from imblearn.over_sampling import SMOTENC >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print(f'Original dataset shape {{X.shape}}') Original dataset shape (1000, 20) >>> print(f'Original dataset samples per class {{Counter(y)}}') Original dataset samples per class Counter({{1: 900, 0: 100}}) >>> # simulate the 2 last columns to be categorical features >>> X[:, -2:] = RandomState(10).randint(0, 4, size=(1000, 2)) >>> sm = SMOTENC(random_state=42, categorical_features=[18, 19]) >>> X_res, y_res = sm.fit_resample(X, y) >>> print(f'Resampled dataset samples per class {{Counter(y_res)}}') Resampled dataset samples per class Counter({{0: 900, 1: 900}}) """ _required_parameters = ["categorical_features"] _parameter_constraints: dict = { **SMOTE._parameter_constraints, "categorical_features": ["array-like", StrOptions({"auto"})], "categorical_encoder": [ HasMethods(["fit_transform", "inverse_transform"]), None, ], } def __init__( self, categorical_features, *, categorical_encoder=None, sampling_strategy="auto", random_state=None, k_neighbors=5, ): super().__init__( sampling_strategy=sampling_strategy, random_state=random_state, k_neighbors=k_neighbors, ) self.categorical_features = categorical_features self.categorical_encoder = categorical_encoder def _check_X_y(self, X, y): """Overwrite the checking to let pass some string for categorical features. """ y, binarize_y = check_target_type(y, indicate_one_vs_all=True) X = _check_X(X) validate_data(self, X=X, y=y, reset=True, skip_check_array=True) return X, y, binarize_y def _validate_column_types(self, X): """Compute the indices of the categorical and continuous features.""" if self.categorical_features == "auto": if not is_pandas_df(X): raise ValueError( "When `categorical_features='auto'`, the input data " f"should be a pandas.DataFrame. Got {type(X)} instead." ) import pandas as pd # safely import pandas now are_columns_categorical = np.array( [isinstance(col_dtype, pd.CategoricalDtype) for col_dtype in X.dtypes] ) self.categorical_features_ = np.flatnonzero(are_columns_categorical) self.continuous_features_ = np.flatnonzero(~are_columns_categorical) else: self.categorical_features_ = np.array( _get_column_indices(X, self.categorical_features) ) self.continuous_features_ = np.setdiff1d( np.arange(self.n_features_), self.categorical_features_ ) def _validate_estimator(self): super()._validate_estimator() if self.categorical_features_.size == self.n_features_in_: raise ValueError( "SMOTE-NC is not designed to work only with categorical " "features. It requires some numerical features." ) elif self.categorical_features_.size == 0: raise ValueError( "SMOTE-NC is not designed to work only with numerical " "features. It requires some categorical features." ) def _fit_resample(self, X, y): self.n_features_ = _num_features(X) self._validate_column_types(X) self._validate_estimator() X_continuous = _safe_indexing(X, self.continuous_features_, axis=1) X_continuous = check_array(X_continuous, accept_sparse=["csr", "csc"]) X_categorical = _safe_indexing(X, self.categorical_features_, axis=1) if X_continuous.dtype.name != "object": dtype_ohe = X_continuous.dtype else: dtype_ohe = np.float64 if self.categorical_encoder is None: self.categorical_encoder_ = OneHotEncoder( handle_unknown="ignore", dtype=dtype_ohe ) else: self.categorical_encoder_ = clone(self.categorical_encoder) # the input of the OneHotEncoder needs to be dense X_ohe = self.categorical_encoder_.fit_transform( X_categorical.toarray() if sparse.issparse(X_categorical) else X_categorical ) if not sparse.issparse(X_ohe): X_ohe = sparse.csr_matrix(X_ohe, dtype=dtype_ohe) # SMOTENC reconstructs the categorical features by activating exactly one # column per categorical feature (see `_generate_samples`). This requires # a complete one-hot encoding with one column per category. Encoders that # emit fewer columns -- e.g. ``OneHotEncoder(drop=...)`` or one merging # infrequent categories -- break this assumption and previously raised an # opaque "zero-size array" error or silently produced wrong categories. n_categories = sum( categories.size for categories in self.categorical_encoder_.categories_ ) if X_ohe.shape[1] != n_categories: raise ValueError( "SMOTENC requires a one-hot encoding with one column per category " "for the categorical features. The provided `categorical_encoder` " f"produced {X_ohe.shape[1]} columns for {n_categories} categories. " "This happens when the encoder drops columns (e.g. " "`OneHotEncoder(drop=...)`) or merges infrequent categories, which " "is not supported. Pass an encoder that keeps all categories, such " "as `OneHotEncoder(handle_unknown='ignore')`." ) X_encoded = sparse.hstack((X_continuous, X_ohe), format="csr", dtype=dtype_ohe) X_resampled = [X_encoded.copy()] y_resampled = [y.copy()] # SMOTE resampling starts here self.median_std_ = {} for class_sample, n_samples in self.sampling_strategy_.items(): if n_samples == 0: continue target_class_indices = np.flatnonzero(y == class_sample) X_class = _safe_indexing(X_encoded, target_class_indices) _, var = csr_mean_variance_axis0( X_class[:, : self.continuous_features_.size] ) self.median_std_[class_sample] = np.median(np.sqrt(var)) # In the edge case where the median of the std is equal to 0, the 1s # entries will be also nullified. In this case, we store the original # categorical encoding which will be later used for inverting the OHE if math.isclose(self.median_std_[class_sample], 0): # This variable will be used when generating data self._X_categorical_minority_encoded = X_class[ :, self.continuous_features_.size : ].toarray() # we can replace the 1 entries of the categorical features with the # median of the standard deviation. It will ensure that whenever # distance is computed between 2 samples, the difference will be equal # to the median of the standard deviation as in the original paper. X_class_categorical = X_class[:, self.continuous_features_.size :] # With one-hot encoding, the median will be repeated twice. We need # to divide by sqrt(2) such that we only have one median value # contributing to the Euclidean distance X_class_categorical.data[:] = self.median_std_[class_sample] / np.sqrt(2) X_class[:, self.continuous_features_.size :] = X_class_categorical self.nn_k_.fit(X_class) nns = self.nn_k_.kneighbors(X_class, return_distance=False)[:, 1:] X_new, y_new = self._make_samples( X_class, y.dtype, class_sample, X_class, nns, n_samples, 1.0 ) X_resampled.append(X_new) y_resampled.append(y_new) X_resampled = sparse.vstack(X_resampled, format=X_encoded.format) y_resampled = np.hstack(y_resampled) # SMOTE resampling ends here # reverse the encoding of the categorical features X_res_cat = X_resampled[:, self.continuous_features_.size :] X_res_cat.data = np.ones_like(X_res_cat.data) X_res_cat_dec = self.categorical_encoder_.inverse_transform(X_res_cat) if sparse.issparse(X): X_resampled = sparse.hstack( ( X_resampled[:, : self.continuous_features_.size], X_res_cat_dec, ), format="csr", ) else: X_resampled = np.hstack( ( X_resampled[:, : self.continuous_features_.size].toarray(), X_res_cat_dec, ) ) indices_reordered = np.argsort( np.hstack((self.continuous_features_, self.categorical_features_)) ) if sparse.issparse(X_resampled): # the matrix is supposed to be in the CSR format after the stacking col_indices = X_resampled.indices.copy() for idx, col_idx in enumerate(indices_reordered): mask = X_resampled.indices == col_idx col_indices[mask] = idx X_resampled.indices = col_indices else: X_resampled = X_resampled[:, indices_reordered] return X_resampled, y_resampled def _generate_samples(self, X, nn_data, nn_num, rows, cols, steps, y_type, y=None): """Generate a synthetic sample with an additional steps for the categorical features. Each new sample is generated the same way than in SMOTE. However, the categorical features are mapped to the most frequent nearest neighbors of the majority class. """ rng = check_random_state(self.random_state) X_new = super()._generate_samples(X, nn_data, nn_num, rows, cols, steps) # change in sparsity structure more efficient with LIL than CSR X_new = X_new.tolil() if sparse.issparse(X_new) else X_new # convert to dense array since scipy.sparse doesn't handle 3D nn_data = nn_data.toarray() if sparse.issparse(nn_data) else nn_data # In the case that the median std was equal to zeros, we have to # create non-null entry based on the encoded of OHE if math.isclose(self.median_std_[y_type], 0): nn_data[:, self.continuous_features_.size :] = ( self._X_categorical_minority_encoded ) all_neighbors = nn_data[nn_num[rows]] categories_size = [self.continuous_features_.size] + [ cat.size for cat in self.categorical_encoder_.categories_ ] for start_idx, end_idx in zip( np.cumsum(categories_size)[:-1], np.cumsum(categories_size)[1:] ): col_maxs = all_neighbors[:, :, start_idx:end_idx].sum(axis=1) # tie breaking argmax is_max = np.isclose(col_maxs, col_maxs.max(axis=1, keepdims=True)) max_idxs = rng.permutation(np.argwhere(is_max)) xs, idx_sels = np.unique(max_idxs[:, 0], return_index=True) col_sels = max_idxs[idx_sels, 1] ys = start_idx + col_sels X_new[:, start_idx:end_idx] = 0 X_new[xs, ys] = 1 return X_new def _more_tags(self): return {"X_types": ["2darray", "dataframe", "string"]} def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.input_tags.sparse = False tags.input_tags.string = True return tags @Substitution( sampling_strategy=BaseOverSampler._sampling_strategy_docstring, random_state=_random_state_docstring, ) class SMOTEN(SMOTE): """Synthetic Minority Over-sampling Technique for Nominal. This method is referred as SMOTEN in [1]_. It expects that the data to resample are only made of categorical features. Read more in the :ref:`User Guide `. .. versionadded:: 0.8 Parameters ---------- categorical_encoder : estimator, default=None Ordinal encoder used to encode the categorical features. If `None`, a :class:`~sklearn.preprocessing.OrdinalEncoder` is used with default parameters. {sampling_strategy} {random_state} k_neighbors : int or object, default=5 The nearest neighbors used to define the neighborhood of samples to use to generate the synthetic samples. You can pass: - an `int` corresponding to the number of neighbors to use. A `~sklearn.neighbors.NearestNeighbors` instance will be fitted in this case. - an instance of a compatible nearest neighbors algorithm that should implement both methods `kneighbors` and `kneighbors_graph`. For instance, it could correspond to a :class:`~sklearn.neighbors.NearestNeighbors` but could be extended to any compatible class. Attributes ---------- categorical_encoder_ : estimator The encoder used to encode the categorical features. sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. nn_k_ : estimator object Validated k-nearest neighbours created from the `k_neighbors` parameter. n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- SMOTE : Over-sample using SMOTE. SMOTENC : Over-sample using SMOTE for continuous and categorical features. BorderlineSMOTE : Over-sample using the borderline-SMOTE variant. SVMSMOTE : Over-sample using the SVM-SMOTE variant. ADASYN : Over-sample using ADASYN. KMeansSMOTE : Over-sample applying a clustering before to oversample using SMOTE. Notes ----- See the original papers: [1]_ for more details. Supports multi-class resampling. A one-vs.-rest scheme is used as originally proposed in [1]_. References ---------- .. [1] N. V. Chawla, K. W. Bowyer, L. O.Hall, W. P. Kegelmeyer, "SMOTE: synthetic minority over-sampling technique," Journal of artificial intelligence research, 321-357, 2002. Examples -------- >>> import numpy as np >>> X = np.array(["A"] * 10 + ["B"] * 20 + ["C"] * 30, dtype=object).reshape(-1, 1) >>> y = np.array([0] * 20 + [1] * 40, dtype=np.int32) >>> from collections import Counter >>> print(f"Original class counts: {{Counter(y)}}") Original class counts: Counter({{1: 40, 0: 20}}) >>> from imblearn.over_sampling import SMOTEN >>> sampler = SMOTEN(random_state=0) >>> X_res, y_res = sampler.fit_resample(X, y) >>> print(f"Class counts after resampling {{Counter(y_res)}}") Class counts after resampling Counter({{0: 40, 1: 40}}) """ _parameter_constraints: dict = { **SMOTE._parameter_constraints, "categorical_encoder": [ HasMethods(["fit_transform", "inverse_transform"]), None, ], } def __init__( self, categorical_encoder=None, *, sampling_strategy="auto", random_state=None, k_neighbors=5, ): super().__init__( sampling_strategy=sampling_strategy, random_state=random_state, k_neighbors=k_neighbors, ) self.categorical_encoder = categorical_encoder def _check_X_y(self, X, y): """Check should accept strings and not sparse matrices.""" y, binarize_y = check_target_type(y, indicate_one_vs_all=True) X, y = validate_data( self, X=X, y=y, reset=True, dtype=None, accept_sparse=["csr", "csc"], ) return X, y, binarize_y def _validate_estimator(self): """Force to use precomputed distance matrix.""" super()._validate_estimator() self.nn_k_.set_params(metric="precomputed") def _make_samples(self, X_class, klass, y_dtype, nn_indices, n_samples): random_state = check_random_state(self.random_state) # generate sample indices that will be used to generate new samples samples_indices = random_state.choice( np.arange(X_class.shape[0]), size=n_samples, replace=True ) # for each drawn samples, select its k-neighbors and generate a sample # where for each feature individually, each category generated is the # most common category X_new = np.squeeze( mode(X_class[nn_indices[samples_indices]], axis=1, keepdims=True).mode, axis=1, ) y_new = np.full(n_samples, fill_value=klass, dtype=y_dtype) return X_new, y_new def _fit_resample(self, X, y): if sparse.issparse(X): X_sparse_format = X.format X = X.toarray() warnings.warn( ( "Passing a sparse matrix to SMOTEN is not really efficient since it" " is converted to a dense array internally." ), DataConversionWarning, ) else: X_sparse_format = None self._validate_estimator() X_resampled = [X.copy()] y_resampled = [y.copy()] if self.categorical_encoder is None: self.categorical_encoder_ = OrdinalEncoder(dtype=np.int32) else: self.categorical_encoder_ = clone(self.categorical_encoder) X_encoded = self.categorical_encoder_.fit_transform(X) vdm = ValueDifferenceMetric( n_categories=[len(cat) for cat in self.categorical_encoder_.categories_] ).fit(X_encoded, y) for class_sample, n_samples in self.sampling_strategy_.items(): if n_samples == 0: continue target_class_indices = np.flatnonzero(y == class_sample) X_class = _safe_indexing(X_encoded, target_class_indices) X_class_dist = vdm.pairwise(X_class) self.nn_k_.fit(X_class_dist) # the kneigbors search will include the sample itself which is # expected from the original algorithm nn_indices = self.nn_k_.kneighbors(X_class_dist, return_distance=False) X_new, y_new = self._make_samples( X_class, class_sample, y.dtype, nn_indices, n_samples ) X_new = self.categorical_encoder_.inverse_transform(X_new) X_resampled.append(X_new) y_resampled.append(y_new) X_resampled = np.vstack(X_resampled) y_resampled = np.hstack(y_resampled) if X_sparse_format == "csr": return sparse.csr_matrix(X_resampled), y_resampled elif X_sparse_format == "csc": return sparse.csc_matrix(X_resampled), y_resampled else: return X_resampled, y_resampled def _more_tags(self): return {"X_types": ["2darray", "dataframe", "string"]} def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.input_tags.string = True return tags scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/_smote/cluster.py000066400000000000000000000256221521136113700311640ustar00rootroot00000000000000"""SMOTE variant employing some clustering before the generation.""" # Authors: Guillaume Lemaitre # Fernando Nogueira # Christos Aridas # License: MIT import math import numbers import numpy as np from scipy import sparse from sklearn.base import clone from sklearn.cluster import MiniBatchKMeans from sklearn.metrics import pairwise_distances from sklearn.utils import _safe_indexing from sklearn.utils._param_validation import HasMethods, Interval, StrOptions from imblearn.over_sampling._smote.base import BaseSMOTE from imblearn.over_sampling.base import BaseOverSampler from imblearn.utils import Substitution from imblearn.utils._docstring import _n_jobs_docstring, _random_state_docstring @Substitution( sampling_strategy=BaseOverSampler._sampling_strategy_docstring, n_jobs=_n_jobs_docstring, random_state=_random_state_docstring, ) class KMeansSMOTE(BaseSMOTE): """Apply a KMeans clustering before to over-sample using SMOTE. This is an implementation of the algorithm described in [1]_. Read more in the :ref:`User Guide `. .. versionadded:: 0.5 Parameters ---------- {sampling_strategy} {random_state} k_neighbors : int or object, default=2 The nearest neighbors used to define the neighborhood of samples to use to generate the synthetic samples. You can pass: - an `int` corresponding to the number of neighbors to use. A `~sklearn.neighbors.NearestNeighbors` instance will be fitted in this case. - an instance of a compatible nearest neighbors algorithm that should implement both methods `kneighbors` and `kneighbors_graph`. For instance, it could correspond to a :class:`~sklearn.neighbors.NearestNeighbors` but could be extended to any compatible class. {n_jobs} kmeans_estimator : int or object, default=None A KMeans instance or the number of clusters to be used. By default, we used a :class:`~sklearn.cluster.MiniBatchKMeans` which tend to be better with large number of samples. cluster_balance_threshold : "auto" or float, default="auto" The threshold at which a cluster is called balanced and where samples of the class selected for SMOTE will be oversampled. If "auto", this will be determined by the ratio for each class, or it can be set manually. density_exponent : "auto" or float, default="auto" This exponent is used to determine the density of a cluster. Leaving this to "auto" will use a feature-length based exponent. Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. kmeans_estimator_ : estimator The fitted clustering method used before to apply SMOTE. nn_k_ : estimator The fitted k-NN estimator used in SMOTE. cluster_balance_threshold_ : float The threshold used during ``fit`` for calling a cluster balanced. n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- SMOTE : Over-sample using SMOTE. SMOTENC : Over-sample using SMOTE for continuous and categorical features. SMOTEN : Over-sample using the SMOTE variant specifically for categorical features only. SVMSMOTE : Over-sample using SVM-SMOTE variant. BorderlineSMOTE : Over-sample using Borderline-SMOTE variant. ADASYN : Over-sample using ADASYN. References ---------- .. [1] Felix Last, Georgios Douzas, Fernando Bacao, "Oversampling for Imbalanced Learning Based on K-Means and SMOTE" https://arxiv.org/abs/1711.00837 Examples -------- >>> import numpy as np >>> from imblearn.over_sampling import KMeansSMOTE >>> from sklearn.datasets import make_blobs >>> blobs = [100, 800, 100] >>> X, y = make_blobs(blobs, centers=[(-10, 0), (0,0), (10, 0)], random_state=0) >>> # Add a single 0 sample in the middle blob >>> X = np.concatenate([X, [[0, 0]]]) >>> y = np.append(y, 0) >>> # Make this a binary classification problem >>> y = y == 1 >>> sm = KMeansSMOTE( ... kmeans_estimator=MiniBatchKMeans(n_init=1, random_state=0), random_state=42 ... ) >>> X_res, y_res = sm.fit_resample(X, y) >>> # Find the number of new samples in the middle blob >>> n_res_in_middle = ((X_res[:, 0] > -5) & (X_res[:, 0] < 5)).sum() >>> print("Samples in the middle blob: %s" % n_res_in_middle) Samples in the middle blob: 801 >>> print("Middle blob unchanged: %s" % (n_res_in_middle == blobs[1] + 1)) Middle blob unchanged: True >>> print("More 0 samples: %s" % ((y_res == 0).sum() > (y == 0).sum())) More 0 samples: True """ _parameter_constraints: dict = { **BaseSMOTE._parameter_constraints, "kmeans_estimator": [ HasMethods(["fit", "predict"]), Interval(numbers.Integral, 1, None, closed="left"), None, ], "cluster_balance_threshold": [StrOptions({"auto"}), numbers.Real], "density_exponent": [StrOptions({"auto"}), numbers.Real], "n_jobs": [numbers.Integral, None], } def __init__( self, *, sampling_strategy="auto", random_state=None, k_neighbors=2, n_jobs=None, kmeans_estimator=None, cluster_balance_threshold="auto", density_exponent="auto", ): super().__init__( sampling_strategy=sampling_strategy, random_state=random_state, k_neighbors=k_neighbors, ) self.kmeans_estimator = kmeans_estimator self.cluster_balance_threshold = cluster_balance_threshold self.density_exponent = density_exponent self.n_jobs = n_jobs def _validate_estimator(self): super()._validate_estimator() if self.kmeans_estimator is None: self.kmeans_estimator_ = MiniBatchKMeans(random_state=self.random_state) elif isinstance(self.kmeans_estimator, int): self.kmeans_estimator_ = MiniBatchKMeans( n_clusters=self.kmeans_estimator, random_state=self.random_state, ) else: self.kmeans_estimator_ = clone(self.kmeans_estimator) self.cluster_balance_threshold_ = ( self.cluster_balance_threshold if self.kmeans_estimator_.n_clusters != 1 else -np.inf ) def _find_cluster_sparsity(self, X): """Compute the cluster sparsity.""" euclidean_distances = pairwise_distances( X, metric="euclidean", n_jobs=self.n_jobs ) # negate diagonal elements for ind in range(X.shape[0]): euclidean_distances[ind, ind] = 0 non_diag_elements = (X.shape[0] ** 2) - X.shape[0] mean_distance = euclidean_distances.sum() / non_diag_elements exponent = ( math.log(X.shape[0], 1.6) ** 1.8 * 0.16 if self.density_exponent == "auto" else self.density_exponent ) return (mean_distance**exponent) / X.shape[0] def _fit_resample(self, X, y): self._validate_estimator() X_resampled = X.copy() y_resampled = y.copy() total_inp_samples = sum(self.sampling_strategy_.values()) for class_sample, n_samples in self.sampling_strategy_.items(): if n_samples == 0: continue X_clusters = self.kmeans_estimator_.fit_predict(X) valid_clusters = [] cluster_sparsities = [] # identify cluster which are answering the requirements for cluster_idx in range(self.kmeans_estimator_.n_clusters): cluster_mask = np.flatnonzero(X_clusters == cluster_idx) if cluster_mask.size == 0: # empty cluster continue X_cluster = _safe_indexing(X, cluster_mask) y_cluster = _safe_indexing(y, cluster_mask) cluster_class_mean = (y_cluster == class_sample).mean() if self.cluster_balance_threshold_ == "auto": balance_threshold = n_samples / total_inp_samples / 2 else: balance_threshold = self.cluster_balance_threshold_ # the cluster is already considered balanced if cluster_class_mean < balance_threshold: continue # not enough samples to apply SMOTE anticipated_samples = cluster_class_mean * X_cluster.shape[0] if anticipated_samples < self.nn_k_.n_neighbors: continue X_cluster_class = _safe_indexing( X_cluster, np.flatnonzero(y_cluster == class_sample) ) valid_clusters.append(cluster_mask) cluster_sparsities.append(self._find_cluster_sparsity(X_cluster_class)) cluster_sparsities = np.array(cluster_sparsities) cluster_weights = cluster_sparsities / cluster_sparsities.sum() if not valid_clusters: raise RuntimeError( "No clusters found with sufficient samples of " f"class {class_sample}. Try lowering the " "cluster_balance_threshold or increasing the number of " "clusters." ) for valid_cluster_idx, valid_cluster in enumerate(valid_clusters): X_cluster = _safe_indexing(X, valid_cluster) y_cluster = _safe_indexing(y, valid_cluster) X_cluster_class = _safe_indexing( X_cluster, np.flatnonzero(y_cluster == class_sample) ) self.nn_k_.fit(X_cluster_class) nns = self.nn_k_.kneighbors(X_cluster_class, return_distance=False)[ :, 1: ] cluster_n_samples = int( math.ceil(n_samples * cluster_weights[valid_cluster_idx]) ) X_new, y_new = self._make_samples( X_cluster_class, y.dtype, class_sample, X_cluster_class, nns, cluster_n_samples, 1.0, ) stack = [np.vstack, sparse.vstack][int(sparse.issparse(X_new))] X_resampled = stack((X_resampled, X_new)) y_resampled = np.hstack((y_resampled, y_new)) return X_resampled, y_resampled scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/_smote/filter.py000066400000000000000000000440451521136113700307700ustar00rootroot00000000000000"""SMOTE variant applying some filtering before the generation process.""" # Authors: Guillaume Lemaitre # Fernando Nogueira # Christos Aridas # Dzianis Dudnik # License: MIT import numbers import numpy as np from scipy import sparse from sklearn.base import clone from sklearn.svm import SVC from sklearn.utils import _safe_indexing, check_random_state from sklearn.utils._param_validation import HasMethods, Interval, StrOptions from imblearn.over_sampling._smote.base import BaseSMOTE from imblearn.over_sampling.base import BaseOverSampler from imblearn.utils import Substitution, check_neighbors_object from imblearn.utils._docstring import _random_state_docstring @Substitution( sampling_strategy=BaseOverSampler._sampling_strategy_docstring, random_state=_random_state_docstring, ) class BorderlineSMOTE(BaseSMOTE): """Over-sampling using Borderline SMOTE. This algorithm is a variant of the original SMOTE algorithm proposed in [2]_. Borderline samples will be detected and used to generate new synthetic samples. Read more in the :ref:`User Guide `. .. versionadded:: 0.4 Parameters ---------- {sampling_strategy} {random_state} k_neighbors : int or object, default=5 The nearest neighbors used to define the neighborhood of samples to use to generate the synthetic samples. You can pass: - an `int` corresponding to the number of neighbors to use. A `~sklearn.neighbors.NearestNeighbors` instance will be fitted in this case. - an instance of a compatible nearest neighbors algorithm that should implement both methods `kneighbors` and `kneighbors_graph`. For instance, it could correspond to a :class:`~sklearn.neighbors.NearestNeighbors` but could be extended to any compatible class. m_neighbors : int or object, default=10 The nearest neighbors used to determine if a minority sample is in "danger". You can pass: - an `int` corresponding to the number of neighbors to use. A `~sklearn.neighbors.NearestNeighbors` instance will be fitted in this case. - an instance of a compatible nearest neighbors algorithm that should implement both methods `kneighbors` and `kneighbors_graph`. For instance, it could correspond to a :class:`~sklearn.neighbors.NearestNeighbors` but could be extended to any compatible class. kind : {{"borderline-1", "borderline-2"}}, default='borderline-1' The type of SMOTE algorithm to use one of the following options: ``'borderline-1'``, ``'borderline-2'``. Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. nn_k_ : estimator object Validated k-nearest neighbours created from the `k_neighbors` parameter. nn_m_ : estimator object Validated m-nearest neighbours created from the `m_neighbors` parameter. in_danger_indices : dict of ndarray Dictionary containing the indices of the samples considered in danger that are used to generate new synthetic samples. The keys corresponds to the class label. n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- SMOTE : Over-sample using SMOTE. SMOTENC : Over-sample using SMOTE for continuous and categorical features. SVMSMOTE : Over-sample using SVM-SMOTE variant. ADASYN : Over-sample using ADASYN. KMeansSMOTE : Over-sample applying a clustering before to oversample using SMOTE. Notes ----- See the original papers: [2]_ for more details. Supports multi-class resampling. A one-vs.-rest scheme is used as originally proposed in [1]_. References ---------- .. [1] N. V. Chawla, K. W. Bowyer, L. O.Hall, W. P. Kegelmeyer, "SMOTE: synthetic minority over-sampling technique," Journal of artificial intelligence research, 321-357, 2002. .. [2] H. Han, W. Wen-Yuan, M. Bing-Huan, "Borderline-SMOTE: a new over-sampling method in imbalanced data sets learning," Advances in intelligent computing, 878-887, 2005. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from imblearn.over_sampling import BorderlineSMOTE >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> sm = BorderlineSMOTE(random_state=42) >>> X_res, y_res = sm.fit_resample(X, y) >>> print('Resampled dataset shape %s' % Counter(y_res)) Resampled dataset shape Counter({{0: 900, 1: 900}}) """ _parameter_constraints: dict = { **BaseSMOTE._parameter_constraints, "m_neighbors": [ Interval(numbers.Integral, 1, None, closed="left"), HasMethods(["kneighbors", "kneighbors_graph"]), ], "kind": [StrOptions({"borderline-1", "borderline-2"})], } def __init__( self, *, sampling_strategy="auto", random_state=None, k_neighbors=5, m_neighbors=10, kind="borderline-1", ): super().__init__( sampling_strategy=sampling_strategy, random_state=random_state, k_neighbors=k_neighbors, ) self.m_neighbors = m_neighbors self.kind = kind def _validate_estimator(self): super()._validate_estimator() self.nn_m_ = check_neighbors_object( "m_neighbors", self.m_neighbors, additional_neighbor=1 ) def _fit_resample(self, X, y): self._validate_estimator() X_resampled = X.copy() y_resampled = y.copy() self.in_danger_indices = {} for class_sample, n_samples in self.sampling_strategy_.items(): if n_samples == 0: continue target_class_indices = np.flatnonzero(y == class_sample) X_class = _safe_indexing(X, target_class_indices) self.nn_m_.fit(X) mask_danger = self._in_danger_noise( self.nn_m_, X_class, class_sample, y, kind="danger" ) if not any(mask_danger): continue X_danger = _safe_indexing(X_class, mask_danger) self.in_danger_indices[class_sample] = target_class_indices[mask_danger] if self.kind == "borderline-1": X_to_sample_from = X_class # consider the positive class only y_to_check_neighbors = None else: # self.kind == "borderline-2" X_to_sample_from = X # consider the whole dataset y_to_check_neighbors = y self.nn_k_.fit(X_to_sample_from) nns = self.nn_k_.kneighbors(X_danger, return_distance=False)[:, 1:] X_new, y_new = self._make_samples( X_danger, y.dtype, class_sample, X_to_sample_from, nns, n_samples, y=y_to_check_neighbors, ) if sparse.issparse(X_new): X_resampled = sparse.vstack([X_resampled, X_new]) else: X_resampled = np.vstack((X_resampled, X_new)) y_resampled = np.hstack((y_resampled, y_new)) return X_resampled, y_resampled @Substitution( sampling_strategy=BaseOverSampler._sampling_strategy_docstring, random_state=_random_state_docstring, ) class SVMSMOTE(BaseSMOTE): """Over-sampling using SVM-SMOTE. Variant of SMOTE algorithm which use an SVM algorithm to detect sample to use for generating new synthetic samples as proposed in [2]_. Read more in the :ref:`User Guide `. .. versionadded:: 0.4 Parameters ---------- {sampling_strategy} {random_state} k_neighbors : int or object, default=5 The nearest neighbors used to define the neighborhood of samples to use to generate the synthetic samples. You can pass: - an `int` corresponding to the number of neighbors to use. A `~sklearn.neighbors.NearestNeighbors` instance will be fitted in this case. - an instance of a compatible nearest neighbors algorithm that should implement both methods `kneighbors` and `kneighbors_graph`. For instance, it could correspond to a :class:`~sklearn.neighbors.NearestNeighbors` but could be extended to any compatible class. m_neighbors : int or object, default=10 The nearest neighbors used to determine if a minority sample is in "danger". You can pass: - an `int` corresponding to the number of neighbors to use. A `~sklearn.neighbors.NearestNeighbors` instance will be fitted in this case. - an instance of a compatible nearest neighbors algorithm that should implement both methods `kneighbors` and `kneighbors_graph`. For instance, it could correspond to a :class:`~sklearn.neighbors.NearestNeighbors` but could be extended to any compatible class. svm_estimator : estimator object, default=SVC() A parametrized :class:`~sklearn.svm.SVC` classifier can be passed. A scikit-learn compatible estimator can be passed but it is required to expose a `support_` fitted attribute. out_step : float, default=0.5 Step size when extrapolating. Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. nn_k_ : estimator object Validated k-nearest neighbours created from the `k_neighbors` parameter. nn_m_ : estimator object Validated m-nearest neighbours created from the `m_neighbors` parameter. svm_estimator_ : estimator object The validated SVM classifier used to detect samples from which to generate new synthetic samples. n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- SMOTE : Over-sample using SMOTE. SMOTENC : Over-sample using SMOTE for continuous and categorical features. SMOTEN : Over-sample using the SMOTE variant specifically for categorical features only. BorderlineSMOTE : Over-sample using Borderline-SMOTE. ADASYN : Over-sample using ADASYN. KMeansSMOTE : Over-sample applying a clustering before to oversample using SMOTE. Notes ----- See the original papers: [2]_ for more details. Supports multi-class resampling. A one-vs.-rest scheme is used as originally proposed in [1]_. References ---------- .. [1] N. V. Chawla, K. W. Bowyer, L. O.Hall, W. P. Kegelmeyer, "SMOTE: synthetic minority over-sampling technique," Journal of artificial intelligence research, 321-357, 2002. .. [2] H. M. Nguyen, E. W. Cooper, K. Kamei, "Borderline over-sampling for imbalanced data classification," International Journal of Knowledge Engineering and Soft Data Paradigms, 3(1), pp.4-21, 2009. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from imblearn.over_sampling import SVMSMOTE >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> sm = SVMSMOTE(random_state=42) >>> X_res, y_res = sm.fit_resample(X, y) >>> print('Resampled dataset shape %s' % Counter(y_res)) Resampled dataset shape Counter({{0: 900, 1: 900}}) """ _parameter_constraints: dict = { **BaseSMOTE._parameter_constraints, "m_neighbors": [ Interval(numbers.Integral, 1, None, closed="left"), HasMethods(["kneighbors", "kneighbors_graph"]), ], "svm_estimator": [HasMethods(["fit", "predict"]), None], "out_step": [Interval(numbers.Real, 0, 1, closed="both")], } def __init__( self, *, sampling_strategy="auto", random_state=None, k_neighbors=5, m_neighbors=10, svm_estimator=None, out_step=0.5, ): super().__init__( sampling_strategy=sampling_strategy, random_state=random_state, k_neighbors=k_neighbors, ) self.m_neighbors = m_neighbors self.svm_estimator = svm_estimator self.out_step = out_step def _validate_estimator(self): super()._validate_estimator() self.nn_m_ = check_neighbors_object( "m_neighbors", self.m_neighbors, additional_neighbor=1 ) if self.svm_estimator is None: self.svm_estimator_ = SVC(gamma="scale", random_state=self.random_state) else: self.svm_estimator_ = clone(self.svm_estimator) def _fit_resample(self, X, y): self._validate_estimator() random_state = check_random_state(self.random_state) X_resampled = X.copy() y_resampled = y.copy() for class_sample, n_samples in self.sampling_strategy_.items(): if n_samples == 0: continue target_class_indices = np.flatnonzero(y == class_sample) X_class = _safe_indexing(X, target_class_indices) self.svm_estimator_.fit(X, y) if not hasattr(self.svm_estimator_, "support_"): raise RuntimeError( "`svm_estimator` is required to exposed a `support_` fitted " "attribute. Such estimator belongs to the familly of Support " "Vector Machine." ) support_index = self.svm_estimator_.support_[ y[self.svm_estimator_.support_] == class_sample ] support_vector = _safe_indexing(X, support_index) self.nn_m_.fit(X) noise_bool = self._in_danger_noise( self.nn_m_, support_vector, class_sample, y, kind="noise" ) support_vector = _safe_indexing( support_vector, np.flatnonzero(np.logical_not(noise_bool)) ) if support_vector.shape[0] == 0: raise ValueError( "All support vectors are considered as noise. SVM-SMOTE is not " "adapted to your dataset. Try another SMOTE variant." ) danger_bool = self._in_danger_noise( self.nn_m_, support_vector, class_sample, y, kind="danger" ) safety_bool = np.logical_not(danger_bool) self.nn_k_.fit(X_class) fractions = random_state.beta(10, 10) n_generated_samples = int(fractions * (n_samples + 1)) if np.count_nonzero(danger_bool) > 0: nns = self.nn_k_.kneighbors( _safe_indexing(support_vector, np.flatnonzero(danger_bool)), return_distance=False, )[:, 1:] X_new_1, y_new_1 = self._make_samples( _safe_indexing(support_vector, np.flatnonzero(danger_bool)), y.dtype, class_sample, X_class, nns, n_generated_samples, step_size=1.0, ) if np.count_nonzero(safety_bool) > 0: nns = self.nn_k_.kneighbors( _safe_indexing(support_vector, np.flatnonzero(safety_bool)), return_distance=False, )[:, 1:] X_new_2, y_new_2 = self._make_samples( _safe_indexing(support_vector, np.flatnonzero(safety_bool)), y.dtype, class_sample, X_class, nns, n_samples - n_generated_samples, step_size=-self.out_step, ) if np.count_nonzero(danger_bool) > 0 and np.count_nonzero(safety_bool) > 0: if sparse.issparse(X_resampled): X_resampled = sparse.vstack([X_resampled, X_new_1, X_new_2]) else: X_resampled = np.vstack((X_resampled, X_new_1, X_new_2)) y_resampled = np.concatenate((y_resampled, y_new_1, y_new_2), axis=0) elif np.count_nonzero(danger_bool) == 0: if sparse.issparse(X_resampled): X_resampled = sparse.vstack([X_resampled, X_new_2]) else: X_resampled = np.vstack((X_resampled, X_new_2)) y_resampled = np.concatenate((y_resampled, y_new_2), axis=0) elif np.count_nonzero(safety_bool) == 0: if sparse.issparse(X_resampled): X_resampled = sparse.vstack([X_resampled, X_new_1]) else: X_resampled = np.vstack((X_resampled, X_new_1)) y_resampled = np.concatenate((y_resampled, y_new_1), axis=0) return X_resampled, y_resampled scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/_smote/tests/000077500000000000000000000000001521136113700302645ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/_smote/tests/__init__.py000066400000000000000000000000001521136113700323630ustar00rootroot00000000000000test_borderline_smote.py000066400000000000000000000066421521136113700351620ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/_smote/testsfrom collections import Counter import pytest from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.utils._testing import assert_allclose, assert_array_equal from imblearn.over_sampling import BorderlineSMOTE @pytest.mark.parametrize("kind", ["borderline-1", "borderline-2"]) def test_borderline_smote_no_in_danger_samples(kind): """Check that the algorithm behave properly even on a dataset without any sample in danger. """ X, y = make_classification( n_samples=500, n_features=2, n_informative=2, n_redundant=0, n_repeated=0, n_clusters_per_class=1, n_classes=3, weights=[0.1, 0.2, 0.7], class_sep=1.5, random_state=1, ) smote = BorderlineSMOTE(kind=kind, m_neighbors=3, k_neighbors=5, random_state=0) X_res, y_res = smote.fit_resample(X, y) assert_allclose(X, X_res) assert_allclose(y, y_res) assert not smote.in_danger_indices def test_borderline_smote_kind(): """Check the behaviour of the `kind` parameter. In short, "borderline-2" generates sample closer to the boundary decision than "borderline-1". We generate an example where a logistic regression will perform worse on "borderline-2" than on "borderline-1". """ X, y = make_classification( n_samples=500, n_features=2, n_informative=2, n_redundant=0, n_repeated=0, n_clusters_per_class=1, n_classes=3, weights=[0.1, 0.2, 0.7], class_sep=1.0, random_state=1, ) smote = BorderlineSMOTE( kind="borderline-1", m_neighbors=9, k_neighbors=5, random_state=0 ) X_res_borderline_1, y_res_borderline_1 = smote.fit_resample(X, y) smote.set_params(kind="borderline-2") X_res_borderline_2, y_res_borderline_2 = smote.fit_resample(X, y) score_borderline_1 = ( LogisticRegression() .fit(X_res_borderline_1, y_res_borderline_1) .score(X_res_borderline_1, y_res_borderline_1) ) score_borderline_2 = ( LogisticRegression() .fit(X_res_borderline_2, y_res_borderline_2) .score(X_res_borderline_2, y_res_borderline_2) ) assert score_borderline_1 > score_borderline_2 def test_borderline_smote_in_danger(): X, y = make_classification( n_samples=500, n_features=2, n_informative=2, n_redundant=0, n_repeated=0, n_clusters_per_class=1, n_classes=3, weights=[0.1, 0.2, 0.7], class_sep=0.8, random_state=1, ) smote = BorderlineSMOTE( kind="borderline-1", m_neighbors=9, k_neighbors=5, random_state=0, ) _, y_res_1 = smote.fit_resample(X, y) in_danger_indices_borderline_1 = smote.in_danger_indices smote.set_params(kind="borderline-2") _, y_res_2 = smote.fit_resample(X, y) in_danger_indices_borderline_2 = smote.in_danger_indices for key1, key2 in zip( in_danger_indices_borderline_1, in_danger_indices_borderline_2 ): assert_array_equal( in_danger_indices_borderline_1[key1], in_danger_indices_borderline_2[key2] ) assert len(in_danger_indices_borderline_1) == len(in_danger_indices_borderline_2) counter = Counter(y_res_1) assert counter[0] == counter[1] == counter[2] counter = Counter(y_res_2) assert counter[0] == counter[1] == counter[2] test_kmeans_smote.py000066400000000000000000000070601521136113700343060ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/_smote/testsimport numpy as np import pytest from sklearn.cluster import KMeans, MiniBatchKMeans from sklearn.datasets import make_classification from sklearn.neighbors import NearestNeighbors from sklearn.utils._testing import assert_allclose, assert_array_equal from imblearn.over_sampling import SMOTE, KMeansSMOTE @pytest.fixture def data(): X = np.array( [ [0.11622591, -0.0317206], [0.77481731, 0.60935141], [1.25192108, -0.22367336], [0.53366841, -0.30312976], [1.52091956, -0.49283504], [-0.28162401, -2.10400981], [0.83680821, 1.72827342], [0.3084254, 0.33299982], [0.70472253, -0.73309052], [0.28893132, -0.38761769], [1.15514042, 0.0129463], [0.88407872, 0.35454207], [1.31301027, -0.92648734], [-1.11515198, -0.93689695], [-0.18410027, -0.45194484], [0.9281014, 0.53085498], [-0.14374509, 0.27370049], [-0.41635887, -0.38299653], [0.08711622, 0.93259929], [1.70580611, -0.11219234], ] ) y = np.array([0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0]) return X, y @pytest.mark.filterwarnings("ignore:The default value of `n_init` will change") def test_kmeans_smote(data): X, y = data kmeans_smote = KMeansSMOTE( kmeans_estimator=1, random_state=42, cluster_balance_threshold=0.0, k_neighbors=5, ) smote = SMOTE(random_state=42) X_res_1, y_res_1 = kmeans_smote.fit_resample(X, y) X_res_2, y_res_2 = smote.fit_resample(X, y) assert_allclose(X_res_1, X_res_2) assert_array_equal(y_res_1, y_res_2) assert kmeans_smote.nn_k_.n_neighbors == 6 assert kmeans_smote.kmeans_estimator_.n_clusters == 1 assert "batch_size" in kmeans_smote.kmeans_estimator_.get_params() @pytest.mark.filterwarnings("ignore:The default value of `n_init` will change") @pytest.mark.parametrize("k_neighbors", [2, NearestNeighbors(n_neighbors=3)]) @pytest.mark.parametrize( "kmeans_estimator", [ 3, KMeans(n_clusters=3, n_init=1, random_state=42), MiniBatchKMeans(n_clusters=3, n_init=1, random_state=42), ], ) def test_sample_kmeans_custom(data, k_neighbors, kmeans_estimator): X, y = data kmeans_smote = KMeansSMOTE( random_state=42, kmeans_estimator=kmeans_estimator, k_neighbors=k_neighbors, ) X_resampled, y_resampled = kmeans_smote.fit_resample(X, y) assert X_resampled.shape == (24, 2) assert y_resampled.shape == (24,) assert kmeans_smote.nn_k_.n_neighbors == 3 assert kmeans_smote.kmeans_estimator_.n_clusters == 3 @pytest.mark.filterwarnings("ignore:The default value of `n_init` will change") def test_sample_kmeans_not_enough_clusters(data): X, y = data smote = KMeansSMOTE(cluster_balance_threshold=10, random_state=42) with pytest.raises(RuntimeError): smote.fit_resample(X, y) @pytest.mark.parametrize("density_exponent", ["auto", 10]) @pytest.mark.parametrize("cluster_balance_threshold", ["auto", 0.1]) def test_sample_kmeans_density_estimation(density_exponent, cluster_balance_threshold): X, y = make_classification( n_samples=10_000, n_classes=2, weights=[0.3, 0.7], random_state=42 ) smote = KMeansSMOTE( kmeans_estimator=MiniBatchKMeans(n_init=1, random_state=42), random_state=0, density_exponent=density_exponent, cluster_balance_threshold=cluster_balance_threshold, ) smote.fit_resample(X, y) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/_smote/tests/test_smote.py000066400000000000000000000116661521136113700330360ustar00rootroot00000000000000"""Test the module SMOTE.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numpy as np from sklearn.neighbors import NearestNeighbors from sklearn.utils._testing import assert_allclose, assert_array_equal from imblearn.over_sampling import SMOTE RND_SEED = 0 X = np.array( [ [0.11622591, -0.0317206], [0.77481731, 0.60935141], [1.25192108, -0.22367336], [0.53366841, -0.30312976], [1.52091956, -0.49283504], [-0.28162401, -2.10400981], [0.83680821, 1.72827342], [0.3084254, 0.33299982], [0.70472253, -0.73309052], [0.28893132, -0.38761769], [1.15514042, 0.0129463], [0.88407872, 0.35454207], [1.31301027, -0.92648734], [-1.11515198, -0.93689695], [-0.18410027, -0.45194484], [0.9281014, 0.53085498], [-0.14374509, 0.27370049], [-0.41635887, -0.38299653], [0.08711622, 0.93259929], [1.70580611, -0.11219234], ] ) Y = np.array([0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0]) R_TOL = 1e-4 def test_sample_regular(): smote = SMOTE(random_state=RND_SEED) X_resampled, y_resampled = smote.fit_resample(X, Y) X_gt = np.array( [ [0.11622591, -0.0317206], [0.77481731, 0.60935141], [1.25192108, -0.22367336], [0.53366841, -0.30312976], [1.52091956, -0.49283504], [-0.28162401, -2.10400981], [0.83680821, 1.72827342], [0.3084254, 0.33299982], [0.70472253, -0.73309052], [0.28893132, -0.38761769], [1.15514042, 0.0129463], [0.88407872, 0.35454207], [1.31301027, -0.92648734], [-1.11515198, -0.93689695], [-0.18410027, -0.45194484], [0.9281014, 0.53085498], [-0.14374509, 0.27370049], [-0.41635887, -0.38299653], [0.08711622, 0.93259929], [1.70580611, -0.11219234], [0.29307743, -0.14670439], [0.84976473, -0.15570176], [0.61319159, -0.11571668], [0.66052536, -0.28246517], ] ) y_gt = np.array( [0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0] ) assert_allclose(X_resampled, X_gt, rtol=R_TOL) assert_array_equal(y_resampled, y_gt) def test_sample_regular_half(): sampling_strategy = {0: 9, 1: 12} smote = SMOTE(sampling_strategy=sampling_strategy, random_state=RND_SEED) X_resampled, y_resampled = smote.fit_resample(X, Y) X_gt = np.array( [ [0.11622591, -0.0317206], [0.77481731, 0.60935141], [1.25192108, -0.22367336], [0.53366841, -0.30312976], [1.52091956, -0.49283504], [-0.28162401, -2.10400981], [0.83680821, 1.72827342], [0.3084254, 0.33299982], [0.70472253, -0.73309052], [0.28893132, -0.38761769], [1.15514042, 0.0129463], [0.88407872, 0.35454207], [1.31301027, -0.92648734], [-1.11515198, -0.93689695], [-0.18410027, -0.45194484], [0.9281014, 0.53085498], [-0.14374509, 0.27370049], [-0.41635887, -0.38299653], [0.08711622, 0.93259929], [1.70580611, -0.11219234], [0.36784496, -0.1953161], ] ) y_gt = np.array([0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0]) assert_allclose(X_resampled, X_gt, rtol=R_TOL) assert_array_equal(y_resampled, y_gt) def test_sample_regular_with_nn(): nn_k = NearestNeighbors(n_neighbors=6) smote = SMOTE(random_state=RND_SEED, k_neighbors=nn_k) X_resampled, y_resampled = smote.fit_resample(X, Y) X_gt = np.array( [ [0.11622591, -0.0317206], [0.77481731, 0.60935141], [1.25192108, -0.22367336], [0.53366841, -0.30312976], [1.52091956, -0.49283504], [-0.28162401, -2.10400981], [0.83680821, 1.72827342], [0.3084254, 0.33299982], [0.70472253, -0.73309052], [0.28893132, -0.38761769], [1.15514042, 0.0129463], [0.88407872, 0.35454207], [1.31301027, -0.92648734], [-1.11515198, -0.93689695], [-0.18410027, -0.45194484], [0.9281014, 0.53085498], [-0.14374509, 0.27370049], [-0.41635887, -0.38299653], [0.08711622, 0.93259929], [1.70580611, -0.11219234], [0.29307743, -0.14670439], [0.84976473, -0.15570176], [0.61319159, -0.11571668], [0.66052536, -0.28246517], ] ) y_gt = np.array( [0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0] ) assert_allclose(X_resampled, X_gt, rtol=R_TOL) assert_array_equal(y_resampled, y_gt) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/_smote/tests/test_smote_nc.py000066400000000000000000000344121521136113700335100ustar00rootroot00000000000000"""Test the module SMOTENC.""" # Authors: Guillaume Lemaitre # Christos Aridas # Dzianis Dudnik # License: MIT from collections import Counter import numpy as np import pytest from scipy import sparse from sklearn.datasets import make_classification from sklearn.preprocessing import OneHotEncoder from sklearn.utils._testing import assert_allclose, assert_array_equal from imblearn.over_sampling import SMOTENC def data_heterogneous_ordered(): rng = np.random.RandomState(42) X = np.empty((30, 4), dtype=object) # create 2 random continuous feature X[:, :2] = rng.randn(30, 2) # create a categorical feature using some string X[:, 2] = rng.choice(["a", "b", "c"], size=30).astype(object) # create a categorical feature using some integer X[:, 3] = rng.randint(3, size=30) y = np.array([0] * 10 + [1] * 20) # return the categories return X, y, [2, 3] def data_heterogneous_unordered(): rng = np.random.RandomState(42) X = np.empty((30, 4), dtype=object) # create 2 random continuous feature X[:, [1, 2]] = rng.randn(30, 2) # create a categorical feature using some string X[:, 0] = rng.choice(["a", "b", "c"], size=30).astype(object) # create a categorical feature using some integer X[:, 3] = rng.randint(3, size=30) y = np.array([0] * 10 + [1] * 20) # return the categories return X, y, [0, 3] def data_heterogneous_masked(): rng = np.random.RandomState(42) X = np.empty((30, 4), dtype=object) # create 2 random continuous feature X[:, [1, 2]] = rng.randn(30, 2) # create a categorical feature using some string X[:, 0] = rng.choice(["a", "b", "c"], size=30).astype(object) # create a categorical feature using some integer X[:, 3] = rng.randint(3, size=30) y = np.array([0] * 10 + [1] * 20) # return the categories return X, y, [True, False, False, True] def data_heterogneous_unordered_multiclass(): rng = np.random.RandomState(42) X = np.empty((50, 4), dtype=object) # create 2 random continuous feature X[:, [1, 2]] = rng.randn(50, 2) # create a categorical feature using some string X[:, 0] = rng.choice(["a", "b", "c"], size=50).astype(object) # create a categorical feature using some integer X[:, 3] = rng.randint(3, size=50) y = np.array([0] * 10 + [1] * 15 + [2] * 25) # return the categories return X, y, [0, 3] def data_sparse(format): rng = np.random.RandomState(42) X = np.empty((30, 4), dtype=np.float64) # create 2 random continuous feature X[:, [1, 2]] = rng.randn(30, 2) # create a categorical feature using some string X[:, 0] = rng.randint(3, size=30) # create a categorical feature using some integer X[:, 3] = rng.randint(3, size=30) y = np.array([0] * 10 + [1] * 20) X = sparse.csr_matrix(X) if format == "csr" else sparse.csc_matrix(X) return X, y, [0, 3] def test_smotenc_error(): X, y, _ = data_heterogneous_unordered() categorical_features = [0, 10] smote = SMOTENC(random_state=0, categorical_features=categorical_features) with pytest.raises(ValueError, match="all features must be in"): smote.fit_resample(X, y) @pytest.mark.parametrize( "data", [ data_heterogneous_ordered(), data_heterogneous_unordered(), data_heterogneous_masked(), data_sparse("csr"), data_sparse("csc"), ], ) def test_smotenc(data): X, y, categorical_features = data smote = SMOTENC(random_state=0, categorical_features=categorical_features) X_resampled, y_resampled = smote.fit_resample(X, y) assert X_resampled.dtype == X.dtype categorical_features = np.array(categorical_features) if categorical_features.dtype == bool: categorical_features = np.flatnonzero(categorical_features) for cat_idx in categorical_features: if sparse.issparse(X): assert set(X[:, cat_idx].data) == set(X_resampled[:, cat_idx].data) assert X[:, cat_idx].dtype == X_resampled[:, cat_idx].dtype else: assert set(X[:, cat_idx]) == set(X_resampled[:, cat_idx]) assert X[:, cat_idx].dtype == X_resampled[:, cat_idx].dtype assert isinstance(smote.median_std_, dict) # part of the common test which apply to SMOTE-NC even if it is not default # constructible def test_smotenc_check_target_type(): X, _, categorical_features = data_heterogneous_unordered() y = np.linspace(0, 1, 30) smote = SMOTENC(categorical_features=categorical_features, random_state=0) with pytest.raises(ValueError, match="Unknown label type"): smote.fit_resample(X, y) rng = np.random.RandomState(42) y = rng.randint(2, size=(20, 3)) msg = "Multilabel and multioutput targets are not supported." with pytest.raises(ValueError, match=msg): smote.fit_resample(X, y) def test_smotenc_samplers_one_label(): X, _, categorical_features = data_heterogneous_unordered() y = np.zeros(30) smote = SMOTENC(categorical_features=categorical_features, random_state=0) with pytest.raises(ValueError, match="needs to have more than 1 class"): smote.fit(X, y) def test_smotenc_fit(): X, y, categorical_features = data_heterogneous_unordered() smote = SMOTENC(categorical_features=categorical_features, random_state=0) smote.fit_resample(X, y) assert hasattr( smote, "sampling_strategy_" ), "No fitted attribute sampling_strategy_" def test_smotenc_fit_resample(): X, y, categorical_features = data_heterogneous_unordered() target_stats = Counter(y) smote = SMOTENC(categorical_features=categorical_features, random_state=0) _, y_res = smote.fit_resample(X, y) _ = Counter(y_res) n_samples = max(target_stats.values()) assert all(value >= n_samples for value in Counter(y_res).values()) def test_smotenc_fit_resample_sampling_strategy(): X, y, categorical_features = data_heterogneous_unordered_multiclass() expected_stat = Counter(y)[1] smote = SMOTENC(categorical_features=categorical_features, random_state=0) sampling_strategy = {2: 25, 0: 25} smote.set_params(sampling_strategy=sampling_strategy) X_res, y_res = smote.fit_resample(X, y) assert Counter(y_res)[1] == expected_stat def test_smotenc_pandas(): pd = pytest.importorskip("pandas") # Check that the samplers handle pandas dataframe and pandas series X, y, categorical_features = data_heterogneous_unordered_multiclass() X_pd = pd.DataFrame(X) smote = SMOTENC(categorical_features=categorical_features, random_state=0) X_res_pd, y_res_pd = smote.fit_resample(X_pd, y) X_res, y_res = smote.fit_resample(X, y) assert_array_equal(X_res_pd.to_numpy(), X_res) assert_allclose(y_res_pd, y_res) assert set(smote.median_std_.keys()) == {0, 1} def test_smotenc_preserve_dtype(): X, y = make_classification( n_samples=50, n_classes=3, n_informative=4, weights=[0.2, 0.3, 0.5], random_state=0, ) # Cast X and y to not default dtype X = X.astype(np.float32) y = y.astype(np.int32) smote = SMOTENC(categorical_features=[1], random_state=0) X_res, y_res = smote.fit_resample(X, y) assert X.dtype == X_res.dtype, "X dtype is not preserved" assert y.dtype == y_res.dtype, "y dtype is not preserved" @pytest.mark.parametrize("categorical_features", [[True, True, True], [0, 1, 2]]) def test_smotenc_raising_error_all_categorical(categorical_features): X, y = make_classification( n_features=3, n_informative=1, n_redundant=1, n_repeated=0, n_clusters_per_class=1, ) smote = SMOTENC(categorical_features=categorical_features) err_msg = "SMOTE-NC is not designed to work only with categorical features" with pytest.raises(ValueError, match=err_msg): smote.fit_resample(X, y) def test_smote_nc_with_null_median_std(): # Non-regression test for #662 # https://github.com/scikit-learn-contrib/imbalanced-learn/issues/662 data = np.array( [ [1, 2, 1, "A"], [2, 1, 2, "A"], [2, 1, 2, "A"], [1, 2, 3, "B"], [1, 2, 4, "C"], [1, 2, 5, "C"], [1, 2, 4, "C"], [1, 2, 4, "C"], [1, 2, 4, "C"], ], dtype="object", ) labels = np.array( [ "class_1", "class_1", "class_1", "class_1", "class_2", "class_2", "class_3", "class_3", "class_3", ], dtype=object, ) smote = SMOTENC(categorical_features=[3], k_neighbors=1, random_state=0) X_res, y_res = smote.fit_resample(data, labels) # check that the categorical feature is not random but correspond to the # categories seen in the minority class samples assert_array_equal(X_res[-3:, -1], np.array(["C", "C", "C"], dtype=object)) assert smote.median_std_ == {"class_2": 0.0, "class_3": 0.0} def test_smotenc_categorical_encoder(): """Check that we can pass our own categorical encoder.""" X, y, categorical_features = data_heterogneous_unordered() smote = SMOTENC(categorical_features=categorical_features, random_state=0) smote.fit_resample(X, y) assert getattr(smote.categorical_encoder_, "sparse_output") is True encoder = OneHotEncoder(sparse_output=False) smote.set_params(categorical_encoder=encoder).fit_resample(X, y) assert smote.categorical_encoder is encoder assert smote.categorical_encoder_ is not encoder assert getattr(smote.categorical_encoder_, "sparse_output") is False @pytest.mark.parametrize("drop", ["first", "if_binary"]) def test_smotenc_categorical_encoder_dropped_columns(drop): """Check that a clear error is raised when the categorical encoder does not keep one column per category (e.g. ``OneHotEncoder(drop=...)``). Non-regression test for: https://github.com/scikit-learn-contrib/imbalanced-learn/issues/1035 """ rng = np.random.RandomState(0) n_samples = 200 X = np.hstack( [ rng.randn(n_samples, 2), rng.randint(0, 2, size=(n_samples, 1)), # binary categorical rng.randint(0, 4, size=(n_samples, 1)), rng.randint(0, 3, size=(n_samples, 1)), ] ).astype(object) y = np.array([1] * 40 + [0] * (n_samples - 40)) rng.shuffle(y) encoder = OneHotEncoder(drop=drop, handle_unknown="ignore") smote = SMOTENC( categorical_features=[2, 3, 4], categorical_encoder=encoder, sampling_strategy="minority", random_state=0, ) err_msg = "SMOTENC requires a one-hot encoding with one column per category" with pytest.raises(ValueError, match=err_msg): smote.fit_resample(X, y) def test_smotenc_bool_categorical(): """Check that we don't try to early convert the full input data to numeric when handling a pandas dataframe. Non-regression test for: https://github.com/scikit-learn-contrib/imbalanced-learn/issues/974 """ pd = pytest.importorskip("pandas") X = pd.DataFrame( { "c": pd.Categorical(list("abbacaba" * 3)), "f": [0.3, 0.5, 0.1, 0.2] * 6, "b": [False, False, True] * 8, } ) y = pd.DataFrame({"out": [1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0] * 2}) smote = SMOTENC(categorical_features=[0]) X_res, y_res = smote.fit_resample(X, y) pd.testing.assert_series_equal(X_res.dtypes, X.dtypes) assert len(X_res) == len(y_res) smote.set_params(categorical_features=[0, 2]) X_res, y_res = smote.fit_resample(X, y) pd.testing.assert_series_equal(X_res.dtypes, X.dtypes) assert len(X_res) == len(y_res) X = X.astype({"b": "category"}) X_res, y_res = smote.fit_resample(X, y) pd.testing.assert_series_equal(X_res.dtypes, X.dtypes) assert len(X_res) == len(y_res) def test_smotenc_categorical_features_str(): """Check that we support array-like of strings for `categorical_features` using pandas dataframe. """ pd = pytest.importorskip("pandas") X = pd.DataFrame( { "A": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "B": ["a", "b"] * 5, "C": ["a", "b", "c"] * 3 + ["a"], } ) X = pd.concat([X] * 10, ignore_index=True) y = np.array([0] * 70 + [1] * 30) smote = SMOTENC(categorical_features=["B", "C"], random_state=0) X_res, y_res = smote.fit_resample(X, y) assert X_res["B"].isin(["a", "b"]).all() assert X_res["C"].isin(["a", "b", "c"]).all() counter = Counter(y_res) assert counter[0] == counter[1] == 70 assert_array_equal(smote.categorical_features_, [1, 2]) assert_array_equal(smote.continuous_features_, [0]) def test_smotenc_categorical_features_auto(): """Check that we can automatically detect categorical features based on pandas dataframe. """ pd = pytest.importorskip("pandas") X = pd.DataFrame( { "A": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "B": ["a", "b"] * 5, "C": ["a", "b", "c"] * 3 + ["a"], } ) X = pd.concat([X] * 10, ignore_index=True) X["B"] = X["B"].astype("category") X["C"] = X["C"].astype("category") y = np.array([0] * 70 + [1] * 30) smote = SMOTENC(categorical_features="auto", random_state=0) X_res, y_res = smote.fit_resample(X, y) assert X_res["B"].isin(["a", "b"]).all() assert X_res["C"].isin(["a", "b", "c"]).all() counter = Counter(y_res) assert counter[0] == counter[1] == 70 assert_array_equal(smote.categorical_features_, [1, 2]) assert_array_equal(smote.continuous_features_, [0]) def test_smote_nc_categorical_features_auto_error(): """Check that we raise a proper error when we cannot use the `'auto'` mode.""" pd = pytest.importorskip("pandas") X = pd.DataFrame( { "A": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], "B": ["a", "b"] * 5, "C": ["a", "b", "c"] * 3 + ["a"], } ) y = np.array([0] * 70 + [1] * 30) smote = SMOTENC(categorical_features="auto", random_state=0) with pytest.raises(ValueError, match="the input data should be a pandas.DataFrame"): smote.fit_resample(X.to_numpy(), y) err_msg = "SMOTE-NC is not designed to work only with numerical features" with pytest.raises(ValueError, match=err_msg): smote.fit_resample(X, y) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/_smote/tests/test_smoten.py000066400000000000000000000062601521136113700332060ustar00rootroot00000000000000import numpy as np import pytest from sklearn.exceptions import DataConversionWarning from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder from sklearn_compat.utils._testing import _convert_container from imblearn.over_sampling import SMOTEN @pytest.fixture def data(): rng = np.random.RandomState(0) feature_1 = ["A"] * 10 + ["B"] * 20 + ["C"] * 30 feature_2 = ["A"] * 40 + ["B"] * 20 feature_3 = ["A"] * 20 + ["B"] * 20 + ["C"] * 10 + ["D"] * 10 X = np.array([feature_1, feature_2, feature_3], dtype=object).T rng.shuffle(X) y = np.array([0] * 20 + [1] * 40, dtype=np.int32) y_labels = np.array(["not apple", "apple"], dtype=object) y = y_labels[y] return X, y def test_smoten(data): # overall check for SMOTEN X, y = data sampler = SMOTEN(random_state=0) X_res, y_res = sampler.fit_resample(X, y) assert X_res.shape == (80, 3) assert y_res.shape == (80,) assert isinstance(sampler.categorical_encoder_, OrdinalEncoder) def test_smoten_resampling(): # check if the SMOTEN resample data as expected # we generate data such that "not apple" will be the minority class and # samples from this class will be generated. We will force the "blue" # category to be associated with this class. Therefore, the new generated # samples should as well be from the "blue" category. X = np.array(["green"] * 5 + ["red"] * 10 + ["blue"] * 7, dtype=object).reshape( -1, 1 ) y = np.array( ["apple"] * 5 + ["not apple"] * 3 + ["apple"] * 7 + ["not apple"] * 5 + ["apple"] * 2, dtype=object, ) sampler = SMOTEN(random_state=0) X_res, y_res = sampler.fit_resample(X, y) X_generated, y_generated = X_res[X.shape[0] :], y_res[X.shape[0] :] np.testing.assert_array_equal(X_generated, "blue") np.testing.assert_array_equal(y_generated, "not apple") @pytest.mark.parametrize("sparse_format", ["sparse_csr", "sparse_csc"]) def test_smoten_sparse_input(data, sparse_format): """Check that we handle sparse input in SMOTEN even if it is not efficient. Non-regression test for: https://github.com/scikit-learn-contrib/imbalanced-learn/issues/971 """ X, y = data X = OneHotEncoder().fit_transform(X).toarray() X = _convert_container(X, sparse_format) with pytest.warns(DataConversionWarning, match="is not really efficient"): X_res, y_res = SMOTEN(random_state=0).fit_resample(X, y) assert X_res.format == X.format assert X_res.shape[0] == len(y_res) def test_smoten_categorical_encoder(data): """Check that `categorical_encoder` is used when provided.""" X, y = data sampler = SMOTEN(random_state=0) sampler.fit_resample(X, y) assert isinstance(sampler.categorical_encoder_, OrdinalEncoder) assert sampler.categorical_encoder_.dtype == np.int32 encoder = OrdinalEncoder(dtype=np.int64) sampler.set_params(categorical_encoder=encoder).fit_resample(X, y) assert isinstance(sampler.categorical_encoder_, OrdinalEncoder) assert sampler.categorical_encoder is encoder assert sampler.categorical_encoder_ is not encoder assert sampler.categorical_encoder_.dtype == np.int64 scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/_smote/tests/test_svm_smote.py000066400000000000000000000054541521136113700337210ustar00rootroot00000000000000import numpy as np import pytest from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from sklearn.neighbors import NearestNeighbors from sklearn.svm import SVC from sklearn.utils._testing import assert_allclose, assert_array_equal from imblearn.over_sampling import SVMSMOTE @pytest.fixture def data(): X = np.array( [ [0.11622591, -0.0317206], [0.77481731, 0.60935141], [1.25192108, -0.22367336], [0.53366841, -0.30312976], [1.52091956, -0.49283504], [-0.28162401, -2.10400981], [0.83680821, 1.72827342], [0.3084254, 0.33299982], [0.70472253, -0.73309052], [0.28893132, -0.38761769], [1.15514042, 0.0129463], [0.88407872, 0.35454207], [1.31301027, -0.92648734], [-1.11515198, -0.93689695], [-0.18410027, -0.45194484], [0.9281014, 0.53085498], [-0.14374509, 0.27370049], [-0.41635887, -0.38299653], [0.08711622, 0.93259929], [1.70580611, -0.11219234], ] ) y = np.array([0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0]) return X, y def test_svm_smote(data): svm_smote = SVMSMOTE(random_state=42) svm_smote_nn = SVMSMOTE( random_state=42, k_neighbors=NearestNeighbors(n_neighbors=6), m_neighbors=NearestNeighbors(n_neighbors=11), svm_estimator=SVC(gamma="scale", random_state=42), ) X_res_1, y_res_1 = svm_smote.fit_resample(*data) X_res_2, y_res_2 = svm_smote_nn.fit_resample(*data) assert_allclose(X_res_1, X_res_2) assert_array_equal(y_res_1, y_res_2) def test_svm_smote_not_svm(data): """Check that we raise a proper error if passing an estimator that does not expose a `support_` fitted attribute.""" err_msg = "`svm_estimator` is required to exposed a `support_` fitted attribute." with pytest.raises(RuntimeError, match=err_msg): SVMSMOTE(svm_estimator=LogisticRegression()).fit_resample(*data) def test_svm_smote_all_noise(data): """Check that we raise a proper error message when all support vectors are detected as noise and there is nothing that we can do. Non-regression test for: https://github.com/scikit-learn-contrib/imbalanced-learn/issues/742 """ X, y = make_classification( n_classes=3, class_sep=0.001, weights=[0.004, 0.451, 0.545], n_informative=3, n_redundant=0, flip_y=0, n_features=3, n_clusters_per_class=2, n_samples=1000, random_state=10, ) with pytest.raises(ValueError, match="SVM-SMOTE is not adapted to your dataset"): SVMSMOTE(k_neighbors=4, random_state=42).fit_resample(X, y) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/base.py000066400000000000000000000047651521136113700271340ustar00rootroot00000000000000""" Base class for the over-sampling method. """ # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numbers from collections.abc import Mapping from sklearn.utils._param_validation import Interval, StrOptions from imblearn.base import BaseSampler class BaseOverSampler(BaseSampler): """Base class for over-sampling algorithms. Warning: This class should not be used directly. Use the derive classes instead. """ _sampling_type = "over-sampling" _sampling_strategy_docstring = ( """sampling_strategy : float, str, dict or callable, default='auto' Sampling information to resample the data set. - When ``float``, it corresponds to the desired ratio of the number of samples in the minority class over the number of samples in the majority class after resampling. Therefore, the ratio is expressed as :math:`\\alpha_{os} = N_{rm} / N_{M}` where :math:`N_{rm}` is the number of samples in the minority class after resampling and :math:`N_{M}` is the number of samples in the majority class. .. warning:: ``float`` is only available for **binary** classification. An error is raised for multi-class classification. - When ``str``, specify the class targeted by the resampling. The number of samples in the different classes will be equalized. Possible choices are: ``'minority'``: resample only the minority class; ``'not minority'``: resample all classes but the minority class; ``'not majority'``: resample all classes but the majority class; ``'all'``: resample all classes; ``'auto'``: equivalent to ``'not majority'``. - When ``dict``, the keys correspond to the targeted classes. The values correspond to the desired number of samples for each targeted class. - When callable, function taking ``y`` and returns a ``dict``. The keys correspond to the targeted classes. The values correspond to the desired number of samples for each class. """.strip() ) # noqa: E501 _parameter_constraints: dict = { "sampling_strategy": [ Interval(numbers.Real, 0, 1, closed="right"), StrOptions({"auto", "minority", "not minority", "not majority", "all"}), Mapping, callable, ], "random_state": ["random_state"], } scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/tests/000077500000000000000000000000001521136113700267765ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/tests/__init__.py000066400000000000000000000000001521136113700310750ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/tests/test_adasyn.py000066400000000000000000000076011521136113700316720ustar00rootroot00000000000000"""Test the module under sampler.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numpy as np from sklearn.neighbors import NearestNeighbors from sklearn.utils._testing import assert_allclose, assert_array_equal from imblearn.over_sampling import ADASYN RND_SEED = 0 X = np.array( [ [0.11622591, -0.0317206], [0.77481731, 0.60935141], [1.25192108, -0.22367336], [0.53366841, -0.30312976], [1.52091956, -0.49283504], [-0.28162401, -2.10400981], [0.83680821, 1.72827342], [0.3084254, 0.33299982], [0.70472253, -0.73309052], [0.28893132, -0.38761769], [1.15514042, 0.0129463], [0.88407872, 0.35454207], [1.31301027, -0.92648734], [-1.11515198, -0.93689695], [-0.18410027, -0.45194484], [0.9281014, 0.53085498], [-0.14374509, 0.27370049], [-0.41635887, -0.38299653], [0.08711622, 0.93259929], [1.70580611, -0.11219234], ] ) Y = np.array([0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0]) R_TOL = 1e-4 def test_ada_init(): sampling_strategy = "auto" ada = ADASYN(sampling_strategy=sampling_strategy, random_state=RND_SEED) assert ada.random_state == RND_SEED def test_ada_fit_resample(): ada = ADASYN(random_state=RND_SEED) X_resampled, y_resampled = ada.fit_resample(X, Y) X_gt = np.array( [ [0.11622591, -0.0317206], [0.77481731, 0.60935141], [1.25192108, -0.22367336], [0.53366841, -0.30312976], [1.52091956, -0.49283504], [-0.28162401, -2.10400981], [0.83680821, 1.72827342], [0.3084254, 0.33299982], [0.70472253, -0.73309052], [0.28893132, -0.38761769], [1.15514042, 0.0129463], [0.88407872, 0.35454207], [1.31301027, -0.92648734], [-1.11515198, -0.93689695], [-0.18410027, -0.45194484], [0.9281014, 0.53085498], [-0.14374509, 0.27370049], [-0.41635887, -0.38299653], [0.08711622, 0.93259929], [1.70580611, -0.11219234], [0.88161986, -0.2829741], [0.35681689, -0.18814597], [1.4148276, 0.05308106], [0.3136591, -0.31327875], ] ) y_gt = np.array( [0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0] ) assert_allclose(X_resampled, X_gt, rtol=R_TOL) assert_array_equal(y_resampled, y_gt) def test_ada_fit_resample_nn_obj(): nn = NearestNeighbors(n_neighbors=6) ada = ADASYN(random_state=RND_SEED, n_neighbors=nn) X_resampled, y_resampled = ada.fit_resample(X, Y) X_gt = np.array( [ [0.11622591, -0.0317206], [0.77481731, 0.60935141], [1.25192108, -0.22367336], [0.53366841, -0.30312976], [1.52091956, -0.49283504], [-0.28162401, -2.10400981], [0.83680821, 1.72827342], [0.3084254, 0.33299982], [0.70472253, -0.73309052], [0.28893132, -0.38761769], [1.15514042, 0.0129463], [0.88407872, 0.35454207], [1.31301027, -0.92648734], [-1.11515198, -0.93689695], [-0.18410027, -0.45194484], [0.9281014, 0.53085498], [-0.14374509, 0.27370049], [-0.41635887, -0.38299653], [0.08711622, 0.93259929], [1.70580611, -0.11219234], [0.88161986, -0.2829741], [0.35681689, -0.18814597], [1.4148276, 0.05308106], [0.3136591, -0.31327875], ] ) y_gt = np.array( [0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0] ) assert_allclose(X_resampled, X_gt, rtol=R_TOL) assert_array_equal(y_resampled, y_gt) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/tests/test_common.py000066400000000000000000000071161521136113700317040ustar00rootroot00000000000000from collections import Counter import numpy as np import pytest from sklearn.cluster import MiniBatchKMeans from imblearn.over_sampling import ( ADASYN, SMOTE, SMOTEN, SMOTENC, SVMSMOTE, BorderlineSMOTE, KMeansSMOTE, ) from imblearn.utils.testing import _CustomNearestNeighbors @pytest.fixture def numerical_data(): rng = np.random.RandomState(0) X = rng.randn(100, 2) y = np.repeat([0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0], 5) return X, y @pytest.fixture def categorical_data(): rng = np.random.RandomState(0) feature_1 = ["A"] * 10 + ["B"] * 20 + ["C"] * 30 feature_2 = ["A"] * 40 + ["B"] * 20 feature_3 = ["A"] * 20 + ["B"] * 20 + ["C"] * 10 + ["D"] * 10 X = np.array([feature_1, feature_2, feature_3], dtype=object).T rng.shuffle(X) y = np.array([0] * 20 + [1] * 40, dtype=np.int32) y_labels = np.array(["not apple", "apple"], dtype=object) y = y_labels[y] return X, y @pytest.fixture def heterogeneous_data(): rng = np.random.RandomState(42) X = np.empty((30, 4), dtype=object) X[:, :2] = rng.randn(30, 2) X[:, 2] = rng.choice(["a", "b", "c"], size=30).astype(object) X[:, 3] = rng.randint(3, size=30) y = np.array([0] * 10 + [1] * 20) return X, y, [2, 3] @pytest.mark.parametrize( "smote", [BorderlineSMOTE(), SVMSMOTE()], ids=["borderline", "svm"] ) def test_smote_m_neighbors(numerical_data, smote): # check that m_neighbors is properly set. Regression test for: # https://github.com/scikit-learn-contrib/imbalanced-learn/issues/568 X, y = numerical_data _ = smote.fit_resample(X, y) assert smote.nn_k_.n_neighbors == 6 assert smote.nn_m_.n_neighbors == 11 @pytest.mark.parametrize( "smote, neighbor_estimator_name", [ (ADASYN(random_state=0), "n_neighbors"), (BorderlineSMOTE(random_state=0), "k_neighbors"), ( KMeansSMOTE( kmeans_estimator=MiniBatchKMeans(n_init=1, random_state=0), random_state=1, ), "k_neighbors", ), (SMOTE(random_state=0), "k_neighbors"), (SVMSMOTE(random_state=0), "k_neighbors"), ], ids=["adasyn", "borderline", "kmeans", "smote", "svm"], ) def test_numerical_smote_custom_nn(numerical_data, smote, neighbor_estimator_name): X, y = numerical_data params = { neighbor_estimator_name: _CustomNearestNeighbors(n_neighbors=5), } smote.set_params(**params) X_res, _ = smote.fit_resample(X, y) assert X_res.shape[0] >= 120 def test_categorical_smote_k_custom_nn(categorical_data): X, y = categorical_data smote = SMOTEN(k_neighbors=_CustomNearestNeighbors(n_neighbors=5)) X_res, y_res = smote.fit_resample(X, y) assert X_res.shape == (80, 3) assert Counter(y_res) == {"apple": 40, "not apple": 40} def test_heterogeneous_smote_k_custom_nn(heterogeneous_data): X, y, categorical_features = heterogeneous_data smote = SMOTENC( categorical_features, k_neighbors=_CustomNearestNeighbors(n_neighbors=5) ) X_res, y_res = smote.fit_resample(X, y) assert X_res.shape == (40, 4) assert Counter(y_res) == {0: 20, 1: 20} @pytest.mark.parametrize( "smote", [BorderlineSMOTE(random_state=0), SVMSMOTE(random_state=0)], ids=["borderline", "svm"], ) def test_numerical_smote_extra_custom_nn(numerical_data, smote): X, y = numerical_data smote.set_params(m_neighbors=_CustomNearestNeighbors(n_neighbors=5)) X_res, y_res = smote.fit_resample(X, y) assert X_res.shape == (120, 2) assert Counter(y_res) == {0: 60, 1: 60} test_random_over_sampler.py000066400000000000000000000235031521136113700343710ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/over_sampling/tests"""Test the module under sampler.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT from collections import Counter from datetime import datetime import numpy as np import pytest from sklearn.datasets import make_classification from sklearn.utils._testing import ( assert_allclose, assert_array_equal, ) from sklearn_compat.utils._testing import _convert_container from imblearn.over_sampling import RandomOverSampler RND_SEED = 0 @pytest.fixture def data(): X = np.array( [ [0.04352327, -0.20515826], [0.92923648, 0.76103773], [0.20792588, 1.49407907], [0.47104475, 0.44386323], [0.22950086, 0.33367433], [0.15490546, 0.3130677], [0.09125309, -0.85409574], [0.12372842, 0.6536186], [0.13347175, 0.12167502], [0.094035, -2.55298982], ] ) Y = np.array([1, 0, 1, 0, 1, 1, 1, 1, 0, 1]) return X, Y def test_ros_init(): sampling_strategy = "auto" ros = RandomOverSampler(sampling_strategy=sampling_strategy, random_state=RND_SEED) assert ros.random_state == RND_SEED @pytest.mark.parametrize( "params", [{"shrinkage": None}, {"shrinkage": 0}, {"shrinkage": {0: 0}}] ) @pytest.mark.parametrize("X_type", ["array", "dataframe"]) def test_ros_fit_resample(X_type, data, params): X, Y = data X_ = _convert_container(X, X_type) ros = RandomOverSampler(**params, random_state=RND_SEED) X_resampled, y_resampled = ros.fit_resample(X_, Y) X_gt = np.array( [ [0.04352327, -0.20515826], [0.92923648, 0.76103773], [0.20792588, 1.49407907], [0.47104475, 0.44386323], [0.22950086, 0.33367433], [0.15490546, 0.3130677], [0.09125309, -0.85409574], [0.12372842, 0.6536186], [0.13347175, 0.12167502], [0.094035, -2.55298982], [0.92923648, 0.76103773], [0.47104475, 0.44386323], [0.92923648, 0.76103773], [0.47104475, 0.44386323], ] ) y_gt = np.array([1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0]) if X_type == "dataframe": assert hasattr(X_resampled, "loc") X_resampled = X_resampled.to_numpy() assert_allclose(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) if params["shrinkage"] is None: assert ros.shrinkage_ is None else: assert ros.shrinkage_ == {0: 0} @pytest.mark.parametrize("params", [{"shrinkage": None}, {"shrinkage": 0}]) def test_ros_fit_resample_half(data, params): X, Y = data sampling_strategy = {0: 3, 1: 7} ros = RandomOverSampler( **params, sampling_strategy=sampling_strategy, random_state=RND_SEED ) X_resampled, y_resampled = ros.fit_resample(X, Y) X_gt = np.array( [ [0.04352327, -0.20515826], [0.92923648, 0.76103773], [0.20792588, 1.49407907], [0.47104475, 0.44386323], [0.22950086, 0.33367433], [0.15490546, 0.3130677], [0.09125309, -0.85409574], [0.12372842, 0.6536186], [0.13347175, 0.12167502], [0.094035, -2.55298982], ] ) y_gt = np.array([1, 0, 1, 0, 1, 1, 1, 1, 0, 1]) assert_allclose(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) if params["shrinkage"] is None: assert ros.shrinkage_ is None else: assert ros.shrinkage_ == {0: 0, 1: 0} @pytest.mark.parametrize("params", [{"shrinkage": None}, {"shrinkage": 0}]) def test_multiclass_fit_resample(data, params): # check the random over-sampling with a multiclass problem X, Y = data y = Y.copy() y[5] = 2 y[6] = 2 ros = RandomOverSampler(**params, random_state=RND_SEED) X_resampled, y_resampled = ros.fit_resample(X, y) count_y_res = Counter(y_resampled) assert count_y_res[0] == 5 assert count_y_res[1] == 5 assert count_y_res[2] == 5 if params["shrinkage"] is None: assert ros.shrinkage_ is None else: assert ros.shrinkage_ == {0: 0, 2: 0} def test_random_over_sampling_heterogeneous_data(): # check that resampling with heterogeneous dtype is working with basic # resampling X_hetero = np.array( [["xxx", 1, 1.0], ["yyy", 2, 2.0], ["zzz", 3, 3.0]], dtype=object ) y = np.array([0, 0, 1]) ros = RandomOverSampler(random_state=RND_SEED) X_res, y_res = ros.fit_resample(X_hetero, y) assert X_res.shape[0] == 4 assert y_res.shape[0] == 4 assert X_res.dtype == object assert X_res[-1, 0] in X_hetero[:, 0] def test_random_over_sampling_nan_inf(data): # check that we can oversample even with missing or infinite data # regression tests for #605 X, Y = data rng = np.random.RandomState(42) n_not_finite = X.shape[0] // 3 row_indices = rng.choice(np.arange(X.shape[0]), size=n_not_finite) col_indices = rng.randint(0, X.shape[1], size=n_not_finite) not_finite_values = rng.choice([np.nan, np.inf], size=n_not_finite) X_ = X.copy() X_[row_indices, col_indices] = not_finite_values ros = RandomOverSampler(random_state=0) X_res, y_res = ros.fit_resample(X_, Y) assert y_res.shape == (14,) assert X_res.shape == (14, 2) assert np.any(~np.isfinite(X_res)) def test_random_over_sampling_heterogeneous_data_smoothed_bootstrap(): # check that we raise an error when heterogeneous dtype data are given # and a smoothed bootstrap is requested X_hetero = np.array( [["xxx", 1, 1.0], ["yyy", 2, 2.0], ["zzz", 3, 3.0]], dtype=object ) y = np.array([0, 0, 1]) ros = RandomOverSampler(shrinkage=1, random_state=RND_SEED) err_msg = "When shrinkage is not None, X needs to contain only numerical" with pytest.raises(ValueError, match=err_msg): ros.fit_resample(X_hetero, y) @pytest.mark.parametrize("X_type", ["dataframe", "array", "sparse_csr", "sparse_csc"]) def test_random_over_sampler_smoothed_bootstrap(X_type, data): # check that smoothed bootstrap is working for numerical array X, y = data sampler = RandomOverSampler(shrinkage=1) X = _convert_container(X, X_type) X_res, y_res = sampler.fit_resample(X, y) assert y_res.shape == (14,) assert X_res.shape == (14, 2) if X_type == "dataframe": assert hasattr(X_res, "loc") def test_random_over_sampler_equivalence_shrinkage(data): # check that a shrinkage factor of 0 is equivalent to not create a smoothed # bootstrap X, y = data ros_not_shrink = RandomOverSampler(shrinkage=0, random_state=0) ros_hard_bootstrap = RandomOverSampler(shrinkage=None, random_state=0) X_res_not_shrink, y_res_not_shrink = ros_not_shrink.fit_resample(X, y) X_res, y_res = ros_hard_bootstrap.fit_resample(X, y) assert_allclose(X_res_not_shrink, X_res) assert_allclose(y_res_not_shrink, y_res) assert y_res.shape == (14,) assert X_res.shape == (14, 2) assert y_res_not_shrink.shape == (14,) assert X_res_not_shrink.shape == (14, 2) def test_random_over_sampler_shrinkage_behaviour(data): # check the behaviour of the shrinkage parameter # the covariance of the data generated with the larger shrinkage factor # should also be larger. X, y = data ros = RandomOverSampler(shrinkage=1, random_state=0) X_res_shink_1, y_res_shrink_1 = ros.fit_resample(X, y) ros.set_params(shrinkage=5) X_res_shink_5, y_res_shrink_5 = ros.fit_resample(X, y) disperstion_shrink_1 = np.linalg.det(np.cov(X_res_shink_1[y_res_shrink_1 == 0].T)) disperstion_shrink_5 = np.linalg.det(np.cov(X_res_shink_5[y_res_shrink_5 == 0].T)) assert disperstion_shrink_1 < disperstion_shrink_5 @pytest.mark.parametrize( "shrinkage, err_msg", [ ({}, "`shrinkage` should contain a shrinkage factor for each class"), ({0: -1}, "The shrinkage factor needs to be >= 0"), ], ) def test_random_over_sampler_shrinkage_error(data, shrinkage, err_msg): # check the validation of the shrinkage parameter X, y = data ros = RandomOverSampler(shrinkage=shrinkage) with pytest.raises(ValueError, match=err_msg): ros.fit_resample(X, y) @pytest.mark.parametrize( "sampling_strategy", ["auto", "minority", "not minority", "not majority", "all"] ) def test_random_over_sampler_strings(sampling_strategy): """Check that we support all supposed strings as `sampling_strategy` in a sampler inheriting from `BaseOverSampler`.""" X, y = make_classification( n_samples=100, n_clusters_per_class=1, n_classes=3, weights=[0.1, 0.3, 0.6], random_state=0, ) RandomOverSampler(sampling_strategy=sampling_strategy).fit_resample(X, y) def test_random_over_sampling_datetime(): """Check that we don't convert input data and only sample from it.""" pd = pytest.importorskip("pandas") X = pd.DataFrame({"label": [0, 0, 0, 1], "td": [datetime.now()] * 4}) y = X["label"] ros = RandomOverSampler(random_state=0) X_res, y_res = ros.fit_resample(X, y) pd.testing.assert_series_equal(X_res.dtypes, X.dtypes) pd.testing.assert_index_equal(X_res.index, y_res.index) assert_array_equal(y_res.to_numpy(), np.array([0, 0, 0, 1, 1, 1])) def test_random_over_sampler_full_nat(): """Check that we can return timedelta columns full of NaT. Non-regression test for: https://github.com/scikit-learn-contrib/imbalanced-learn/issues/1055 """ pd = pytest.importorskip("pandas") X = pd.DataFrame( { "col_str": ["abc", "def", "xyz"], "col_timedelta": pd.to_timedelta([np.nan, np.nan, np.nan]), } ) y = np.array([0, 0, 1]) X_res, y_res = RandomOverSampler().fit_resample(X, y) assert X_res.shape == (4, 2) assert y_res.shape == (4,) assert X_res["col_timedelta"].dtype.kind == "m" # timedelta scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/pipeline.py000066400000000000000000001624251521136113700251600ustar00rootroot00000000000000""" The :mod:`imblearn.pipeline` module implements utilities to build a composite estimator, as a chain of transforms, samples and estimators. """ # Adapted from scikit-learn # Author: Edouard Duchesnay # Gael Varoquaux # Virgile Fritsch # Alexandre Gramfort # Lars Buitinck # Christos Aridas # Guillaume Lemaitre # License: BSD import warnings from contextlib import contextmanager from copy import deepcopy from sklearn import pipeline from sklearn.base import clone from sklearn.exceptions import NotFittedError from sklearn.utils import Bunch from sklearn.utils._param_validation import HasMethods from sklearn.utils.fixes import parse_version from sklearn.utils.metadata_routing import ( MetadataRouter, MethodMapping, _routing_enabled, get_routing_for_object, ) from sklearn.utils.metaestimators import available_if from sklearn.utils.validation import check_is_fitted, check_memory from sklearn_compat._sklearn_compat import sklearn_version from sklearn_compat.base import _fit_context from sklearn_compat.utils._param_validation import validate_params from sklearn_compat.utils._user_interface import _print_elapsed_time from sklearn_compat.utils.metadata_routing import _raise_for_params, process_routing from imblearn.base import METHODS from imblearn.utils._tags import get_tags __all__ = ["Pipeline", "make_pipeline"] @contextmanager def _raise_or_warn_if_not_fitted(estimator): """A context manager to make sure a NotFittedError is raised, if a sub-estimator raises the error. Otherwise, we raise a warning if the pipeline is not fitted, with the deprecation. TODO(0.15): remove this context manager and replace with check_is_fitted. """ try: yield except NotFittedError as exc: raise NotFittedError("Pipeline is not fitted yet.") from exc # we only get here if the above didn't raise try: check_is_fitted(estimator) except NotFittedError: warnings.warn( ( "This Pipeline instance is not fitted yet. Call 'fit' with " "appropriate arguments before using other methods such as transform, " "predict, etc. This will raise an error in 0.15 instead of the current " "warning." ), FutureWarning, ) def _cached_transform( sub_pipeline, *, cache, param_name, param_value, transform_params ): """Transform a parameter value using a sub-pipeline and cache the result. Parameters ---------- sub_pipeline : Pipeline The sub-pipeline to be used for transformation. cache : dict The cache dictionary to store the transformed values. param_name : str The name of the parameter to be transformed. param_value : object The value of the parameter to be transformed. transform_params : dict The metadata to be used for transformation. This passed to the `transform` method of the sub-pipeline. Returns ------- transformed_value : object The transformed value of the parameter. """ if param_name not in cache: # If the parameter is a tuple, transform each element of the # tuple. This is needed to support the pattern present in # `lightgbm` and `xgboost` where users can pass multiple # validation sets. if isinstance(param_value, tuple): cache[param_name] = tuple( sub_pipeline.transform(element, **transform_params) for element in param_value ) else: cache[param_name] = sub_pipeline.transform(param_value, **transform_params) return cache[param_name] class Pipeline(pipeline.Pipeline): """Pipeline of transforms and resamples with a final estimator. Sequentially apply a list of transforms, sampling, and a final estimator. Intermediate steps of the pipeline must be transformers or resamplers, that is, they must implement fit, transform and sample methods. The samplers are only applied during fit. The final estimator only needs to implement fit. The transformers and samplers in the pipeline can be cached using ``memory`` argument. The purpose of the pipeline is to assemble several steps that can be cross-validated together while setting different parameters. For this, it enables setting parameters of the various steps using their names and the parameter name separated by a '__', as in the example below. A step's estimator may be replaced entirely by setting the parameter with its name to another estimator, or a transformer removed by setting it to 'passthrough' or ``None``. Parameters ---------- steps : list List of (name, transform) tuples (implementing fit/transform/fit_resample) that are chained, in the order in which they are chained, with the last object an estimator. transform_input : list of str, default=None The names of the :term:`metadata` parameters that should be transformed by the pipeline before passing it to the step consuming it. This enables transforming some input arguments to ``fit`` (other than ``X``) to be transformed by the steps of the pipeline up to the step which requires them. Requirement is defined via :ref:`metadata routing `. For instance, this can be used to pass a validation set through the pipeline. You can only set this if metadata routing is enabled, which you can enable using ``sklearn.set_config(enable_metadata_routing=True)``. .. versionadded:: 1.6 memory : Instance of joblib.Memory or str, default=None Used to cache the fitted transformers of the pipeline. By default, no caching is performed. If a string is given, it is the path to the caching directory. Enabling caching triggers a clone of the transformers before fitting. Therefore, the transformer instance given to the pipeline cannot be inspected directly. Use the attribute ``named_steps`` or ``steps`` to inspect estimators within the pipeline. Caching the transformers is advantageous when fitting is time consuming. verbose : bool, default=False If True, the time elapsed while fitting each step will be printed as it is completed. Attributes ---------- named_steps : :class:`~sklearn.utils.Bunch` Read-only attribute to access any step parameter by user given name. Keys are step names and values are steps parameters. classes_ : ndarray of shape (n_classes,) The classes labels. n_features_in_ : int Number of features seen during first step `fit` method. feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during :term:`fit`. Only defined if the underlying estimator exposes such an attribute when fit. See Also -------- make_pipeline : Helper function to make pipeline. Notes ----- See :ref:`sphx_glr_auto_examples_pipeline_plot_pipeline_classification.py` .. warning:: A surprising behaviour of the `imbalanced-learn` pipeline is that it breaks the `scikit-learn` contract where one expects `estimmator.fit_transform(X, y)` to be equivalent to `estimator.fit(X, y).transform(X)`. The semantic of `fit_resample` is to be applied only during the fit stage. Therefore, resampling will happen when calling `fit_transform` while it will only happen on the `fit` stage when calling `fit` and `transform` separately. Practically, `fit_transform` will lead to a resampled dataset while `fit` and `transform` will not. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from sklearn.model_selection import train_test_split as tts >>> from sklearn.decomposition import PCA >>> from sklearn.neighbors import KNeighborsClassifier as KNN >>> from sklearn.metrics import classification_report >>> from imblearn.over_sampling import SMOTE >>> from imblearn.pipeline import Pipeline >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print(f'Original dataset shape {Counter(y)}') Original dataset shape Counter({1: 900, 0: 100}) >>> pca = PCA() >>> smt = SMOTE(random_state=42) >>> knn = KNN() >>> pipeline = Pipeline([('smt', smt), ('pca', pca), ('knn', knn)]) >>> X_train, X_test, y_train, y_test = tts(X, y, random_state=42) >>> pipeline.fit(X_train, y_train) Pipeline(...) >>> y_hat = pipeline.predict(X_test) >>> print(classification_report(y_test, y_hat)) precision recall f1-score support 0 0.87 1.00 0.93 26 1 1.00 0.98 0.99 224 accuracy 0.98 250 macro avg 0.93 0.99 0.96 250 weighted avg 0.99 0.98 0.98 250 """ _parameter_constraints: dict = { "steps": "no_validation", # validated in `_validate_steps` "transform_input": [list, None], "memory": [None, str, HasMethods(["cache"])], "verbose": ["boolean"], } def __init__(self, steps, *, transform_input=None, memory=None, verbose=False): self.steps = steps self.transform_input = transform_input self.memory = memory self.verbose = verbose # BaseEstimator interface def _validate_steps(self): names, estimators = zip(*self.steps) # validate names self._validate_names(names) # validate estimators transformers = estimators[:-1] estimator = estimators[-1] for t in transformers: if t is None or t == "passthrough": continue is_transfomer = hasattr(t, "fit") and hasattr(t, "transform") is_sampler = hasattr(t, "fit_resample") is_not_transfomer_or_sampler = not (is_transfomer or is_sampler) if is_not_transfomer_or_sampler: raise TypeError( "All intermediate steps of the chain should " "be estimators that implement fit and transform or " "fit_resample (but not both) or be a string 'passthrough' " f"'{t}' (type {type(t)}) doesn't)" ) if is_transfomer and is_sampler: raise TypeError( "All intermediate steps of the chain should " "be estimators that implement fit and transform or " "fit_resample." f" '{t}' implements both)" ) if isinstance(t, pipeline.Pipeline): raise TypeError( "All intermediate steps of the chain should not be Pipelines" ) # We allow last estimator to be None as an identity transformation if ( estimator is not None and estimator != "passthrough" and not hasattr(estimator, "fit") ): raise TypeError( "Last step of Pipeline should implement fit or be the string" f" 'passthrough'. '{estimator}' (type {type(estimator)}) doesn't" ) def _iter(self, with_final=True, filter_passthrough=True, filter_resample=True): """Generate (idx, (name, trans)) tuples from self.steps. When `filter_passthrough` is `True`, 'passthrough' and None transformers are filtered out. When `filter_resample` is `True`, estimator with a method `fit_resample` are filtered out. """ it = super()._iter(with_final, filter_passthrough) if filter_resample: return filter(lambda x: not hasattr(x[-1], "fit_resample"), it) else: return it def _get_metadata_for_step(self, *, step_idx, step_params, all_params): """Get params (metadata) for step `name`. This transforms the metadata up to this step if required, which is indicated by the `transform_input` parameter. If a param in `step_params` is included in the `transform_input` list, it will be transformed. Parameters ---------- step_idx : int Index of the step in the pipeline. step_params : dict Parameters specific to the step. These are routed parameters, e.g. `routed_params[name]`. If a parameter name here is included in the `pipeline.transform_input`, then it will be transformed. Note that these parameters are *after* routing, so the aliases are already resolved. all_params : dict All parameters passed by the user. Here this is used to call `transform` on the slice of the pipeline itself. Returns ------- dict Parameters to be passed to the step. The ones which should be transformed are transformed. """ if ( self.transform_input is None or not all_params or not step_params or step_idx == 0 ): # we only need to process step_params if transform_input is set # and metadata is given by the user. return step_params sub_pipeline = self[:step_idx] sub_metadata_routing = get_routing_for_object(sub_pipeline) # here we get the metadata required by sub_pipeline.transform transform_params = { key: value for key, value in all_params.items() if key in sub_metadata_routing.consumes( method="transform", params=all_params.keys() ) } transformed_params = dict() # this is to be returned transformed_cache = dict() # used to transform each param once # `step_params` is the output of `process_routing`, so it has a dict for each # method (e.g. fit, transform, predict), which are the args to be passed to # those methods. We need to transform the parameters which are in the # `transform_input`, before returning these dicts. for method, method_params in step_params.items(): transformed_params[method] = Bunch() for param_name, param_value in method_params.items(): # An example of `(param_name, param_value)` is # `('sample_weight', array([0.5, 0.5, ...]))` if param_name in self.transform_input: # This parameter now needs to be transformed by the sub_pipeline, to # this step. We cache these computations to avoid repeating them. transformed_params[method][param_name] = _cached_transform( sub_pipeline, cache=transformed_cache, param_name=param_name, param_value=param_value, transform_params=transform_params, ) else: transformed_params[method][param_name] = param_value return transformed_params # Estimator interface # def _fit(self, X, y=None, **fit_params_steps): def _fit(self, X, y=None, routed_params=None, raw_params=None): self.steps = list(self.steps) self._validate_steps() # Setup the memory memory = check_memory(self.memory) fit_transform_one_cached = memory.cache(_fit_transform_one) fit_resample_one_cached = memory.cache(_fit_resample_one) for step_idx, name, transformer in self._iter( with_final=False, filter_passthrough=False, filter_resample=False ): if transformer is None or transformer == "passthrough": with _print_elapsed_time("Pipeline", self._log_message(step_idx)): continue if hasattr(memory, "location") and memory.location is None: # we do not clone when caching is disabled to # preserve backward compatibility cloned_transformer = transformer else: cloned_transformer = clone(transformer) # Fit or load from cache the current transformer step_params = self._get_metadata_for_step( step_idx=step_idx, step_params=routed_params[name], all_params=raw_params, ) if hasattr(cloned_transformer, "transform") or hasattr( cloned_transformer, "fit_transform" ): X, fitted_transformer = fit_transform_one_cached( cloned_transformer, X, y, weight=None, message_clsname="Pipeline", message=self._log_message(step_idx), params=step_params, ) elif hasattr(cloned_transformer, "fit_resample"): X, y, fitted_transformer = fit_resample_one_cached( cloned_transformer, X, y, message_clsname="Pipeline", message=self._log_message(step_idx), params=routed_params[name], ) # Replace the transformer of the step with the fitted # transformer. This is necessary when loading the transformer # from the cache. self.steps[step_idx] = (name, fitted_transformer) return X, y # The `fit_*` methods need to be overridden to support the samplers. @_fit_context( # estimators in Pipeline.steps are not validated yet prefer_skip_nested_validation=False ) def fit(self, X, y=None, **params): """Fit the model. Fit all the transforms/samplers one after the other and transform/sample the data, then fit the transformed/sampled data using the final estimator. Parameters ---------- X : iterable Training data. Must fulfill input requirements of first step of the pipeline. y : iterable, default=None Training targets. Must fulfill label requirements for all steps of the pipeline. **params : dict of str -> object - If `enable_metadata_routing=False` (default): Parameters passed to the ``fit`` method of each step, where each parameter name is prefixed such that parameter ``p`` for step ``s`` has key ``s__p``. - If `enable_metadata_routing=True`: Parameters requested and accepted by steps. Each step must have requested certain metadata for these parameters to be forwarded to them. .. versionchanged:: 1.4 Parameters are now passed to the ``transform`` method of the intermediate steps as well, if requested, and if `enable_metadata_routing=True` is set via :func:`~sklearn.set_config`. See :ref:`Metadata Routing User Guide ` for more details. Returns ------- self : Pipeline This estimator. """ if not _routing_enabled() and self.transform_input is not None: raise ValueError( "The `transform_input` parameter can only be set if metadata " "routing is enabled. You can enable metadata routing using " "`sklearn.set_config(enable_metadata_routing=True)`." ) if sklearn_version < parse_version("1.4") and self.transform_input is not None: raise ValueError( "The `transform_input` parameter is not supported in scikit-learn " "versions prior to 1.4. Please upgrade to scikit-learn 1.4 or " "later." ) routed_params = self._check_method_params(method="fit", props=params) Xt, yt = self._fit(X, y, routed_params, raw_params=params) with _print_elapsed_time("Pipeline", self._log_message(len(self.steps) - 1)): if self._final_estimator != "passthrough": last_step_params = self._get_metadata_for_step( step_idx=len(self) - 1, step_params=routed_params[self.steps[-1][0]], all_params=params, ) self._final_estimator.fit(Xt, yt, **last_step_params["fit"]) return self def _can_fit_transform(self): return ( self._final_estimator == "passthrough" or hasattr(self._final_estimator, "transform") or hasattr(self._final_estimator, "fit_transform") ) @available_if(_can_fit_transform) @_fit_context( # estimators in Pipeline.steps are not validated yet prefer_skip_nested_validation=False ) def fit_transform(self, X, y=None, **params): """Fit the model and transform with the final estimator. Fits all the transformers/samplers one after the other and transform/sample the data, then uses fit_transform on transformed data with the final estimator. Parameters ---------- X : iterable Training data. Must fulfill input requirements of first step of the pipeline. y : iterable, default=None Training targets. Must fulfill label requirements for all steps of the pipeline. **params : dict of str -> object - If `enable_metadata_routing=False` (default): Parameters passed to the ``fit`` method of each step, where each parameter name is prefixed such that parameter ``p`` for step ``s`` has key ``s__p``. - If `enable_metadata_routing=True`: Parameters requested and accepted by steps. Each step must have requested certain metadata for these parameters to be forwarded to them. .. versionchanged:: 1.4 Parameters are now passed to the ``transform`` method of the intermediate steps as well, if requested, and if `enable_metadata_routing=True`. See :ref:`Metadata Routing User Guide ` for more details. Returns ------- Xt : array-like of shape (n_samples, n_transformed_features) Transformed samples. """ routed_params = self._check_method_params(method="fit_transform", props=params) Xt, yt = self._fit(X, y, routed_params) last_step = self._final_estimator with _print_elapsed_time("Pipeline", self._log_message(len(self.steps) - 1)): if last_step == "passthrough": return Xt last_step_params = self._get_metadata_for_step( step_idx=len(self) - 1, step_params=routed_params[self.steps[-1][0]], all_params=params, ) if hasattr(last_step, "fit_transform"): return last_step.fit_transform( Xt, yt, **last_step_params["fit_transform"] ) else: return last_step.fit(Xt, y, **last_step_params["fit"]).transform( Xt, **last_step_params["transform"] ) @available_if(pipeline._final_estimator_has("predict")) def predict(self, X, **params): """Transform the data, and apply `predict` with the final estimator. Call `transform` of each transformer in the pipeline. The transformed data are finally passed to the final estimator that calls `predict` method. Only valid if the final estimator implements `predict`. Parameters ---------- X : iterable Data to predict on. Must fulfill input requirements of first step of the pipeline. **params : dict of str -> object - If `enable_metadata_routing=False` (default): Parameters to the ``predict`` called at the end of all transformations in the pipeline. - If `enable_metadata_routing=True`: Parameters requested and accepted by steps. Each step must have requested certain metadata for these parameters to be forwarded to them. .. versionadded:: 0.20 .. versionchanged:: 1.4 Parameters are now passed to the ``transform`` method of the intermediate steps as well, if requested, and if `enable_metadata_routing=True` is set via :func:`~sklearn.set_config`. See :ref:`Metadata Routing User Guide ` for more details. Note that while this may be used to return uncertainties from some models with ``return_std`` or ``return_cov``, uncertainties that are generated by the transformations in the pipeline are not propagated to the final estimator. Returns ------- y_pred : ndarray Result of calling `predict` on the final estimator. """ # TODO(0.15): Remove the context manager and use check_is_fitted(self) with _raise_or_warn_if_not_fitted(self): Xt = X if not _routing_enabled(): for _, name, transform in self._iter(with_final=False): Xt = transform.transform(Xt) return self.steps[-1][1].predict(Xt, **params) # metadata routing enabled routed_params = process_routing(self, "predict", **params) for _, name, transform in self._iter(with_final=False): Xt = transform.transform(Xt, **routed_params[name].transform) return self.steps[-1][1].predict( Xt, **routed_params[self.steps[-1][0]].predict ) def _can_fit_resample(self): return self._final_estimator == "passthrough" or hasattr( self._final_estimator, "fit_resample" ) @available_if(_can_fit_resample) @_fit_context( # estimators in Pipeline.steps are not validated yet prefer_skip_nested_validation=False ) def fit_resample(self, X, y=None, **params): """Fit the model and sample with the final estimator. Fits all the transformers/samplers one after the other and transform/sample the data, then uses fit_resample on transformed data with the final estimator. Parameters ---------- X : iterable Training data. Must fulfill input requirements of first step of the pipeline. y : iterable, default=None Training targets. Must fulfill label requirements for all steps of the pipeline. **params : dict of str -> object - If `enable_metadata_routing=False` (default): Parameters passed to the ``fit`` method of each step, where each parameter name is prefixed such that parameter ``p`` for step ``s`` has key ``s__p``. - If `enable_metadata_routing=True`: Parameters requested and accepted by steps. Each step must have requested certain metadata for these parameters to be forwarded to them. .. versionchanged:: 1.4 Parameters are now passed to the ``transform`` method of the intermediate steps as well, if requested, and if `enable_metadata_routing=True`. See :ref:`Metadata Routing User Guide ` for more details. Returns ------- Xt : array-like of shape (n_samples, n_transformed_features) Transformed samples. yt : array-like of shape (n_samples, n_transformed_features) Transformed target. """ routed_params = self._check_method_params(method="fit_resample", props=params) Xt, yt = self._fit(X, y, routed_params) last_step = self._final_estimator with _print_elapsed_time("Pipeline", self._log_message(len(self.steps) - 1)): if last_step == "passthrough": return Xt last_step_params = routed_params[self.steps[-1][0]] if hasattr(last_step, "fit_resample"): return last_step.fit_resample( Xt, yt, **last_step_params["fit_resample"] ) @available_if(pipeline._final_estimator_has("fit_predict")) @_fit_context( # estimators in Pipeline.steps are not validated yet prefer_skip_nested_validation=False ) def fit_predict(self, X, y=None, **params): """Apply `fit_predict` of last step in pipeline after transforms. Applies fit_transforms of a pipeline to the data, followed by the fit_predict method of the final estimator in the pipeline. Valid only if the final estimator implements fit_predict. Parameters ---------- X : iterable Training data. Must fulfill input requirements of first step of the pipeline. y : iterable, default=None Training targets. Must fulfill label requirements for all steps of the pipeline. **params : dict of str -> object - If `enable_metadata_routing=False` (default): Parameters to the ``predict`` called at the end of all transformations in the pipeline. - If `enable_metadata_routing=True`: Parameters requested and accepted by steps. Each step must have requested certain metadata for these parameters to be forwarded to them. .. versionadded:: 0.20 .. versionchanged:: 1.4 Parameters are now passed to the ``transform`` method of the intermediate steps as well, if requested, and if `enable_metadata_routing=True`. See :ref:`Metadata Routing User Guide ` for more details. Note that while this may be used to return uncertainties from some models with ``return_std`` or ``return_cov``, uncertainties that are generated by the transformations in the pipeline are not propagated to the final estimator. Returns ------- y_pred : ndarray of shape (n_samples,) The predicted target. """ routed_params = self._check_method_params(method="fit_predict", props=params) Xt, yt = self._fit(X, y, routed_params) params_last_step = routed_params[self.steps[-1][0]] with _print_elapsed_time("Pipeline", self._log_message(len(self.steps) - 1)): y_pred = self.steps[-1][-1].fit_predict( Xt, yt, **params_last_step.get("fit_predict", {}) ) return y_pred # TODO: remove the following methods when the minimum scikit-learn >= 1.4 # They do not depend on resampling but we need to redefine them for the # compatibility with the metadata routing framework. @available_if(pipeline._final_estimator_has("predict_proba")) def predict_proba(self, X, **params): """Transform the data, and apply `predict_proba` with the final estimator. Call `transform` of each transformer in the pipeline. The transformed data are finally passed to the final estimator that calls `predict_proba` method. Only valid if the final estimator implements `predict_proba`. Parameters ---------- X : iterable Data to predict on. Must fulfill input requirements of first step of the pipeline. **params : dict of str -> object - If `enable_metadata_routing=False` (default): Parameters to the `predict_proba` called at the end of all transformations in the pipeline. - If `enable_metadata_routing=True`: Parameters requested and accepted by steps. Each step must have requested certain metadata for these parameters to be forwarded to them. .. versionadded:: 0.20 .. versionchanged:: 1.4 Parameters are now passed to the ``transform`` method of the intermediate steps as well, if requested, and if `enable_metadata_routing=True`. See :ref:`Metadata Routing User Guide ` for more details. Returns ------- y_proba : ndarray of shape (n_samples, n_classes) Result of calling `predict_proba` on the final estimator. """ # TODO(0.15): Remove the context manager and use check_is_fitted(self) with _raise_or_warn_if_not_fitted(self): Xt = X if not _routing_enabled(): for _, name, transform in self._iter(with_final=False): Xt = transform.transform(Xt) return self.steps[-1][1].predict_proba(Xt, **params) # metadata routing enabled routed_params = process_routing(self, "predict_proba", **params) for _, name, transform in self._iter(with_final=False): Xt = transform.transform(Xt, **routed_params[name].transform) return self.steps[-1][1].predict_proba( Xt, **routed_params[self.steps[-1][0]].predict_proba ) @available_if(pipeline._final_estimator_has("decision_function")) def decision_function(self, X, **params): """Transform the data, and apply `decision_function` with the final estimator. Call `transform` of each transformer in the pipeline. The transformed data are finally passed to the final estimator that calls `decision_function` method. Only valid if the final estimator implements `decision_function`. Parameters ---------- X : iterable Data to predict on. Must fulfill input requirements of first step of the pipeline. **params : dict of string -> object Parameters requested and accepted by steps. Each step must have requested certain metadata for these parameters to be forwarded to them. .. versionadded:: 1.4 Only available if `enable_metadata_routing=True`. See :ref:`Metadata Routing User Guide ` for more details. Returns ------- y_score : ndarray of shape (n_samples, n_classes) Result of calling `decision_function` on the final estimator. """ # TODO(0.15): Remove the context manager and use check_is_fitted(self) with _raise_or_warn_if_not_fitted(self): _raise_for_params(params, self, "decision_function") # not branching here since params is only available if # enable_metadata_routing=True routed_params = process_routing(self, "decision_function", **params) Xt = X for _, name, transform in self._iter(with_final=False): Xt = transform.transform( Xt, **routed_params.get(name, {}).get("transform", {}) ) return self.steps[-1][1].decision_function( Xt, **routed_params.get(self.steps[-1][0], {}).get("decision_function", {}), ) @available_if(pipeline._final_estimator_has("score_samples")) def score_samples(self, X): """Transform the data, and apply `score_samples` with the final estimator. Call `transform` of each transformer in the pipeline. The transformed data are finally passed to the final estimator that calls `score_samples` method. Only valid if the final estimator implements `score_samples`. Parameters ---------- X : iterable Data to predict on. Must fulfill input requirements of first step of the pipeline. Returns ------- y_score : ndarray of shape (n_samples,) Result of calling `score_samples` on the final estimator. """ # TODO(0.15): Remove the context manager and use check_is_fitted(self) with _raise_or_warn_if_not_fitted(self): Xt = X for _, _, transformer in self._iter(with_final=False): Xt = transformer.transform(Xt) return self.steps[-1][1].score_samples(Xt) @available_if(pipeline._final_estimator_has("predict_log_proba")) def predict_log_proba(self, X, **params): """Transform the data, and apply `predict_log_proba` with the final estimator. Call `transform` of each transformer in the pipeline. The transformed data are finally passed to the final estimator that calls `predict_log_proba` method. Only valid if the final estimator implements `predict_log_proba`. Parameters ---------- X : iterable Data to predict on. Must fulfill input requirements of first step of the pipeline. **params : dict of str -> object - If `enable_metadata_routing=False` (default): Parameters to the `predict_log_proba` called at the end of all transformations in the pipeline. - If `enable_metadata_routing=True`: Parameters requested and accepted by steps. Each step must have requested certain metadata for these parameters to be forwarded to them. .. versionadded:: 0.20 .. versionchanged:: 1.4 Parameters are now passed to the ``transform`` method of the intermediate steps as well, if requested, and if `enable_metadata_routing=True`. See :ref:`Metadata Routing User Guide ` for more details. Returns ------- y_log_proba : ndarray of shape (n_samples, n_classes) Result of calling `predict_log_proba` on the final estimator. """ # TODO(0.15): Remove the context manager and use check_is_fitted(self) with _raise_or_warn_if_not_fitted(self): Xt = X if not _routing_enabled(): for _, name, transform in self._iter(with_final=False): Xt = transform.transform(Xt) return self.steps[-1][1].predict_log_proba(Xt, **params) # metadata routing enabled routed_params = process_routing(self, "predict_log_proba", **params) for _, name, transform in self._iter(with_final=False): Xt = transform.transform(Xt, **routed_params[name].transform) return self.steps[-1][1].predict_log_proba( Xt, **routed_params[self.steps[-1][0]].predict_log_proba ) def _can_transform(self): return self._final_estimator == "passthrough" or hasattr( self._final_estimator, "transform" ) @available_if(_can_transform) def transform(self, X, **params): """Transform the data, and apply `transform` with the final estimator. Call `transform` of each transformer in the pipeline. The transformed data are finally passed to the final estimator that calls `transform` method. Only valid if the final estimator implements `transform`. This also works where final estimator is `None` in which case all prior transformations are applied. Parameters ---------- X : iterable Data to transform. Must fulfill input requirements of first step of the pipeline. **params : dict of str -> object Parameters requested and accepted by steps. Each step must have requested certain metadata for these parameters to be forwarded to them. .. versionadded:: 1.4 Only available if `enable_metadata_routing=True`. See :ref:`Metadata Routing User Guide ` for more details. Returns ------- Xt : ndarray of shape (n_samples, n_transformed_features) Transformed data. """ # TODO(0.15): Remove the context manager and use check_is_fitted(self) with _raise_or_warn_if_not_fitted(self): _raise_for_params(params, self, "transform") # not branching here since params is only available if # enable_metadata_routing=True routed_params = process_routing(self, "transform", **params) Xt = X for _, name, transform in self._iter(): Xt = transform.transform(Xt, **routed_params[name].transform) return Xt def _can_inverse_transform(self): return all(hasattr(t, "inverse_transform") for _, _, t in self._iter()) @available_if(_can_inverse_transform) def inverse_transform(self, Xt, **params): """Apply `inverse_transform` for each step in a reverse order. All estimators in the pipeline must support `inverse_transform`. Parameters ---------- Xt : array-like of shape (n_samples, n_transformed_features) Data samples, where ``n_samples`` is the number of samples and ``n_features`` is the number of features. Must fulfill input requirements of last step of pipeline's ``inverse_transform`` method. **params : dict of str -> object Parameters requested and accepted by steps. Each step must have requested certain metadata for these parameters to be forwarded to them. .. versionadded:: 1.4 Only available if `enable_metadata_routing=True`. See :ref:`Metadata Routing User Guide ` for more details. Returns ------- Xt : ndarray of shape (n_samples, n_features) Inverse transformed data, that is, data in the original feature space. """ # TODO(0.15): Remove the context manager and use check_is_fitted(self) with _raise_or_warn_if_not_fitted(self): _raise_for_params(params, self, "inverse_transform") # we don't have to branch here, since params is only non-empty if # enable_metadata_routing=True. routed_params = process_routing(self, "inverse_transform", **params) reverse_iter = reversed(list(self._iter())) for _, name, transform in reverse_iter: Xt = transform.inverse_transform( Xt, **routed_params[name].inverse_transform ) return Xt @available_if(pipeline._final_estimator_has("score")) def score(self, X, y=None, sample_weight=None, **params): """Transform the data, and apply `score` with the final estimator. Call `transform` of each transformer in the pipeline. The transformed data are finally passed to the final estimator that calls `score` method. Only valid if the final estimator implements `score`. Parameters ---------- X : iterable Data to predict on. Must fulfill input requirements of first step of the pipeline. y : iterable, default=None Targets used for scoring. Must fulfill label requirements for all steps of the pipeline. sample_weight : array-like, default=None If not None, this argument is passed as ``sample_weight`` keyword argument to the ``score`` method of the final estimator. **params : dict of str -> object Parameters requested and accepted by steps. Each step must have requested certain metadata for these parameters to be forwarded to them. .. versionadded:: 1.4 Only available if `enable_metadata_routing=True`. See :ref:`Metadata Routing User Guide ` for more details. Returns ------- score : float Result of calling `score` on the final estimator. """ # TODO(0.15): Remove the context manager and use check_is_fitted(self) with _raise_or_warn_if_not_fitted(self): Xt = X if not _routing_enabled(): for _, name, transform in self._iter(with_final=False): Xt = transform.transform(Xt) score_params = {} if sample_weight is not None: score_params["sample_weight"] = sample_weight return self.steps[-1][1].score(Xt, y, **score_params) # metadata routing is enabled. routed_params = process_routing( self, "score", sample_weight=sample_weight, **params ) Xt = X for _, name, transform in self._iter(with_final=False): Xt = transform.transform(Xt, **routed_params[name].transform) return self.steps[-1][1].score( Xt, y, **routed_params[self.steps[-1][0]].score ) # TODO: once scikit-learn >= 1.4, the following function should be simplified by # calling `super().get_metadata_routing()` def get_metadata_routing(self): """Get metadata routing of this object. Please check :ref:`User Guide ` on how the routing mechanism works. Returns ------- routing : MetadataRouter A :class:`~utils.metadata_routing.MetadataRouter` encapsulating routing information. """ router = MetadataRouter(owner=self.__class__.__name__) # first we add all steps except the last one for _, name, trans in self._iter( with_final=False, filter_passthrough=True, filter_resample=False ): method_mapping = MethodMapping() # fit, fit_predict, and fit_transform call fit_transform if it # exists, or else fit and transform if hasattr(trans, "fit_transform"): ( method_mapping.add(caller="fit", callee="fit_transform") .add(caller="fit_transform", callee="fit_transform") .add(caller="fit_predict", callee="fit_transform") ) else: ( method_mapping.add(caller="fit", callee="fit") .add(caller="fit", callee="transform") .add(caller="fit_transform", callee="fit") .add(caller="fit_transform", callee="transform") .add(caller="fit_predict", callee="fit") .add(caller="fit_predict", callee="transform") ) ( # handling sampler if the fit_* stage method_mapping.add(caller="fit", callee="fit_resample") .add(caller="fit_transform", callee="fit_resample") .add(caller="fit_predict", callee="fit_resample") ) ( method_mapping.add(caller="predict", callee="transform") .add(caller="predict", callee="transform") .add(caller="predict_proba", callee="transform") .add(caller="decision_function", callee="transform") .add(caller="predict_log_proba", callee="transform") .add(caller="transform", callee="transform") .add(caller="inverse_transform", callee="inverse_transform") .add(caller="score", callee="transform") .add(caller="fit_resample", callee="transform") ) router.add(method_mapping=method_mapping, **{name: trans}) final_name, final_est = self.steps[-1] if final_est is None or final_est == "passthrough": return router # then we add the last step method_mapping = MethodMapping() if hasattr(final_est, "fit_transform"): method_mapping.add(caller="fit_transform", callee="fit_transform") else: ( method_mapping.add(caller="fit", callee="fit").add( caller="fit", callee="transform" ) ) ( method_mapping.add(caller="fit", callee="fit") .add(caller="predict", callee="predict") .add(caller="fit_predict", callee="fit_predict") .add(caller="predict_proba", callee="predict_proba") .add(caller="decision_function", callee="decision_function") .add(caller="predict_log_proba", callee="predict_log_proba") .add(caller="transform", callee="transform") .add(caller="inverse_transform", callee="inverse_transform") .add(caller="score", callee="score") .add(caller="fit_resample", callee="fit_resample") ) router.add(method_mapping=method_mapping, **{final_name: final_est}) return router def _check_method_params(self, method, props, **kwargs): if _routing_enabled(): routed_params = process_routing(self, method, **props, **kwargs) return routed_params else: fit_params_steps = Bunch( **{ name: Bunch(**{method: {} for method in METHODS}) for name, step in self.steps if step is not None } ) for pname, pval in props.items(): if "__" not in pname: raise ValueError( f"Pipeline.fit does not accept the {pname} parameter. " "You can pass parameters to specific steps of your " "pipeline using the stepname__parameter format, e.g. " "`Pipeline.fit(X, y, logisticregression__sample_weight" "=sample_weight)`." ) step, param = pname.split("__", 1) fit_params_steps[step]["fit"][param] = pval # without metadata routing, fit_transform and fit_predict # get all the same params and pass it to the last fit. fit_params_steps[step]["fit_transform"][param] = pval fit_params_steps[step]["fit_predict"][param] = pval return fit_params_steps def __sklearn_is_fitted__(self): """Indicate whether pipeline has been fit. This is done by checking whether the last non-`passthrough` step of the pipeline is fitted. An empty pipeline is considered fitted. """ # First find the last step that is not 'passthrough' last_step = None for _, estimator in reversed(self.steps): if estimator != "passthrough": last_step = estimator break if last_step is None: # All steps are 'passthrough', so the pipeline is considered fitted return True try: # check if the last step of the pipeline is fitted # we only check the last step since if the last step is fit, it # means the previous steps should also be fit. This is faster than # checking if every step of the pipeline is fit. check_is_fitted(last_step) return True except NotFittedError: return False def __sklearn_tags__(self): tags = super().__sklearn_tags__() if not self.steps: return tags try: if self.steps[0][1] is not None and self.steps[0][1] != "passthrough": tags.input_tags.pairwise = get_tags( self.steps[0][1] ).input_tags.pairwise except (ValueError, AttributeError, TypeError): # This happens when the `steps` is not a list of (name, estimator) # tuples and `fit` is not called yet to validate the steps. pass try: if self.steps[-1][1] is not None and self.steps[-1][1] != "passthrough": last_step_tags = get_tags(self.steps[-1][1]) tags.estimator_type = last_step_tags.estimator_type tags.target_tags.multi_output = last_step_tags.target_tags.multi_output tags.classifier_tags = deepcopy(last_step_tags.classifier_tags) tags.regressor_tags = deepcopy(last_step_tags.regressor_tags) tags.transformer_tags = deepcopy(last_step_tags.transformer_tags) except (ValueError, AttributeError, TypeError): # This happens when the `steps` is not a list of (name, estimator) # tuples and `fit` is not called yet to validate the steps. pass return tags def _fit_resample_one(sampler, X, y, message_clsname="", message=None, params=None): with _print_elapsed_time(message_clsname, message): X_res, y_res = sampler.fit_resample(X, y, **params.get("fit_resample", {})) return X_res, y_res, sampler def _transform_one(transformer, X, y, weight, params=None): """Call transform and apply weight to output. Parameters ---------- transformer : estimator Estimator to be used for transformation. X : {array-like, sparse matrix} of shape (n_samples, n_features) Input data to be transformed. y : ndarray of shape (n_samples,) Ignored. weight : float Weight to be applied to the output of the transformation. params : dict Parameters to be passed to the transformer's ``transform`` method. This should be of the form ``process_routing()["step_name"]``. """ res = transformer.transform(X, **params.transform) # if we have a weight for this transformer, multiply output if weight is None: return res return res * weight def _fit_transform_one( transformer, X, y, weight, message_clsname="", message=None, params=None ): """ Fits ``transformer`` to ``X`` and ``y``. The transformed result is returned with the fitted transformer. If ``weight`` is not ``None``, the result will be multiplied by ``weight``. ``params`` needs to be of the form ``process_routing()["step_name"]``. """ params = params or {} with _print_elapsed_time(message_clsname, message): if hasattr(transformer, "fit_transform"): res = transformer.fit_transform(X, y, **params.get("fit_transform", {})) else: res = transformer.fit(X, y, **params.get("fit", {})).transform( X, **params.get("transform", {}) ) if weight is None: return res, transformer return res * weight, transformer @validate_params( { "memory": [None, str, HasMethods(["cache"])], "transform_input": [None, list], "verbose": ["boolean"], }, prefer_skip_nested_validation=True, ) def make_pipeline(*steps, memory=None, transform_input=None, verbose=False): """Construct a Pipeline from the given estimators. This is a shorthand for the Pipeline constructor; it does not require, and does not permit, naming the estimators. Instead, their names will be set to the lowercase of their types automatically. Parameters ---------- *steps : list of estimators A list of estimators. memory : None, str or object with the joblib.Memory interface, default=None Used to cache the fitted transformers of the pipeline. By default, no caching is performed. If a string is given, it is the path to the caching directory. Enabling caching triggers a clone of the transformers before fitting. Therefore, the transformer instance given to the pipeline cannot be inspected directly. Use the attribute ``named_steps`` or ``steps`` to inspect estimators within the pipeline. Caching the transformers is advantageous when fitting is time consuming. transform_input : list of str, default=None This enables transforming some input arguments to ``fit`` (other than ``X``) to be transformed by the steps of the pipeline up to the step which requires them. Requirement is defined via :ref:`metadata routing `. This can be used to pass a validation set through the pipeline for instance. You can only set this if metadata routing is enabled, which you can enable using ``sklearn.set_config(enable_metadata_routing=True)``. .. versionadded:: 1.6 verbose : bool, default=False If True, the time elapsed while fitting each step will be printed as it is completed. Returns ------- p : Pipeline Returns an imbalanced-learn `Pipeline` instance that handles samplers. See Also -------- imblearn.pipeline.Pipeline : Class for creating a pipeline of transforms with a final estimator. Examples -------- >>> from sklearn.naive_bayes import GaussianNB >>> from sklearn.preprocessing import StandardScaler >>> make_pipeline(StandardScaler(), GaussianNB(priors=None)) Pipeline(steps=[('standardscaler', StandardScaler()), ('gaussiannb', GaussianNB())]) """ return Pipeline( pipeline._name_estimators(steps), memory=memory, transform_input=transform_input, verbose=verbose, ) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/tensorflow/000077500000000000000000000000001521136113700251715ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/tensorflow/__init__.py000066400000000000000000000003241521136113700273010ustar00rootroot00000000000000"""The :mod:`imblearn.tensorflow` provides utilities to deal with imbalanced dataset in tensorflow.""" from imblearn.tensorflow._generator import balanced_batch_generator __all__ = ["balanced_batch_generator"] scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/tensorflow/_generator.py000066400000000000000000000063631521136113700277000ustar00rootroot00000000000000"""Implement generators for ``tensorflow`` which will balance the data.""" from scipy.sparse import issparse from sklearn.base import clone from sklearn.utils import _safe_indexing, check_random_state from imblearn.under_sampling import RandomUnderSampler from imblearn.utils import Substitution from imblearn.utils._docstring import _random_state_docstring @Substitution(random_state=_random_state_docstring) def balanced_batch_generator( X, y, *, sample_weight=None, sampler=None, batch_size=32, keep_sparse=False, random_state=None, ): """Create a balanced batch generator to train tensorflow model. Returns a generator --- as well as the number of step per epoch --- to iterate to get the mini-batches. The sampler defines the sampling strategy used to balance the dataset ahead of creating the batch. The sampler should have an attribute ``sample_indices_``. .. versionadded:: 0.4 Parameters ---------- X : ndarray of shape (n_samples, n_features) Original imbalanced dataset. y : ndarray of shape (n_samples,) or (n_samples, n_classes) Associated targets. sample_weight : ndarray of shape (n_samples,), default=None Sample weight. sampler : sampler object, default=None A sampler instance which has an attribute ``sample_indices_``. By default, the sampler used is a :class:`~imblearn.under_sampling.RandomUnderSampler`. batch_size : int, default=32 Number of samples per gradient update. keep_sparse : bool, default=False Either or not to conserve or not the sparsity of the input ``X``. By default, the returned batches will be dense. {random_state} Returns ------- generator : generator of tuple Generate batch of data. The tuple generated are either (X_batch, y_batch) or (X_batch, y_batch, sampler_weight_batch). steps_per_epoch : int The number of samples per epoch. """ random_state = check_random_state(random_state) if sampler is None: sampler_ = RandomUnderSampler(random_state=random_state) else: sampler_ = clone(sampler) sampler_.fit_resample(X, y) if not hasattr(sampler_, "sample_indices_"): raise ValueError("'sampler' needs to have an attribute 'sample_indices_'.") indices = sampler_.sample_indices_ # shuffle the indices since the sampler are packing them by class random_state.shuffle(indices) def generator(X, y, sample_weight, indices, batch_size): while True: for index in range(0, len(indices), batch_size): X_res = _safe_indexing(X, indices[index : index + batch_size]) y_res = _safe_indexing(y, indices[index : index + batch_size]) if issparse(X_res) and not keep_sparse: X_res = X_res.toarray() if sample_weight is None: yield X_res, y_res else: sw_res = _safe_indexing( sample_weight, indices[index : index + batch_size] ) yield X_res, y_res, sw_res return ( generator(X, y, sample_weight, indices, batch_size), int(indices.size // batch_size), ) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/tensorflow/tests/000077500000000000000000000000001521136113700263335ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/tensorflow/tests/__init__.py000066400000000000000000000000001521136113700304320ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/tensorflow/tests/test_generator.py000066400000000000000000000124641521136113700317410ustar00rootroot00000000000000import numpy as np import pytest from scipy import sparse from sklearn.datasets import load_iris from sklearn.utils.fixes import parse_version from imblearn.datasets import make_imbalance from imblearn.over_sampling import RandomOverSampler from imblearn.tensorflow import balanced_batch_generator from imblearn.under_sampling import NearMiss tf = pytest.importorskip("tensorflow") @pytest.fixture def data(): X, y = load_iris(return_X_y=True) X, y = make_imbalance(X, y, sampling_strategy={0: 30, 1: 50, 2: 40}) X = X.astype(np.float32) return X, y def check_balanced_batch_generator_tf_1_X_X(dataset, sampler): X, y = dataset batch_size = 10 training_generator, steps_per_epoch = balanced_batch_generator( X, y, sample_weight=None, sampler=sampler, batch_size=batch_size, random_state=42, ) learning_rate = 0.01 epochs = 10 input_size = X.shape[1] output_size = 3 # helper functions def init_weights(shape): return tf.Variable(tf.random_normal(shape, stddev=0.01)) def accuracy(y_true, y_pred): return np.mean(np.argmax(y_pred, axis=1) == y_true) # input and output data = tf.placeholder("float32", shape=[None, input_size]) targets = tf.placeholder("int32", shape=[None]) # build the model and weights W = init_weights([input_size, output_size]) b = init_weights([output_size]) out_act = tf.nn.sigmoid(tf.matmul(data, W) + b) # build the loss, predict, and train operator cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits( logits=out_act, labels=targets ) loss = tf.reduce_sum(cross_entropy) optimizer = tf.train.GradientDescentOptimizer(learning_rate) train_op = optimizer.minimize(loss) predict = tf.nn.softmax(out_act) # Initialization of all variables in the graph init = tf.global_variables_initializer() with tf.Session() as sess: sess.run(init) for e in range(epochs): for i in range(steps_per_epoch): X_batch, y_batch = next(training_generator) sess.run( [train_op, loss], feed_dict={data: X_batch, targets: y_batch}, ) # For each epoch, run accuracy on train and test predicts_train = sess.run(predict, feed_dict={data: X}) print(f"epoch: {e} train accuracy: {accuracy(y, predicts_train):.3f}") def check_balanced_batch_generator_tf_2_X_X_compat_1_X_X(dataset, sampler): tf.compat.v1.disable_eager_execution() X, y = dataset batch_size = 10 training_generator, steps_per_epoch = balanced_batch_generator( X, y, sample_weight=None, sampler=sampler, batch_size=batch_size, random_state=42, ) learning_rate = 0.01 epochs = 10 input_size = X.shape[1] output_size = 3 # helper functions def init_weights(shape): return tf.Variable(tf.random.normal(shape, stddev=0.01)) def accuracy(y_true, y_pred): return np.mean(np.argmax(y_pred, axis=1) == y_true) # input and output data = tf.compat.v1.placeholder("float32", shape=[None, input_size]) targets = tf.compat.v1.placeholder("int32", shape=[None]) # build the model and weights W = init_weights([input_size, output_size]) b = init_weights([output_size]) out_act = tf.nn.sigmoid(tf.matmul(data, W) + b) # build the loss, predict, and train operator cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits( logits=out_act, labels=targets ) loss = tf.reduce_sum(input_tensor=cross_entropy) optimizer = tf.compat.v1.train.GradientDescentOptimizer(learning_rate) train_op = optimizer.minimize(loss) predict = tf.nn.softmax(out_act) # Initialization of all variables in the graph init = tf.compat.v1.global_variables_initializer() with tf.compat.v1.Session() as sess: sess.run(init) for e in range(epochs): for i in range(steps_per_epoch): X_batch, y_batch = next(training_generator) sess.run( [train_op, loss], feed_dict={data: X_batch, targets: y_batch}, ) # For each epoch, run accuracy on train and test predicts_train = sess.run(predict, feed_dict={data: X}) print(f"epoch: {e} train accuracy: {accuracy(y, predicts_train):.3f}") @pytest.mark.parametrize("sampler", [None, NearMiss(), RandomOverSampler()]) def test_balanced_batch_generator(data, sampler): if parse_version(tf.__version__) < parse_version("2.0.0"): check_balanced_batch_generator_tf_1_X_X(data, sampler) else: check_balanced_batch_generator_tf_2_X_X_compat_1_X_X(data, sampler) @pytest.mark.parametrize("keep_sparse", [True, False]) def test_balanced_batch_generator_function_sparse(data, keep_sparse): X, y = data training_generator, steps_per_epoch = balanced_batch_generator( sparse.csr_matrix(X), y, keep_sparse=keep_sparse, batch_size=10, random_state=42, ) for idx in range(steps_per_epoch): X_batch, y_batch = next(training_generator) if keep_sparse: assert sparse.issparse(X_batch) else: assert not sparse.issparse(X_batch) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/tests/000077500000000000000000000000001521136113700241315ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/tests/__init__.py000066400000000000000000000000001521136113700262300ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/tests/test_base.py000066400000000000000000000065051521136113700264620ustar00rootroot00000000000000"""Test for miscellaneous samplers objects.""" # Authors: Guillaume Lemaitre # License: MIT import numpy as np import pytest from scipy import sparse from sklearn.datasets import load_iris, make_regression from sklearn.linear_model import LinearRegression from sklearn.utils import _safe_indexing from sklearn.utils._testing import assert_allclose_dense_sparse, assert_array_equal from sklearn.utils.multiclass import type_of_target from imblearn import FunctionSampler from imblearn.datasets import make_imbalance from imblearn.pipeline import make_pipeline from imblearn.under_sampling import RandomUnderSampler iris = load_iris() X, y = make_imbalance( iris.data, iris.target, sampling_strategy={0: 10, 1: 25}, random_state=0 ) def test_function_sampler_reject_sparse(): X_sparse = sparse.csr_matrix(X) sampler = FunctionSampler(accept_sparse=False) err_msg = "dense data is required" with pytest.raises( TypeError, match=err_msg, ): sampler.fit_resample(X_sparse, y) @pytest.mark.parametrize( "X, y", [(X, y), (sparse.csr_matrix(X), y), (sparse.csc_matrix(X), y)] ) def test_function_sampler_identity(X, y): sampler = FunctionSampler() X_res, y_res = sampler.fit_resample(X, y) assert_allclose_dense_sparse(X_res, X) assert_array_equal(y_res, y) @pytest.mark.parametrize( "X, y", [(X, y), (sparse.csr_matrix(X), y), (sparse.csc_matrix(X), y)] ) def test_function_sampler_func(X, y): def func(X, y): return X[:10], y[:10] sampler = FunctionSampler(func=func) X_res, y_res = sampler.fit_resample(X, y) assert_allclose_dense_sparse(X_res, X[:10]) assert_array_equal(y_res, y[:10]) @pytest.mark.parametrize( "X, y", [(X, y), (sparse.csr_matrix(X), y), (sparse.csc_matrix(X), y)] ) def test_function_sampler_func_kwargs(X, y): def func(X, y, sampling_strategy, random_state): rus = RandomUnderSampler( sampling_strategy=sampling_strategy, random_state=random_state ) return rus.fit_resample(X, y) sampler = FunctionSampler( func=func, kw_args={"sampling_strategy": "auto", "random_state": 0} ) X_res, y_res = sampler.fit_resample(X, y) X_res_2, y_res_2 = RandomUnderSampler(random_state=0).fit_resample(X, y) assert_allclose_dense_sparse(X_res, X_res_2) assert_array_equal(y_res, y_res_2) def test_function_sampler_validate(): # check that we can let a pass a regression variable by turning down the # validation X, y = make_regression() def dummy_sampler(X, y): indices = np.random.choice(np.arange(X.shape[0]), size=100) return _safe_indexing(X, indices), _safe_indexing(y, indices) sampler = FunctionSampler(func=dummy_sampler, validate=False) pipeline = make_pipeline(sampler, LinearRegression()) y_pred = pipeline.fit(X, y).predict(X) assert type_of_target(y_pred) == "continuous" def test_function_resampler_fit(): # Check that the validation is bypass when calling `fit` # Non-regression test for: # https://github.com/scikit-learn-contrib/imbalanced-learn/issues/782 X = np.array([[1, np.nan], [2, 3], [np.inf, 4]]) y = np.array([0, 1, 1]) def func(X, y): return X[:1], y[:1] sampler = FunctionSampler(func=func, validate=False) sampler.fit(X, y) sampler.fit_resample(X, y) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/tests/test_common.py000066400000000000000000000065431521136113700270420ustar00rootroot00000000000000"""Common tests""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import warnings from collections import OrderedDict import numpy as np import pytest from sklearn.exceptions import ConvergenceWarning from sklearn.utils._testing import ignore_warnings from sklearn_compat.utils.estimator_checks import ( parametrize_with_checks as parametrize_with_checks_sklearn, ) from imblearn.over_sampling import RandomOverSampler from imblearn.under_sampling import RandomUnderSampler from imblearn.utils._test_common.instance_generator import ( _get_check_estimator_ids, _get_expected_failed_checks, _tested_estimators, ) from imblearn.utils.estimator_checks import ( _set_checking_parameters, check_dataframe_column_names_consistency, check_param_validation, parametrize_with_checks, ) from imblearn.utils.testing import all_estimators @pytest.mark.parametrize("name, Estimator", all_estimators()) def test_all_estimator_no_base_class(name, Estimator): # test that all_estimators doesn't find abstract classes. msg = f"Base estimators such as {name} should not be included in all_estimators" assert not name.lower().startswith("base"), msg @parametrize_with_checks_sklearn( list(_tested_estimators()), expected_failed_checks=_get_expected_failed_checks ) def test_estimators_compatibility_sklearn(estimator, check, request): _set_checking_parameters(estimator) check(estimator) @parametrize_with_checks( list(_tested_estimators()), expected_failed_checks=_get_expected_failed_checks ) def test_estimators_imblearn(estimator, check, request): # Common tests for estimator instances with ignore_warnings( category=( FutureWarning, ConvergenceWarning, UserWarning, FutureWarning, ) ): _set_checking_parameters(estimator) check(estimator) @pytest.mark.parametrize( "estimator", _tested_estimators(), ids=_get_check_estimator_ids ) def test_check_param_validation(estimator): name = estimator.__class__.__name__ _set_checking_parameters(estimator) check_param_validation(name, estimator) @pytest.mark.parametrize("Sampler", [RandomOverSampler, RandomUnderSampler]) def test_strategy_as_ordered_dict(Sampler): """Check that it is possible to pass an `OrderedDict` as strategy.""" rng = np.random.RandomState(42) X, y = rng.randn(30, 2), np.array([0] * 10 + [1] * 20) sampler = Sampler(random_state=42) if isinstance(sampler, RandomOverSampler): strategy = OrderedDict({0: 20, 1: 20}) else: strategy = OrderedDict({0: 10, 1: 10}) sampler.set_params(sampling_strategy=strategy) X_res, y_res = sampler.fit_resample(X, y) assert X_res.shape[0] == sum(strategy.values()) assert y_res.shape[0] == sum(strategy.values()) @pytest.mark.parametrize( "estimator", _tested_estimators(), ids=_get_check_estimator_ids ) def test_pandas_column_name_consistency(estimator): _set_checking_parameters(estimator) with ignore_warnings(category=(FutureWarning)): with warnings.catch_warnings(record=True) as record: check_dataframe_column_names_consistency( estimator.__class__.__name__, estimator ) for warning in record: assert "was fitted without feature names" not in str(warning.message) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/tests/test_docstring_parameters.py000066400000000000000000000201341521136113700317610ustar00rootroot00000000000000# Authors: Alexandre Gramfort # Raghav RV # License: BSD 3 clause import importlib import inspect import warnings from inspect import signature from pkgutil import walk_packages import pytest from sklearn.datasets import make_classification from sklearn.utils._testing import ( _get_func_name, check_docstring_parameters, ignore_warnings, ) from sklearn.utils.deprecation import _is_deprecated from sklearn.utils.estimator_checks import ( _enforce_estimator_tags_X, _enforce_estimator_tags_y, ) import imblearn from imblearn.base import is_sampler from imblearn.under_sampling import NearMiss from imblearn.utils._test_common.instance_generator import _tested_estimators from imblearn.utils.estimator_checks import _set_checking_parameters # walk_packages() ignores DeprecationWarnings, now we need to ignore # FutureWarnings with warnings.catch_warnings(): warnings.simplefilter("ignore", FutureWarning) # mypy error: Module has no attribute "__path__" imblearn_path = imblearn.__path__ # type: ignore # mypy issue #1422 PUBLIC_MODULES = set( [ pckg[1] for pckg in walk_packages(prefix="imblearn.", path=imblearn_path) if not ("._" in pckg[1] or ".tests." in pckg[1]) ] ) # functions to ignore args / docstring of _DOCSTRING_IGNORES = ["ValueDifferenceMetric"] _IGNORE_ATTRIBUTES = { NearMiss: ["nn_ver3_"], } # Methods where y param should be ignored if y=None by default _METHODS_IGNORE_NONE_Y = [ "fit", "score", "fit_predict", "fit_transform", "partial_fit", "predict", ] # numpydoc 0.8.0's docscrape tool raises because of collections.abc under # Python 3.7 @pytest.mark.filterwarnings("ignore::FutureWarning") @pytest.mark.filterwarnings("ignore::DeprecationWarning") def test_docstring_parameters(): # Test module docstring formatting # Skip test if numpydoc is not found pytest.importorskip( "numpydoc", reason="numpydoc is required to test the docstrings" ) # XXX unreached code as of v0.22 from numpydoc import docscrape incorrect = [] for name in PUBLIC_MODULES: if name.endswith(".conftest"): # pytest tooling, not part of the scikit-learn API continue with warnings.catch_warnings(record=True): module = importlib.import_module(name) classes = inspect.getmembers(module, inspect.isclass) # Exclude non-scikit-learn classes classes = [cls for cls in classes if cls[1].__module__.startswith("imblearn")] for cname, cls in classes: this_incorrect = [] if cname in _DOCSTRING_IGNORES or cname.startswith("_"): continue if inspect.isabstract(cls): continue with warnings.catch_warnings(record=True) as w: cdoc = docscrape.ClassDoc(cls) if len(w): raise RuntimeError(f"Error for __init__ of {cls} in {name}:\n{w[0]}") cls_init = getattr(cls, "__init__", None) if _is_deprecated(cls_init): continue elif cls_init is not None: this_incorrect += check_docstring_parameters(cls.__init__, cdoc) for method_name in cdoc.methods: method = getattr(cls, method_name) if _is_deprecated(method): continue param_ignore = None # Now skip docstring test for y when y is None # by default for API reason if method_name in _METHODS_IGNORE_NONE_Y: sig = signature(method) if "y" in sig.parameters and sig.parameters["y"].default is None: param_ignore = ["y"] # ignore y for fit and score result = check_docstring_parameters(method, ignore=param_ignore) this_incorrect += result incorrect += this_incorrect functions = inspect.getmembers(module, inspect.isfunction) # Exclude imported functions functions = [fn for fn in functions if fn[1].__module__ == name] for fname, func in functions: # Don't test private methods / functions if fname.startswith("_"): continue if fname == "configuration" and name.endswith("setup"): continue name_ = _get_func_name(func) if not any(d in name_ for d in _DOCSTRING_IGNORES) and not _is_deprecated( func ): incorrect += check_docstring_parameters(func) msg = "\n".join(incorrect) if len(incorrect) > 0: raise AssertionError("Docstring Error:\n" + msg) @ignore_warnings(category=FutureWarning) def test_tabs(): # Test that there are no tabs in our source files for importer, modname, ispkg in walk_packages( imblearn.__path__, prefix="imblearn." ): # because we don't import mod = importlib.import_module(modname) try: source = inspect.getsource(mod) except OSError: # user probably should have run "make clean" continue assert "\t" not in source, ( f'"{modname}" has tabs, please remove them or add it to the ignore list', ) @pytest.mark.parametrize("estimator", list(_tested_estimators())) def test_fit_docstring_attributes(estimator): pytest.importorskip("numpydoc") from numpydoc import docscrape Estimator = estimator.__class__ if Estimator.__name__ in _DOCSTRING_IGNORES: return doc = docscrape.ClassDoc(Estimator) attributes = doc["Attributes"] _set_checking_parameters(estimator) X, y = make_classification( n_samples=20, n_features=3, n_redundant=0, n_classes=2, random_state=2, ) y = _enforce_estimator_tags_y(estimator, y) X = _enforce_estimator_tags_X(estimator, X) if "oob_score" in estimator.get_params(): estimator.set_params(bootstrap=True, oob_score=True) if is_sampler(estimator): estimator.fit_resample(X, y) else: estimator.fit(X, y) skipped_attributes = set( [ "base_estimator_", # this attribute exist with old version of sklearn ] ) for attr in attributes: if attr.name in skipped_attributes: continue desc = " ".join(attr.desc).lower() # As certain attributes are present "only" if a certain parameter is # provided, this checks if the word "only" is present in the attribute # description, and if not the attribute is required to be present. if "only " in desc: continue # ignore deprecation warnings with ignore_warnings(category=FutureWarning): if attr.name in _IGNORE_ATTRIBUTES.get(Estimator, []): continue assert hasattr(estimator, attr.name) fit_attr = _get_all_fitted_attributes(estimator) fit_attr_names = [attr.name for attr in attributes] undocumented_attrs = set(fit_attr).difference(fit_attr_names) undocumented_attrs = set(undocumented_attrs).difference(skipped_attributes) if undocumented_attrs: raise AssertionError( f"Undocumented attributes for {Estimator.__name__}: {undocumented_attrs}" ) def _get_all_fitted_attributes(estimator): "Get all the fitted attributes of an estimator including properties" # attributes fit_attr = list(estimator.__dict__.keys()) # properties with warnings.catch_warnings(): warnings.filterwarnings("error", category=FutureWarning) for name in dir(estimator.__class__): obj = getattr(estimator.__class__, name) if not isinstance(obj, property): continue # ignore properties that raises an AttributeError and deprecated # properties try: getattr(estimator, name) except (AttributeError, FutureWarning): continue fit_attr.append(name) return [k for k in fit_attr if k.endswith("_") and not k.startswith("_")] scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/tests/test_exceptions.py000066400000000000000000000005671521136113700277330ustar00rootroot00000000000000"""Test for the exceptions modules""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT from pytest import raises from imblearn.exceptions import raise_isinstance_error def test_raise_isinstance_error(): var = 10.0 with raises(ValueError, match="has to be one of"): raise_isinstance_error("var", [int], var) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/tests/test_pipeline.py000066400000000000000000001367011521136113700273570ustar00rootroot00000000000000""" Test the pipeline module. """ # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import itertools import re import shutil import time from tempfile import mkdtemp import numpy as np import pytest from joblib import Memory from pytest import raises from sklearn import config_context from sklearn.base import BaseEstimator, ClassifierMixin, TransformerMixin, clone from sklearn.cluster import KMeans from sklearn.datasets import load_iris, make_classification from sklearn.decomposition import PCA from sklearn.feature_selection import SelectKBest, f_classif from sklearn.linear_model import LinearRegression, LogisticRegression from sklearn.neighbors import LocalOutlierFactor from sklearn.pipeline import FeatureUnion from sklearn.preprocessing import StandardScaler from sklearn.svm import SVC from sklearn.utils._testing import ( assert_allclose, assert_array_almost_equal, assert_array_equal, ) from sklearn.utils.fixes import parse_version from sklearn_compat._sklearn_compat import sklearn_version from sklearn_compat.utils._tags import Tags from imblearn.base import BaseSampler from imblearn.datasets import make_imbalance from imblearn.pipeline import Pipeline, make_pipeline from imblearn.under_sampling import EditedNearestNeighbours as ENN from imblearn.under_sampling import RandomUnderSampler from imblearn.utils.estimator_checks import check_param_validation JUNK_FOOD_DOCS = ( "the pizza pizza beer copyright", "the pizza burger beer copyright", "the the pizza beer beer copyright", "the burger beer beer copyright", "the coke burger coke copyright", "the coke burger burger", ) R_TOL = 1e-4 class NoFit: """Small class to test parameter dispatching.""" def __init__(self, a=None, b=None): self.a = a self.b = b def __sklearn_tags__(self): return Tags() class NoTrans(NoFit): def fit(self, X, y): return self def get_params(self, deep=False): return {"a": self.a, "b": self.b} def set_params(self, **params): self.a = params["a"] return self class NoInvTransf(NoTrans): def transform(self, X, y=None): return X class Transf(NoInvTransf): def transform(self, X, y=None): return X def inverse_transform(self, X): return X class TransfFitParams(Transf): def fit(self, X, y, **fit_params): self.fit_params = fit_params return self class Mult(BaseEstimator): def __init__(self, mult=1): self.mult = mult def __sklearn_is_fitted__(self): return True def fit(self, X, y): return self def transform(self, X): return np.asarray(X) * self.mult def inverse_transform(self, X): return np.asarray(X) / self.mult def predict(self, X): return (np.asarray(X) * self.mult).sum(axis=1) predict_proba = predict_log_proba = decision_function = predict def score(self, X, y=None): return np.sum(X) class FitParamT(BaseEstimator): """Mock classifier""" def __init__(self): self.successful = False def fit(self, X, y, should_succeed=False): self.fitted_ = True self.successful = should_succeed return self def predict(self, X): return self.successful def fit_predict(self, X, y, should_succeed=False): self.fit(X, y, should_succeed=should_succeed) return self.predict(X) def score(self, X, y=None, sample_weight=None): if sample_weight is not None: X = X * sample_weight return np.sum(X) class DummyTransf(Transf): """Transformer which store the column means""" def fit(self, X, y): self.means_ = np.mean(X, axis=0) # store timestamp to figure out whether the result of 'fit' has been # cached or not self.timestamp_ = time.time() return self class DummyEstimatorParams(BaseEstimator): """Mock classifier that takes params on predict""" def __sklearn_is_fitted__(self): return True def fit(self, X, y): return self def predict(self, X, got_attribute=False): self.got_attribute = got_attribute return self class DummySampler(NoTrans): """Samplers which returns a balanced number of samples""" def fit_resample(self, X, y): self.means_ = np.mean(X, axis=0) # store timestamp to figure out whether the result of 'fit' has been # cached or not self.timestamp_ = time.time() return X, y class FitTransformSample(NoTrans): """Estimator implementing both transform and sample""" def __sklearn_is_fitted__(self): return True def fit(self, X, y, should_succeed=False): pass def fit_resample(self, X, y=None): return X, y def fit_transform(self, X, y=None): return self.fit(X, y).transform(X) def transform(self, X, y=None): return X def test_pipeline_init_tuple(): # Pipeline accepts steps as tuple X = np.array([[1, 2]]) pipe = Pipeline((("transf", Transf()), ("clf", FitParamT()))) pipe.fit(X, y=None) pipe.score(X) pipe.set_params(transf="passthrough") pipe.fit(X, y=None) pipe.score(X) def test_pipeline_init(): # Test the various init parameters of the pipeline. with raises(TypeError): Pipeline() # Check that we can't instantiate pipelines with objects without fit # method X, y = load_iris(return_X_y=True) error_regex = ( "Last step of Pipeline should implement fit or be the string 'passthrough'" ) with raises(TypeError, match=error_regex): model = Pipeline([("clf", NoFit())]) model.fit(X, y) # Smoke test with only an estimator clf = NoTrans() pipe = Pipeline([("svc", clf)]) expected = dict(svc__a=None, svc__b=None, svc=clf, **pipe.get_params(deep=False)) assert pipe.get_params(deep=True) == expected # Check that params are set pipe.set_params(svc__a=0.1) assert clf.a == 0.1 assert clf.b is None # Smoke test the repr: repr(pipe) # Test with two objects clf = SVC(gamma="scale") filter1 = SelectKBest(f_classif) pipe = Pipeline([("anova", filter1), ("svc", clf)]) # Check that we can't instantiate with non-transformers on the way # Note that NoTrans implements fit, but not transform error_regex = "implement fit and transform or fit_resample" with raises(TypeError, match=error_regex): model = Pipeline([("t", NoTrans()), ("svc", clf)]) model.fit(X, y) # Check that params are set pipe.set_params(svc__C=0.1) assert clf.C == 0.1 # Smoke test the repr: repr(pipe) # Check that params are not set when naming them wrong with raises(ValueError): pipe.set_params(anova__C=0.1) # Test clone pipe2 = clone(pipe) assert pipe.named_steps["svc"] is not pipe2.named_steps["svc"] # Check that apart from estimators, the parameters are the same params = pipe.get_params(deep=True) params2 = pipe2.get_params(deep=True) for x in pipe.get_params(deep=False): params.pop(x) for x in pipe2.get_params(deep=False): params2.pop(x) # Remove estimators that where copied params.pop("svc") params.pop("anova") params2.pop("svc") params2.pop("anova") assert params == params2 def test_pipeline_methods_anova(): # Test the various methods of the pipeline (anova). iris = load_iris() X = iris.data y = iris.target # Test with Anova + LogisticRegression clf = LogisticRegression() filter1 = SelectKBest(f_classif, k=2) pipe = Pipeline([("anova", filter1), ("logistic", clf)]) pipe.fit(X, y) pipe.predict(X) pipe.predict_proba(X) pipe.predict_log_proba(X) pipe.score(X, y) def test_pipeline_fit_params(): # Test that the pipeline can take fit parameters pipe = Pipeline([("transf", Transf()), ("clf", FitParamT())]) pipe.fit(X=None, y=None, clf__should_succeed=True) # classifier should return True assert pipe.predict(None) # and transformer params should not be changed assert pipe.named_steps["transf"].a is None assert pipe.named_steps["transf"].b is None # invalid parameters should raise an error message with raises(TypeError, match="unexpected keyword argument"): pipe.fit(None, None, clf__bad=True) def test_pipeline_sample_weight_supported(): # Pipeline should pass sample_weight X = np.array([[1, 2]]) pipe = Pipeline([("transf", Transf()), ("clf", FitParamT())]) pipe.fit(X, y=None) assert pipe.score(X) == 3 assert pipe.score(X, y=None) == 3 assert pipe.score(X, y=None, sample_weight=None) == 3 assert pipe.score(X, sample_weight=np.array([2, 3])) == 8 def test_pipeline_sample_weight_unsupported(): # When sample_weight is None it shouldn't be passed X = np.array([[1, 2]]) pipe = Pipeline([("transf", Transf()), ("clf", Mult())]) pipe.fit(X, y=None) assert pipe.score(X) == 3 assert pipe.score(X, sample_weight=None) == 3 with raises(TypeError, match="unexpected keyword argument"): pipe.score(X, sample_weight=np.array([2, 3])) def test_pipeline_raise_set_params_error(): # Test pipeline raises set params error message for nested models. pipe = Pipeline([("cls", LinearRegression())]) with raises(ValueError, match="Invalid parameter"): pipe.set_params(fake="nope") # nested model check with raises(ValueError, match="Invalid parameter"): pipe.set_params(fake__estimator="nope") def test_pipeline_methods_pca_svm(): # Test the various methods of the pipeline (pca + svm). iris = load_iris() X = iris.data y = iris.target # Test with PCA + LogisticRegression clf = LogisticRegression() pca = PCA(svd_solver="full", n_components="mle", whiten=True) pipe = Pipeline([("pca", pca), ("clf", clf)]) pipe.fit(X, y) pipe.predict(X) pipe.predict_proba(X) pipe.predict_log_proba(X) pipe.score(X, y) def test_pipeline_methods_preprocessing_svm(): # Test the various methods of the pipeline (preprocessing + svm). iris = load_iris() X = iris.data y = iris.target n_samples = X.shape[0] n_classes = len(np.unique(y)) scaler = StandardScaler() pca = PCA(n_components=2, svd_solver="randomized", whiten=True) clf = LogisticRegression() for preprocessing in [scaler, pca]: pipe = Pipeline([("preprocess", preprocessing), ("clf", clf)]) pipe.fit(X, y) # check shapes of various prediction functions predict = pipe.predict(X) assert predict.shape == (n_samples,) proba = pipe.predict_proba(X) assert proba.shape == (n_samples, n_classes) log_proba = pipe.predict_log_proba(X) assert log_proba.shape == (n_samples, n_classes) decision_function = pipe.decision_function(X) assert decision_function.shape == (n_samples, n_classes) pipe.score(X, y) def test_fit_predict_on_pipeline(): # test that the fit_predict method is implemented on a pipeline # test that the fit_predict on pipeline yields same results as applying # transform and clustering steps separately iris = load_iris() scaler = StandardScaler() km = KMeans(random_state=0, n_init=10) # As pipeline doesn't clone estimators on construction, # it must have its own estimators scaler_for_pipeline = StandardScaler() km_for_pipeline = KMeans(random_state=0, n_init=10) # first compute the transform and clustering step separately scaled = scaler.fit_transform(iris.data) separate_pred = km.fit_predict(scaled) # use a pipeline to do the transform and clustering in one step pipe = Pipeline([("scaler", scaler_for_pipeline), ("Kmeans", km_for_pipeline)]) pipeline_pred = pipe.fit_predict(iris.data) assert_array_almost_equal(pipeline_pred, separate_pred) def test_fit_predict_on_pipeline_without_fit_predict(): # tests that a pipeline does not have fit_predict method when final # step of pipeline does not have fit_predict defined scaler = StandardScaler() pca = PCA(svd_solver="full") pipe = Pipeline([("scaler", scaler), ("pca", pca)]) error_regex = "has no attribute 'fit_predict'" with raises(AttributeError, match=error_regex): getattr(pipe, "fit_predict") def test_fit_predict_with_intermediate_fit_params(): # tests that Pipeline passes fit_params to intermediate steps # when fit_predict is invoked pipe = Pipeline([("transf", TransfFitParams()), ("clf", FitParamT())]) pipe.fit_predict( X=None, y=None, transf__should_get_this=True, clf__should_succeed=True ) assert pipe.named_steps["transf"].fit_params["should_get_this"] assert pipe.named_steps["clf"].successful assert "should_succeed" not in pipe.named_steps["transf"].fit_params def test_pipeline_transform(): # Test whether pipeline works with a transformer at the end. # Also test pipeline.transform and pipeline.inverse_transform iris = load_iris() X = iris.data pca = PCA(n_components=2, svd_solver="full") pipeline = Pipeline([("pca", pca)]) # test transform and fit_transform: X_trans = pipeline.fit(X).transform(X) X_trans2 = pipeline.fit_transform(X) X_trans3 = pca.fit_transform(X) assert_array_almost_equal(X_trans, X_trans2) assert_array_almost_equal(X_trans, X_trans3) X_back = pipeline.inverse_transform(X_trans) X_back2 = pca.inverse_transform(X_trans) assert_array_almost_equal(X_back, X_back2) def test_pipeline_fit_transform(): # Test whether pipeline works with a transformer missing fit_transform iris = load_iris() X = iris.data y = iris.target transf = Transf() pipeline = Pipeline([("mock", transf)]) # test fit_transform: X_trans = pipeline.fit_transform(X, y) X_trans2 = transf.fit(X, y).transform(X) assert_array_almost_equal(X_trans, X_trans2) def test_set_pipeline_steps(): transf1 = Transf() transf2 = Transf() pipeline = Pipeline([("mock", transf1)]) assert pipeline.named_steps["mock"] is transf1 # Directly setting attr pipeline.steps = [("mock2", transf2)] assert "mock" not in pipeline.named_steps assert pipeline.named_steps["mock2"] is transf2 assert [("mock2", transf2)] == pipeline.steps # Using set_params pipeline.set_params(steps=[("mock", transf1)]) assert [("mock", transf1)] == pipeline.steps # Using set_params to replace single step pipeline.set_params(mock=transf2) assert [("mock", transf2)] == pipeline.steps # With invalid data pipeline.set_params(steps=[("junk", ())]) with raises(TypeError): pipeline.fit([[1]], [1]) with raises(AttributeError): pipeline.fit_transform([[1]], [1]) @pytest.mark.parametrize("passthrough", [None, "passthrough"]) def test_pipeline_correctly_adjusts_steps(passthrough): X = np.array([[1]]) y = np.array([1]) mult2 = Mult(mult=2) mult3 = Mult(mult=3) mult5 = Mult(mult=5) pipeline = Pipeline( [("m2", mult2), ("bad", passthrough), ("m3", mult3), ("m5", mult5)] ) pipeline.fit(X, y) expected_names = ["m2", "bad", "m3", "m5"] actual_names = [name for name, _ in pipeline.steps] assert expected_names == actual_names @pytest.mark.parametrize("passthrough", [None, "passthrough"]) def test_set_pipeline_step_passthrough(passthrough): # Test setting Pipeline steps to None X = np.array([[1]]) y = np.array([1]) mult2 = Mult(mult=2) mult3 = Mult(mult=3) mult5 = Mult(mult=5) def make(): return Pipeline([("m2", mult2), ("m3", mult3), ("last", mult5)]) pipeline = make() exp = 2 * 3 * 5 assert_array_equal([[exp]], pipeline.fit_transform(X, y)) assert_array_equal([exp], pipeline.fit(X).predict(X)) assert_array_equal(X, pipeline.inverse_transform([[exp]])) pipeline.set_params(m3=passthrough) exp = 2 * 5 assert_array_equal([[exp]], pipeline.fit_transform(X, y)) assert_array_equal([exp], pipeline.fit(X).predict(X)) assert_array_equal(X, pipeline.inverse_transform([[exp]])) expected_params = { "steps": pipeline.steps, "m2": mult2, "m3": passthrough, "last": mult5, "memory": None, "m2__mult": 2, "last__mult": 5, "verbose": False, "transform_input": None, } assert pipeline.get_params(deep=True) == expected_params pipeline.set_params(m2=passthrough) exp = 5 assert_array_equal([[exp]], pipeline.fit_transform(X, y)) assert_array_equal([exp], pipeline.fit(X).predict(X)) assert_array_equal(X, pipeline.inverse_transform([[exp]])) # for other methods, ensure no AttributeErrors on None: other_methods = [ "predict_proba", "predict_log_proba", "decision_function", "transform", "score", ] for method in other_methods: getattr(pipeline, method)(X) pipeline.set_params(m2=mult2) exp = 2 * 5 assert_array_equal([[exp]], pipeline.fit_transform(X, y)) assert_array_equal([exp], pipeline.fit(X).predict(X)) assert_array_equal(X, pipeline.inverse_transform([[exp]])) pipeline = make() pipeline.set_params(last=passthrough) # mult2 and mult3 are active exp = 6 pipeline.fit(X, y) pipeline.transform(X) assert_array_equal([[exp]], pipeline.fit(X, y).transform(X)) assert_array_equal([[exp]], pipeline.fit_transform(X, y)) assert_array_equal(X, pipeline.inverse_transform([[exp]])) with raises(AttributeError, match="has no attribute 'predict'"): getattr(pipeline, "predict") # Check 'passthrough' step at construction time exp = 2 * 5 pipeline = Pipeline([("m2", mult2), ("m3", passthrough), ("last", mult5)]) assert_array_equal([[exp]], pipeline.fit_transform(X, y)) assert_array_equal([exp], pipeline.fit(X).predict(X)) assert_array_equal(X, pipeline.inverse_transform([[exp]])) def test_pipeline_ducktyping(): pipeline = make_pipeline(Mult(5)) pipeline.predict pipeline.transform pipeline.inverse_transform pipeline = make_pipeline(Transf()) assert not hasattr(pipeline, "predict") pipeline.transform pipeline.inverse_transform pipeline = make_pipeline("passthrough") assert pipeline.steps[0] == ("passthrough", "passthrough") assert not hasattr(pipeline, "predict") pipeline.transform pipeline.inverse_transform pipeline = make_pipeline(Transf(), NoInvTransf()) assert not hasattr(pipeline, "predict") pipeline.transform assert not hasattr(pipeline, "inverse_transform") pipeline = make_pipeline(NoInvTransf(), Transf()) assert not hasattr(pipeline, "predict") pipeline.transform assert not hasattr(pipeline, "inverse_transform") def test_make_pipeline(): t1 = Transf() t2 = Transf() pipe = make_pipeline(t1, t2) assert isinstance(pipe, Pipeline) assert pipe.steps[0][0] == "transf-1" assert pipe.steps[1][0] == "transf-2" pipe = make_pipeline(t1, t2, FitParamT()) assert isinstance(pipe, Pipeline) assert pipe.steps[0][0] == "transf-1" assert pipe.steps[1][0] == "transf-2" assert pipe.steps[2][0] == "fitparamt" def test_classes_property(): iris = load_iris() X = iris.data y = iris.target reg = make_pipeline(SelectKBest(k=1), LinearRegression()) reg.fit(X, y) with raises(AttributeError): getattr(reg, "classes_") clf = make_pipeline( SelectKBest(k=1), LogisticRegression(), ) with raises(AttributeError): getattr(clf, "classes_") clf.fit(X, y) assert_array_equal(clf.classes_, np.unique(y)) def test_pipeline_memory_transformer(): iris = load_iris() X = iris.data y = iris.target cachedir = mkdtemp() try: memory = Memory(cachedir, verbose=10) # Test with Transformer + LogisticRegression clf = LogisticRegression() transf = DummyTransf() pipe = Pipeline([("transf", clone(transf)), ("clf", clf)]) cached_pipe = Pipeline([("transf", transf), ("clf", clf)], memory=memory) # Memoize the transformer at the first fit cached_pipe.fit(X, y) pipe.fit(X, y) # Get the time stamp of the tranformer in the cached pipeline expected_ts = cached_pipe.named_steps["transf"].timestamp_ # Check that cached_pipe and pipe yield identical results assert_array_equal(pipe.predict(X), cached_pipe.predict(X)) assert_array_equal(pipe.predict_proba(X), cached_pipe.predict_proba(X)) assert_array_equal(pipe.predict_log_proba(X), cached_pipe.predict_log_proba(X)) assert_array_equal(pipe.score(X, y), cached_pipe.score(X, y)) assert_array_equal( pipe.named_steps["transf"].means_, cached_pipe.named_steps["transf"].means_, ) assert not hasattr(transf, "means_") # Check that we are reading the cache while fitting # a second time cached_pipe.fit(X, y) # Check that cached_pipe and pipe yield identical results assert_array_equal(pipe.predict(X), cached_pipe.predict(X)) assert_array_equal(pipe.predict_proba(X), cached_pipe.predict_proba(X)) assert_array_equal(pipe.predict_log_proba(X), cached_pipe.predict_log_proba(X)) assert_array_equal(pipe.score(X, y), cached_pipe.score(X, y)) assert_array_equal( pipe.named_steps["transf"].means_, cached_pipe.named_steps["transf"].means_, ) assert cached_pipe.named_steps["transf"].timestamp_ == expected_ts # Create a new pipeline with cloned estimators # Check that even changing the name step does not affect the cache hit clf_2 = LogisticRegression(random_state=0) transf_2 = DummyTransf() cached_pipe_2 = Pipeline( [("transf_2", transf_2), ("clf", clf_2)], memory=memory ) cached_pipe_2.fit(X, y) # Check that cached_pipe and pipe yield identical results assert_array_equal(pipe.predict(X), cached_pipe_2.predict(X)) assert_array_equal(pipe.predict_proba(X), cached_pipe_2.predict_proba(X)) assert_array_equal( pipe.predict_log_proba(X), cached_pipe_2.predict_log_proba(X) ) assert_array_equal(pipe.score(X, y), cached_pipe_2.score(X, y)) assert_array_equal( pipe.named_steps["transf"].means_, cached_pipe_2.named_steps["transf_2"].means_, ) assert cached_pipe_2.named_steps["transf_2"].timestamp_ == expected_ts finally: shutil.rmtree(cachedir) def test_pipeline_memory_sampler(): X, y = make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=5000, random_state=0, ) cachedir = mkdtemp() try: memory = Memory(cachedir, verbose=10) # Test with Sampler + LogisticRegression clf = LogisticRegression() transf = DummySampler() pipe = Pipeline([("transf", clone(transf)), ("clf", clf)]) cached_pipe = Pipeline([("transf", transf), ("clf", clf)], memory=memory) # Memoize the transformer at the first fit cached_pipe.fit(X, y) pipe.fit(X, y) # Get the time stamp of the tranformer in the cached pipeline expected_ts = cached_pipe.named_steps["transf"].timestamp_ # Check that cached_pipe and pipe yield identical results assert_array_equal(pipe.predict(X), cached_pipe.predict(X)) assert_array_equal(pipe.predict_proba(X), cached_pipe.predict_proba(X)) assert_array_equal(pipe.predict_log_proba(X), cached_pipe.predict_log_proba(X)) assert_array_equal(pipe.score(X, y), cached_pipe.score(X, y)) assert_array_equal( pipe.named_steps["transf"].means_, cached_pipe.named_steps["transf"].means_, ) assert not hasattr(transf, "means_") # Check that we are reading the cache while fitting # a second time cached_pipe.fit(X, y) # Check that cached_pipe and pipe yield identical results assert_array_equal(pipe.predict(X), cached_pipe.predict(X)) assert_array_equal(pipe.predict_proba(X), cached_pipe.predict_proba(X)) assert_array_equal(pipe.predict_log_proba(X), cached_pipe.predict_log_proba(X)) assert_array_equal(pipe.score(X, y), cached_pipe.score(X, y)) assert_array_equal( pipe.named_steps["transf"].means_, cached_pipe.named_steps["transf"].means_, ) assert cached_pipe.named_steps["transf"].timestamp_ == expected_ts # Create a new pipeline with cloned estimators # Check that even changing the name step does not affect the cache hit clf_2 = LogisticRegression(random_state=0) transf_2 = DummySampler() cached_pipe_2 = Pipeline( [("transf_2", transf_2), ("clf", clf_2)], memory=memory ) cached_pipe_2.fit(X, y) # Check that cached_pipe and pipe yield identical results assert_array_equal(pipe.predict(X), cached_pipe_2.predict(X)) assert_array_equal(pipe.predict_proba(X), cached_pipe_2.predict_proba(X)) assert_array_equal( pipe.predict_log_proba(X), cached_pipe_2.predict_log_proba(X) ) assert_array_equal(pipe.score(X, y), cached_pipe_2.score(X, y)) assert_array_equal( pipe.named_steps["transf"].means_, cached_pipe_2.named_steps["transf_2"].means_, ) assert cached_pipe_2.named_steps["transf_2"].timestamp_ == expected_ts finally: shutil.rmtree(cachedir) def test_pipeline_methods_pca_rus_svm(): # Test the various methods of the pipeline (pca + svm). X, y = make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=5000, random_state=0, ) # Test with PCA + LogisticRegression clf = LogisticRegression() pca = PCA() rus = RandomUnderSampler(random_state=0) pipe = Pipeline([("pca", pca), ("rus", rus), ("clf", clf)]) pipe.fit(X, y) pipe.predict(X) pipe.predict_proba(X) pipe.predict_log_proba(X) pipe.score(X, y) def test_pipeline_methods_rus_pca_svm(): # Test the various methods of the pipeline (pca + svm). X, y = make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=5000, random_state=0, ) # Test with PCA + LogisticRegression clf = LogisticRegression() pca = PCA() rus = RandomUnderSampler(random_state=0) pipe = Pipeline([("rus", rus), ("pca", pca), ("clf", clf)]) pipe.fit(X, y) pipe.predict(X) pipe.predict_proba(X) pipe.predict_log_proba(X) pipe.score(X, y) def test_pipeline_sample(): # Test whether pipeline works with a sampler at the end. # Also test pipeline.sampler X, y = make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=5000, random_state=0, ) rus = RandomUnderSampler(random_state=0) pipeline = Pipeline([("rus", rus)]) # test transform and fit_transform: X_trans, y_trans = pipeline.fit_resample(X, y) X_trans2, y_trans2 = rus.fit_resample(X, y) assert_allclose(X_trans, X_trans2, rtol=R_TOL) assert_allclose(y_trans, y_trans2, rtol=R_TOL) pca = PCA() pipeline = Pipeline([("pca", PCA()), ("rus", rus)]) X_trans, y_trans = pipeline.fit_resample(X, y) X_pca = pca.fit_transform(X) X_trans2, y_trans2 = rus.fit_resample(X_pca, y) # We round the value near to zero. It seems that PCA has some issue # with that X_trans[np.bitwise_and(X_trans < R_TOL, X_trans > -R_TOL)] = 0 X_trans2[np.bitwise_and(X_trans2 < R_TOL, X_trans2 > -R_TOL)] = 0 assert_allclose(X_trans, X_trans2, rtol=R_TOL) assert_allclose(y_trans, y_trans2, rtol=R_TOL) def test_pipeline_sample_transform(): # Test whether pipeline works with a sampler at the end. # Also test pipeline.sampler X, y = make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=5000, random_state=0, ) rus = RandomUnderSampler(random_state=0) pca = PCA() pca2 = PCA() pipeline = Pipeline([("pca", pca), ("rus", rus), ("pca2", pca2)]) pipeline.fit(X, y).transform(X) def test_pipeline_none_classifier(): # Test pipeline using None as preprocessing step and a classifier X, y = make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=5000, random_state=0, ) clf = LogisticRegression(solver="lbfgs", random_state=0) pipe = make_pipeline(None, clf) pipe.fit(X, y) pipe.predict(X) pipe.predict_proba(X) pipe.decision_function(X) pipe.score(X, y) def test_pipeline_none_sampler_classifier(): # Test pipeline using None, RUS and a classifier X, y = make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=5000, random_state=0, ) clf = LogisticRegression(solver="lbfgs", random_state=0) rus = RandomUnderSampler(random_state=0) pipe = make_pipeline(None, rus, clf) pipe.fit(X, y) pipe.predict(X) pipe.predict_proba(X) pipe.decision_function(X) pipe.score(X, y) def test_pipeline_sampler_none_classifier(): # Test pipeline using RUS, None and a classifier X, y = make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=5000, random_state=0, ) clf = LogisticRegression(solver="lbfgs", random_state=0) rus = RandomUnderSampler(random_state=0) pipe = make_pipeline(rus, None, clf) pipe.fit(X, y) pipe.predict(X) pipe.predict_proba(X) pipe.decision_function(X) pipe.score(X, y) def test_pipeline_none_sampler_sample(): # Test pipeline using None step and a sampler X, y = make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=5000, random_state=0, ) rus = RandomUnderSampler(random_state=0) pipe = make_pipeline(None, rus) pipe.fit_resample(X, y) def test_pipeline_none_transformer(): # Test pipeline using None and a transformer that implements transform and # inverse_transform X, y = make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=5000, random_state=0, ) pca = PCA(whiten=True) pipe = make_pipeline(None, pca) pipe.fit(X, y) X_trans = pipe.transform(X) X_inversed = pipe.inverse_transform(X_trans) assert_array_almost_equal(X, X_inversed) def test_pipeline_methods_anova_rus(): # Test the various methods of the pipeline (anova). X, y = make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=5000, random_state=0, ) # Test with RandomUnderSampling + Anova + LogisticRegression clf = LogisticRegression(solver="lbfgs") rus = RandomUnderSampler(random_state=0) filter1 = SelectKBest(f_classif, k=2) pipe = Pipeline([("rus", rus), ("anova", filter1), ("logistic", clf)]) pipe.fit(X, y) pipe.predict(X) pipe.predict_proba(X) pipe.predict_log_proba(X) pipe.score(X, y) def test_pipeline_with_step_that_implements_both_sample_and_transform(): # Test the various methods of the pipeline (anova). X, y = make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=5000, random_state=0, ) clf = LogisticRegression(solver="lbfgs") with raises(TypeError): pipeline = Pipeline([("step", FitTransformSample()), ("logistic", clf)]) pipeline.fit(X, y) def test_pipeline_with_step_that_it_is_pipeline(): # Test the various methods of the pipeline (anova). X, y = make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=5000, random_state=0, ) # Test with RandomUnderSampling + Anova + LogisticRegression clf = LogisticRegression(solver="lbfgs") rus = RandomUnderSampler(random_state=0) filter1 = SelectKBest(f_classif, k=2) pipe1 = Pipeline([("rus", rus), ("anova", filter1)]) with raises(TypeError): pipe2 = Pipeline([("pipe1", pipe1), ("logistic", clf)]) pipe2.fit(X, y) def test_pipeline_fit_then_sample_with_sampler_last_estimator(): X, y = make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=50000, random_state=0, ) rus = RandomUnderSampler(random_state=42) enn = ENN() pipeline = make_pipeline(rus, enn) X_fit_resample_resampled, y_fit_resample_resampled = pipeline.fit_resample(X, y) pipeline = make_pipeline(rus, enn) pipeline.fit(X, y) X_fit_then_sample_res, y_fit_then_sample_res = pipeline.fit_resample(X, y) assert_array_equal(X_fit_resample_resampled, X_fit_then_sample_res) assert_array_equal(y_fit_resample_resampled, y_fit_then_sample_res) def test_pipeline_fit_then_sample_3_samplers_with_sampler_last_estimator(): X, y = make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=50000, random_state=0, ) rus = RandomUnderSampler(random_state=42) enn = ENN() pipeline = make_pipeline(rus, enn, rus) X_fit_resample_resampled, y_fit_resample_resampled = pipeline.fit_resample(X, y) pipeline = make_pipeline(rus, enn, rus) pipeline.fit(X, y) X_fit_then_sample_res, y_fit_then_sample_res = pipeline.fit_resample(X, y) assert_array_equal(X_fit_resample_resampled, X_fit_then_sample_res) assert_array_equal(y_fit_resample_resampled, y_fit_then_sample_res) def test_make_pipeline_memory(): cachedir = mkdtemp() try: memory = Memory(cachedir, verbose=10) pipeline = make_pipeline(DummyTransf(), SVC(gamma="scale"), memory=memory) assert pipeline.memory is memory pipeline = make_pipeline(DummyTransf(), SVC(gamma="scale")) assert pipeline.memory is None finally: shutil.rmtree(cachedir) def test_predict_with_predict_params(): # tests that Pipeline passes predict_params to the final estimator # when predict is invoked pipe = Pipeline([("transf", Transf()), ("clf", DummyEstimatorParams())]) pipe.fit(None, None) pipe.predict(X=None, got_attribute=True) assert pipe.named_steps["clf"].got_attribute def test_resampler_last_stage_passthrough(): X, y = make_classification( n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=50000, random_state=0, ) rus = RandomUnderSampler(random_state=42) pipe = make_pipeline(rus, None) pipe.fit_resample(X, y) def test_pipeline_score_samples_pca_lof_binary(): X, y = make_classification( n_classes=2, class_sep=2, weights=[0.3, 0.7], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=500, random_state=0, ) # Test that the score_samples method is implemented on a pipeline. # Test that the score_samples method on pipeline yields same results as # applying transform and score_samples steps separately. rus = RandomUnderSampler(random_state=42) pca = PCA(svd_solver="full", n_components="mle", whiten=True) lof = LocalOutlierFactor(novelty=True) pipe = Pipeline([("rus", rus), ("pca", pca), ("lof", lof)]) pipe.fit(X, y) # Check the shapes assert pipe.score_samples(X).shape == (X.shape[0],) # Check the values X_res, _ = rus.fit_resample(X, y) lof.fit(pca.fit_transform(X_res)) assert_allclose(pipe.score_samples(X), lof.score_samples(pca.transform(X))) def test_score_samples_on_pipeline_without_score_samples(): X = np.array([[1], [2]]) y = np.array([1, 2]) # Test that a pipeline does not have score_samples method when the final # step of the pipeline does not have score_samples defined. pipe = make_pipeline(LogisticRegression()) pipe.fit(X, y) with pytest.raises( AttributeError, match="has no attribute 'score_samples'", ): pipe.score_samples(X) def test_pipeline_param_error(): clf = make_pipeline(LogisticRegression()) with pytest.raises( ValueError, match="Pipeline.fit does not accept the sample_weight parameter", ): clf.fit([[0], [0]], [0, 1], sample_weight=[1, 1]) parameter_grid_test_verbose = ( (est, pattern, method) for (est, pattern), method in itertools.product( [ ( Pipeline([("transf", Transf()), ("clf", FitParamT())]), r"\[Pipeline\].*\(step 1 of 2\) Processing transf.* total=.*\n" r"\[Pipeline\].*\(step 2 of 2\) Processing clf.* total=.*\n$", ), ( Pipeline([("transf", Transf()), ("noop", None), ("clf", FitParamT())]), r"\[Pipeline\].*\(step 1 of 3\) Processing transf.* total=.*\n" r"\[Pipeline\].*\(step 2 of 3\) Processing noop.* total=.*\n" r"\[Pipeline\].*\(step 3 of 3\) Processing clf.* total=.*\n$", ), ( Pipeline( [ ("transf", Transf()), ("noop", "passthrough"), ("clf", FitParamT()), ] ), r"\[Pipeline\].*\(step 1 of 3\) Processing transf.* total=.*\n" r"\[Pipeline\].*\(step 2 of 3\) Processing noop.* total=.*\n" r"\[Pipeline\].*\(step 3 of 3\) Processing clf.* total=.*\n$", ), ( Pipeline([("transf", Transf()), ("clf", None)]), r"\[Pipeline\].*\(step 1 of 2\) Processing transf.* total=.*\n" r"\[Pipeline\].*\(step 2 of 2\) Processing clf.* total=.*\n$", ), ( Pipeline([("transf", None), ("mult", Mult())]), r"\[Pipeline\].*\(step 1 of 2\) Processing transf.* total=.*\n" r"\[Pipeline\].*\(step 2 of 2\) Processing mult.* total=.*\n$", ), ( Pipeline([("transf", "passthrough"), ("mult", Mult())]), r"\[Pipeline\].*\(step 1 of 2\) Processing transf.* total=.*\n" r"\[Pipeline\].*\(step 2 of 2\) Processing mult.* total=.*\n$", ), ( FeatureUnion([("mult1", Mult()), ("mult2", Mult())]), r"\[FeatureUnion\].*\(step 1 of 2\) Processing mult1.* total=.*\n" r"\[FeatureUnion\].*\(step 2 of 2\) Processing mult2.* total=.*\n$", ), ( FeatureUnion([("mult1", "drop"), ("mult2", Mult()), ("mult3", "drop")]), r"\[FeatureUnion\].*\(step 1 of 1\) Processing mult2.* total=.*\n$", ), ], ["fit", "fit_transform", "fit_predict"], ) if hasattr(est, method) and not ( method == "fit_transform" and hasattr(est, "steps") and isinstance(est.steps[-1][1], FitParamT) ) ) @pytest.mark.parametrize("est, pattern, method", parameter_grid_test_verbose) def test_verbose(est, method, pattern, capsys): func = getattr(est, method) X = [[1, 2, 3], [4, 5, 6]] y = [[7], [8]] est.set_params(verbose=False) func(X, y) assert not capsys.readouterr().out, "Got output for verbose=False" est.set_params(verbose=True) func(X, y) assert re.match(pattern, capsys.readouterr().out) def test_pipeline_score_samples_pca_lof_multiclass(): X, y = load_iris(return_X_y=True) sampling_strategy = {0: 50, 1: 30, 2: 20} X, y = make_imbalance(X, y, sampling_strategy=sampling_strategy) # Test that the score_samples method is implemented on a pipeline. # Test that the score_samples method on pipeline yields same results as # applying transform and score_samples steps separately. rus = RandomUnderSampler() pca = PCA(svd_solver="full", n_components="mle", whiten=True) lof = LocalOutlierFactor(novelty=True) pipe = Pipeline([("rus", rus), ("pca", pca), ("lof", lof)]) pipe.fit(X, y) # Check the shapes assert pipe.score_samples(X).shape == (X.shape[0],) # Check the values lof.fit(pca.fit_transform(X)) assert_allclose(pipe.score_samples(X), lof.score_samples(pca.transform(X))) def test_pipeline_param_validation(): model = Pipeline( [("sampler", RandomUnderSampler()), ("classifier", LogisticRegression())] ) check_param_validation("Pipeline", model) def test_pipeline_with_set_output(): pd = pytest.importorskip("pandas") X, y = load_iris(return_X_y=True, as_frame=True) pipeline = make_pipeline( StandardScaler(), RandomUnderSampler(), LogisticRegression() ).set_output(transform="default") pipeline.fit(X, y) X_res, y_res = pipeline[:-1].fit_resample(X, y) assert isinstance(X_res, np.ndarray) # transformer will not change `y` and sampler will always preserve the type of `y` assert isinstance(y_res, type(y)) pipeline.set_output(transform="pandas") X_res, y_res = pipeline[:-1].fit_resample(X, y) assert isinstance(X_res, pd.DataFrame) # transformer will not change `y` and sampler will always preserve the type of `y` assert isinstance(y_res, type(y)) # TODO(0.15): change warning to checking for NotFittedError @pytest.mark.parametrize( "method", [ "predict", "predict_proba", "predict_log_proba", "decision_function", "score", "score_samples", "transform", "inverse_transform", ], ) def test_pipeline_warns_not_fitted(method): class StatelessEstimator(BaseEstimator): """Stateless estimator that doesn't check if it's fitted. Stateless estimators that don't require fit, should properly set the `requires_fit` flag and implement a `__sklearn_check_is_fitted__` returning `True`. """ def fit(self, X, y): return self # pragma: no cover def transform(self, X): return X def predict(self, X): return np.ones(len(X)) def predict_proba(self, X): return np.ones(len(X)) def predict_log_proba(self, X): return np.zeros(len(X)) def decision_function(self, X): return np.ones(len(X)) def score(self, X, y): return 1 def score_samples(self, X): return np.ones(len(X)) def inverse_transform(self, X): return X pipe = Pipeline([("estimator", StatelessEstimator())]) with pytest.warns(FutureWarning, match="This Pipeline instance is not fitted yet."): getattr(pipe, method)([[1]]) # transform_input tests # ===================== @pytest.mark.skipif( sklearn_version < parse_version("1.4"), reason="scikit-learn < 1.4 does not support transform_input", ) @config_context(enable_metadata_routing=True) def test_transform_input_explicit_value_check(): """Test that the right transformed values are passed to `fit`.""" class Transformer(TransformerMixin, BaseEstimator): def fit(self, X, y): self.fitted_ = True return self def transform(self, X): return X + 1 class Estimator(ClassifierMixin, BaseEstimator): def fit(self, X, y, X_val=None, y_val=None): assert_array_equal(X, np.array([[1, 2]])) assert_array_equal(y, np.array([0, 1])) assert_array_equal(X_val, np.array([[2, 3]])) assert_array_equal(y_val, np.array([0, 1])) return self X = np.array([[0, 1]]) y = np.array([0, 1]) X_val = np.array([[1, 2]]) y_val = np.array([0, 1]) pipe = Pipeline( [ ("transformer", Transformer()), ("estimator", Estimator().set_fit_request(X_val=True, y_val=True)), ], transform_input=["X_val"], ) pipe.fit(X, y, X_val=X_val, y_val=y_val) def test_transform_input_no_slep6(): """Make sure the right error is raised if slep6 is not enabled.""" X = np.array([[1, 2], [3, 4]]) y = np.array([0, 1]) msg = "The `transform_input` parameter can only be set if metadata" with pytest.raises(ValueError, match=msg): make_pipeline(DummyTransf(), transform_input=["blah"]).fit(X, y) @pytest.mark.skipif( sklearn_version >= parse_version("1.4"), reason="scikit-learn >= 1.4 supports transform_input", ) @config_context(enable_metadata_routing=True) def test_transform_input_sklearn_version(): """Test that transform_input raises error with sklearn < 1.4.""" X = np.array([[1, 2], [3, 4]]) y = np.array([0, 1]) msg = ( "The `transform_input` parameter is not supported in scikit-learn versions " "prior to 1.4" ) with pytest.raises(ValueError, match=msg): make_pipeline(DummyTransf(), transform_input=["blah"]).fit(X, y) # end of transform_input tests # ============================= def test_metadata_routing_with_sampler(): """Check that we can use a sampler with metadata routing.""" X, y = make_classification() cost_matrix = np.random.rand(X.shape[0], 2, 2) class CostSensitiveSampler(BaseSampler): def fit_resample(self, X, y, cost_matrix=None): return self._fit_resample(X, y, cost_matrix=cost_matrix) def _fit_resample(self, X, y, cost_matrix=None): self.cost_matrix_ = cost_matrix return X, y with config_context(enable_metadata_routing=True): sampler = CostSensitiveSampler().set_fit_resample_request(cost_matrix=True) pipeline = Pipeline([("sampler", sampler), ("model", LogisticRegression())]) pipeline.fit(X, y, cost_matrix=cost_matrix) assert_allclose(pipeline[0].cost_matrix_, cost_matrix) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/tests/test_public_functions.py000066400000000000000000000077041521136113700311200ustar00rootroot00000000000000"""This is a copy of sklearn/tests/test_public_functions.py. It can be removed when we support scikit-learn >= 1.2. """ from importlib import import_module from inspect import signature import pytest from sklearn.utils._param_validation import ( generate_invalid_param_val, generate_valid_param, make_constraint, ) PARAM_VALIDATION_FUNCTION_LIST = [ "imblearn.datasets.fetch_datasets", "imblearn.datasets.make_imbalance", "imblearn.metrics.classification_report_imbalanced", "imblearn.metrics.geometric_mean_score", "imblearn.metrics.macro_averaged_mean_absolute_error", "imblearn.metrics.make_index_balanced_accuracy", "imblearn.metrics.sensitivity_specificity_support", "imblearn.metrics.sensitivity_score", "imblearn.metrics.specificity_score", "imblearn.pipeline.make_pipeline", ] @pytest.mark.parametrize("func_module", PARAM_VALIDATION_FUNCTION_LIST) def test_function_param_validation(func_module): """Check that an informative error is raised when the value of a parameter does not have an appropriate type or value. """ module_name, func_name = func_module.rsplit(".", 1) module = import_module(module_name) func = getattr(module, func_name) func_sig = signature(func) func_params = [ p.name for p in func_sig.parameters.values() if p.kind not in (p.VAR_POSITIONAL, p.VAR_KEYWORD) ] parameter_constraints = getattr(func, "_skl_parameter_constraints") # Generate valid values for the required parameters # The parameters `*args` and `**kwargs` are ignored since we cannot generate # constraints. required_params = [ p.name for p in func_sig.parameters.values() if p.default is p.empty and p.kind not in (p.VAR_POSITIONAL, p.VAR_KEYWORD) ] valid_required_params = {} for param_name in required_params: if parameter_constraints[param_name] == "no_validation": valid_required_params[param_name] = 1 else: valid_required_params[param_name] = generate_valid_param( make_constraint(parameter_constraints[param_name][0]) ) # check that there is a constraint for each parameter if func_params: validation_params = parameter_constraints.keys() unexpected_params = set(validation_params) - set(func_params) missing_params = set(func_params) - set(validation_params) err_msg = ( "Mismatch between _parameter_constraints and the parameters of" f" {func_name}.\nConsider the unexpected parameters {unexpected_params} and" f" expected but missing parameters {missing_params}\n" ) assert set(validation_params) == set(func_params), err_msg # this object does not have a valid type for sure for all params param_with_bad_type = type("BadType", (), {})() for param_name in func_params: constraints = parameter_constraints[param_name] if constraints == "no_validation": # This parameter is not validated continue match = ( rf"The '{param_name}' parameter of {func_name} must be .* Got .* instead." ) # First, check that the error is raised if param doesn't match any valid type. with pytest.raises(ValueError, match=match): func(**{**valid_required_params, param_name: param_with_bad_type}) # Then, for constraints that are more than a type constraint, check that the # error is raised if param does match a valid type but does not match any valid # value for this type. constraints = [make_constraint(constraint) for constraint in constraints] for constraint in constraints: try: bad_value = generate_invalid_param_val(constraint) except NotImplementedError: continue with pytest.raises(ValueError, match=match): func(**{**valid_required_params, param_name: bad_value}) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/000077500000000000000000000000001521136113700257765ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/__init__.py000066400000000000000000000014131521136113700301060ustar00rootroot00000000000000""" The :mod:`imblearn.under_sampling` provides methods to under-sample a dataset. """ from imblearn.under_sampling._prototype_generation import ClusterCentroids from imblearn.under_sampling._prototype_selection import ( AllKNN, CondensedNearestNeighbour, EditedNearestNeighbours, InstanceHardnessThreshold, NearMiss, NeighbourhoodCleaningRule, OneSidedSelection, RandomUnderSampler, RepeatedEditedNearestNeighbours, TomekLinks, ) __all__ = [ "ClusterCentroids", "RandomUnderSampler", "InstanceHardnessThreshold", "NearMiss", "TomekLinks", "EditedNearestNeighbours", "RepeatedEditedNearestNeighbours", "AllKNN", "OneSidedSelection", "CondensedNearestNeighbour", "NeighbourhoodCleaningRule", ] scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_generation/000077500000000000000000000000001521136113700324155ustar00rootroot00000000000000__init__.py000066400000000000000000000004361521136113700344520ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_generation""" The :mod:`imblearn.under_sampling.prototype_generation` submodule contains methods that generate new samples in order to balance the dataset. """ from imblearn.under_sampling._prototype_generation._cluster_centroids import ( ClusterCentroids, ) __all__ = ["ClusterCentroids"] _cluster_centroids.py000066400000000000000000000170371521136113700366120ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_generation"""Class to perform under-sampling by generating centroids based on clustering.""" # Authors: Guillaume Lemaitre # Fernando Nogueira # Christos Aridas # License: MIT import numpy as np from scipy import sparse from sklearn.base import clone from sklearn.cluster import KMeans from sklearn.neighbors import NearestNeighbors from sklearn.utils import _safe_indexing from sklearn.utils._param_validation import HasMethods, StrOptions from imblearn.under_sampling.base import BaseUnderSampler from imblearn.utils import Substitution from imblearn.utils._docstring import _random_state_docstring VOTING_KIND = ("auto", "hard", "soft") @Substitution( sampling_strategy=BaseUnderSampler._sampling_strategy_docstring, random_state=_random_state_docstring, ) class ClusterCentroids(BaseUnderSampler): """Undersample by generating centroids based on clustering methods. Method that under samples the majority class by replacing a cluster of majority samples by the cluster centroid of a KMeans algorithm. This algorithm keeps N majority samples by fitting the KMeans algorithm with N cluster to the majority class and using the coordinates of the N cluster centroids as the new majority samples. Read more in the :ref:`User Guide `. Parameters ---------- {sampling_strategy} {random_state} estimator : estimator object, default=None A scikit-learn compatible clustering method that exposes a `n_clusters` parameter and a `cluster_centers_` fitted attribute. By default, it will be a default :class:`~sklearn.cluster.KMeans` estimator. voting : {{"hard", "soft", "auto"}}, default='auto' Voting strategy to generate the new samples: - If ``'hard'``, the nearest-neighbors of the centroids found using the clustering algorithm will be used. - If ``'soft'``, the centroids found by the clustering algorithm will be used. - If ``'auto'``, if the input is sparse, it will default on ``'hard'`` otherwise, ``'soft'`` will be used. .. versionadded:: 0.3.0 Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. estimator_ : estimator object The validated estimator created from the `estimator` parameter. voting_ : str The validated voting strategy. n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- EditedNearestNeighbours : Under-sampling by editing samples. CondensedNearestNeighbour: Under-sampling by condensing samples. Notes ----- Supports multi-class resampling by sampling each class independently. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from sklearn.cluster import MiniBatchKMeans >>> from imblearn.under_sampling import ClusterCentroids >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> cc = ClusterCentroids( ... estimator=MiniBatchKMeans(n_init=1, random_state=0), random_state=42 ... ) >>> X_res, y_res = cc.fit_resample(X, y) >>> print('Resampled dataset shape %s' % Counter(y_res)) Resampled dataset shape Counter({{...}}) """ _parameter_constraints: dict = { **BaseUnderSampler._parameter_constraints, "estimator": [HasMethods(["fit", "predict"]), None], "voting": [StrOptions({"auto", "hard", "soft"})], "random_state": ["random_state"], } def __init__( self, *, sampling_strategy="auto", random_state=None, estimator=None, voting="auto", ): super().__init__(sampling_strategy=sampling_strategy) self.random_state = random_state self.estimator = estimator self.voting = voting def _validate_estimator(self): """Private function to create the KMeans estimator""" if self.estimator is None: self.estimator_ = KMeans(random_state=self.random_state) else: self.estimator_ = clone(self.estimator) if "n_clusters" not in self.estimator_.get_params(): raise ValueError( "`estimator` should be a clustering estimator exposing a parameter" " `n_clusters` and a fitted parameter `cluster_centers_`." ) def _generate_sample(self, X, y, centroids, target_class): if self.voting_ == "hard": nearest_neighbors = NearestNeighbors(n_neighbors=1) nearest_neighbors.fit(X, y) indices = nearest_neighbors.kneighbors(centroids, return_distance=False) X_new = _safe_indexing(X, np.squeeze(indices)) else: if sparse.issparse(X): X_new = sparse.csr_matrix(centroids, dtype=X.dtype) else: X_new = centroids y_new = np.array([target_class] * centroids.shape[0], dtype=y.dtype) return X_new, y_new def _fit_resample(self, X, y): self._validate_estimator() if self.voting == "auto": self.voting_ = "hard" if sparse.issparse(X) else "soft" else: self.voting_ = self.voting X_resampled, y_resampled = [], [] for target_class in np.unique(y): target_class_indices = np.flatnonzero(y == target_class) if target_class in self.sampling_strategy_.keys(): n_samples = self.sampling_strategy_[target_class] self.estimator_.set_params(**{"n_clusters": n_samples}) self.estimator_.fit(_safe_indexing(X, target_class_indices)) if not hasattr(self.estimator_, "cluster_centers_"): raise RuntimeError( "`estimator` should be a clustering estimator exposing a " "fitted parameter `cluster_centers_`." ) X_new, y_new = self._generate_sample( _safe_indexing(X, target_class_indices), _safe_indexing(y, target_class_indices), self.estimator_.cluster_centers_, target_class, ) X_resampled.append(X_new) y_resampled.append(y_new) else: X_resampled.append(_safe_indexing(X, target_class_indices)) y_resampled.append(_safe_indexing(y, target_class_indices)) if sparse.issparse(X): X_resampled = sparse.vstack(X_resampled) else: X_resampled = np.vstack(X_resampled) y_resampled = np.hstack(y_resampled) return X_resampled, np.array(y_resampled, dtype=y.dtype) def _more_tags(self): return {"sample_indices": False} def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.sampler_tags.sample_indices = False return tags scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_generation/tests/000077500000000000000000000000001521136113700335575ustar00rootroot00000000000000__init__.py000066400000000000000000000000001521136113700355770ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_generation/teststest_cluster_centroids.py000066400000000000000000000122511521136113700406450ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_generation/tests"""Test the module cluster centroids.""" from collections import Counter import numpy as np import pytest from scipy import sparse from sklearn.cluster import KMeans from sklearn.datasets import make_classification from sklearn.linear_model import LogisticRegression from imblearn.under_sampling import ClusterCentroids from imblearn.utils.testing import _CustomClusterer RND_SEED = 0 X = np.array( [ [0.04352327, -0.20515826], [0.92923648, 0.76103773], [0.20792588, 1.49407907], [0.47104475, 0.44386323], [0.22950086, 0.33367433], [0.15490546, 0.3130677], [0.09125309, -0.85409574], [0.12372842, 0.6536186], [0.13347175, 0.12167502], [0.094035, -2.55298982], ] ) Y = np.array([1, 0, 1, 0, 1, 1, 1, 1, 0, 1]) R_TOL = 1e-4 @pytest.mark.parametrize( "X, expected_voting", [(X, "soft"), (sparse.csr_matrix(X), "hard")] ) @pytest.mark.filterwarnings("ignore:The default value of `n_init` will change") def test_fit_resample_check_voting(X, expected_voting): cc = ClusterCentroids(random_state=RND_SEED) cc.fit_resample(X, Y) assert cc.voting_ == expected_voting @pytest.mark.filterwarnings("ignore:The default value of `n_init` will change") def test_fit_resample_auto(): sampling_strategy = "auto" cc = ClusterCentroids(sampling_strategy=sampling_strategy, random_state=RND_SEED) X_resampled, y_resampled = cc.fit_resample(X, Y) assert X_resampled.shape == (6, 2) assert y_resampled.shape == (6,) @pytest.mark.filterwarnings("ignore:The default value of `n_init` will change") def test_fit_resample_half(): sampling_strategy = {0: 3, 1: 6} cc = ClusterCentroids(sampling_strategy=sampling_strategy, random_state=RND_SEED) X_resampled, y_resampled = cc.fit_resample(X, Y) assert X_resampled.shape == (9, 2) assert y_resampled.shape == (9,) @pytest.mark.filterwarnings("ignore:The default value of `n_init` will change") def test_multiclass_fit_resample(): y = Y.copy() y[5] = 2 y[6] = 2 cc = ClusterCentroids(random_state=RND_SEED) _, y_resampled = cc.fit_resample(X, y) count_y_res = Counter(y_resampled) assert count_y_res[0] == 2 assert count_y_res[1] == 2 assert count_y_res[2] == 2 def test_fit_resample_object(): sampling_strategy = "auto" cluster = KMeans(random_state=RND_SEED, n_init=1) cc = ClusterCentroids( sampling_strategy=sampling_strategy, random_state=RND_SEED, estimator=cluster, ) X_resampled, y_resampled = cc.fit_resample(X, Y) assert X_resampled.shape == (6, 2) assert y_resampled.shape == (6,) def test_fit_hard_voting(): sampling_strategy = "auto" voting = "hard" cluster = KMeans(random_state=RND_SEED, n_init=1) cc = ClusterCentroids( sampling_strategy=sampling_strategy, random_state=RND_SEED, estimator=cluster, voting=voting, ) X_resampled, y_resampled = cc.fit_resample(X, Y) assert X_resampled.shape == (6, 2) assert y_resampled.shape == (6,) for x in X_resampled: assert np.any(np.all(x == X, axis=1)) @pytest.mark.filterwarnings("ignore:The default value of `n_init` will change") def test_cluster_centroids_hard_target_class(): # check that the samples selecting by the hard voting corresponds to the # targeted class # non-regression test for: # https://github.com/scikit-learn-contrib/imbalanced-learn/issues/738 X, y = make_classification( n_samples=1000, n_features=2, n_informative=1, n_redundant=0, n_repeated=0, n_clusters_per_class=1, weights=[0.3, 0.7], class_sep=0.01, random_state=0, ) cc = ClusterCentroids(voting="hard", random_state=0) X_res, y_res = cc.fit_resample(X, y) minority_class_indices = np.flatnonzero(y == 0) X_minority_class = X[minority_class_indices] resampled_majority_class_indices = np.flatnonzero(y_res == 1) X_res_majority = X_res[resampled_majority_class_indices] sample_from_minority_in_majority = [ np.all(np.isclose(selected_sample, minority_sample)) for selected_sample in X_res_majority for minority_sample in X_minority_class ] assert sum(sample_from_minority_in_majority) == 0 def test_cluster_centroids_custom_clusterer(): clusterer = _CustomClusterer() cc = ClusterCentroids(estimator=clusterer, random_state=RND_SEED) cc.fit_resample(X, Y) assert isinstance(cc.estimator_.cluster_centers_, np.ndarray) clusterer = _CustomClusterer(expose_cluster_centers=False) cc = ClusterCentroids(estimator=clusterer, random_state=RND_SEED) err_msg = ( "`estimator` should be a clustering estimator exposing a fitted parameter " "`cluster_centers_`." ) with pytest.raises(RuntimeError, match=err_msg): cc.fit_resample(X, Y) clusterer = LogisticRegression() cc = ClusterCentroids(estimator=clusterer, random_state=RND_SEED) err_msg = ( "`estimator` should be a clustering estimator exposing a parameter " "`n_clusters` and a fitted parameter `cluster_centers_`." ) with pytest.raises(ValueError, match=err_msg): cc.fit_resample(X, Y) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection/000077500000000000000000000000001521136113700322475ustar00rootroot00000000000000__init__.py000066400000000000000000000024551521136113700343070ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection""" The :mod:`imblearn.under_sampling.prototype_selection` submodule contains methods that select samples in order to balance the dataset. """ from imblearn.under_sampling._prototype_selection._condensed_nearest_neighbour import ( CondensedNearestNeighbour, ) from imblearn.under_sampling._prototype_selection._edited_nearest_neighbours import ( AllKNN, EditedNearestNeighbours, RepeatedEditedNearestNeighbours, ) from imblearn.under_sampling._prototype_selection._instance_hardness_threshold import ( InstanceHardnessThreshold, ) from imblearn.under_sampling._prototype_selection._nearmiss import NearMiss from imblearn.under_sampling._prototype_selection._neighbourhood_cleaning_rule import ( NeighbourhoodCleaningRule, ) from imblearn.under_sampling._prototype_selection._one_sided_selection import ( OneSidedSelection, ) from imblearn.under_sampling._prototype_selection._random_under_sampler import ( RandomUnderSampler, ) from imblearn.under_sampling._prototype_selection._tomek_links import TomekLinks __all__ = [ "RandomUnderSampler", "InstanceHardnessThreshold", "NearMiss", "TomekLinks", "EditedNearestNeighbours", "RepeatedEditedNearestNeighbours", "AllKNN", "OneSidedSelection", "CondensedNearestNeighbour", "NeighbourhoodCleaningRule", ] _condensed_nearest_neighbour.py000066400000000000000000000216211521136113700404300ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection"""Class to perform under-sampling based on the condensed nearest neighbour method.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numbers from collections import Counter import numpy as np from scipy.sparse import issparse from sklearn.base import clone from sklearn.neighbors import KNeighborsClassifier from sklearn.utils import _safe_indexing, check_random_state from sklearn.utils._param_validation import HasMethods, Interval from imblearn.under_sampling.base import BaseCleaningSampler from imblearn.utils import Substitution from imblearn.utils._docstring import _n_jobs_docstring, _random_state_docstring @Substitution( sampling_strategy=BaseCleaningSampler._sampling_strategy_docstring, n_jobs=_n_jobs_docstring, random_state=_random_state_docstring, ) class CondensedNearestNeighbour(BaseCleaningSampler): """Undersample based on the condensed nearest neighbour method. Read more in the :ref:`User Guide `. Parameters ---------- {sampling_strategy} {random_state} n_neighbors : int or estimator object, default=None If ``int``, size of the neighbourhood to consider to compute the nearest neighbors. If object, an estimator that inherits from :class:`~sklearn.neighbors.base.KNeighborsMixin` that will be used to find the nearest-neighbors. If `None`, a :class:`~sklearn.neighbors.KNeighborsClassifier` with a 1-NN rules will be used. n_seeds_S : int, default=1 Number of samples to extract in order to build the set S. {n_jobs} Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. estimators_ : list of estimator objects of shape (n_resampled_classes - 1,) Contains the K-nearest neighbor estimator used for per of classes. .. versionadded:: 0.12 sample_indices_ : ndarray of shape (n_new_samples,) Indices of the samples selected. .. versionadded:: 0.4 n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- EditedNearestNeighbours : Undersample by editing samples. RepeatedEditedNearestNeighbours : Undersample by repeating ENN algorithm. AllKNN : Undersample using ENN and various number of neighbours. Notes ----- The method is based on [1]_. Supports multi-class resampling: a strategy one (minority) vs. each other classes is applied. References ---------- .. [1] P. Hart, "The condensed nearest neighbor rule," In Information Theory, IEEE Transactions on, vol. 14(3), pp. 515-516, 1968. Examples -------- >>> from collections import Counter # doctest: +SKIP >>> from sklearn.datasets import fetch_openml # doctest: +SKIP >>> from sklearn.preprocessing import scale # doctest: +SKIP >>> from imblearn.under_sampling import \ CondensedNearestNeighbour # doctest: +SKIP >>> X, y = fetch_openml('diabetes', version=1, return_X_y=True) # doctest: +SKIP >>> X = scale(X) # doctest: +SKIP >>> print('Original dataset shape %s' % Counter(y)) # doctest: +SKIP Original dataset shape Counter({{'tested_negative': 500, \ 'tested_positive': 268}}) # doctest: +SKIP >>> cnn = CondensedNearestNeighbour(random_state=42) # doctest: +SKIP >>> X_res, y_res = cnn.fit_resample(X, y) #doctest: +SKIP >>> print('Resampled dataset shape %s' % Counter(y_res)) # doctest: +SKIP Resampled dataset shape Counter({{'tested_positive': 268, \ 'tested_negative': 181}}) # doctest: +SKIP """ _parameter_constraints: dict = { **BaseCleaningSampler._parameter_constraints, "n_neighbors": [ Interval(numbers.Integral, 1, None, closed="left"), HasMethods(["kneighbors", "kneighbors_graph"]), None, ], "n_seeds_S": [Interval(numbers.Integral, 1, None, closed="left")], "n_jobs": [numbers.Integral, None], "random_state": ["random_state"], } def __init__( self, *, sampling_strategy="auto", random_state=None, n_neighbors=None, n_seeds_S=1, n_jobs=None, ): super().__init__(sampling_strategy=sampling_strategy) self.random_state = random_state self.n_neighbors = n_neighbors self.n_seeds_S = n_seeds_S self.n_jobs = n_jobs def _validate_estimator(self): """Private function to create the NN estimator""" if self.n_neighbors is None: estimator = KNeighborsClassifier(n_neighbors=1, n_jobs=self.n_jobs) elif isinstance(self.n_neighbors, numbers.Integral): estimator = KNeighborsClassifier( n_neighbors=self.n_neighbors, n_jobs=self.n_jobs ) elif isinstance(self.n_neighbors, KNeighborsClassifier): estimator = clone(self.n_neighbors) return estimator def _fit_resample(self, X, y): estimator = self._validate_estimator() random_state = check_random_state(self.random_state) target_stats = Counter(y) class_minority = min(target_stats, key=target_stats.get) idx_under = np.empty((0,), dtype=int) self.estimators_ = [] for target_class in np.unique(y): if target_class in self.sampling_strategy_.keys(): # Randomly get one sample from the majority class # Generate the index to select idx_maj = np.flatnonzero(y == target_class) idx_maj_sample = idx_maj[ random_state.randint( low=0, high=target_stats[target_class], size=self.n_seeds_S, ) ] # Create the set C - One majority samples and all minority C_indices = np.append( np.flatnonzero(y == class_minority), idx_maj_sample ) C_x = _safe_indexing(X, C_indices) C_y = _safe_indexing(y, C_indices) # Create the set S - all majority samples S_indices = np.flatnonzero(y == target_class) S_x = _safe_indexing(X, S_indices) S_y = _safe_indexing(y, S_indices) # fit knn on C self.estimators_.append(clone(estimator).fit(C_x, C_y)) good_classif_label = idx_maj_sample.copy() # Check each sample in S if we keep it or drop it for idx_sam, (x_sam, y_sam) in enumerate(zip(S_x, S_y)): # Do not select sample which are already well classified if idx_sam in good_classif_label: continue # Classify on S if not issparse(x_sam): x_sam = x_sam.reshape(1, -1) pred_y = self.estimators_[-1].predict(x_sam) # If the prediction do not agree with the true label # append it in C_x if y_sam != pred_y: # Keep the index for later idx_maj_sample = np.append(idx_maj_sample, idx_maj[idx_sam]) # Update C C_indices = np.append(C_indices, idx_maj[idx_sam]) C_x = _safe_indexing(X, C_indices) C_y = _safe_indexing(y, C_indices) # fit a knn on C self.estimators_[-1].fit(C_x, C_y) # This experimental to speed up the search # Classify all the element in S and avoid to test the # well classified elements pred_S_y = self.estimators_[-1].predict(S_x) good_classif_label = np.unique( np.append(idx_maj_sample, np.flatnonzero(pred_S_y == S_y)) ) idx_under = np.concatenate((idx_under, idx_maj_sample), axis=0) else: idx_under = np.concatenate( (idx_under, np.flatnonzero(y == target_class)), axis=0 ) self.sample_indices_ = idx_under return _safe_indexing(X, idx_under), _safe_indexing(y, idx_under) def _more_tags(self): return {"sample_indices": True} def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.sampler_tags.sample_indices = True return tags _edited_nearest_neighbours.py000066400000000000000000000527211521136113700401140ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection"""Classes to perform under-sampling based on the edited nearest neighbour method.""" # Authors: Guillaume Lemaitre # Dayvid Oliveira # Christos Aridas # License: MIT import numbers from collections import Counter import numpy as np from scipy.stats import mode from sklearn.utils import _safe_indexing from sklearn.utils._param_validation import HasMethods, Interval, StrOptions from imblearn.under_sampling.base import BaseCleaningSampler from imblearn.utils import Substitution, check_neighbors_object from imblearn.utils._docstring import _n_jobs_docstring SEL_KIND = ("all", "mode") @Substitution( sampling_strategy=BaseCleaningSampler._sampling_strategy_docstring, n_jobs=_n_jobs_docstring, ) class EditedNearestNeighbours(BaseCleaningSampler): """Undersample based on the edited nearest neighbour method. This method cleans the dataset by removing samples close to the decision boundary. It removes observations from the majority class or classes when any or most of its closest neighours are from a different class. Read more in the :ref:`User Guide `. Parameters ---------- {sampling_strategy} n_neighbors : int or object, default=3 If ``int``, size of the neighbourhood to consider for the undersampling, i.e., if `n_neighbors=3`, a sample will be removed when any or most of its 3 closest neighbours are from a different class. If object, an estimator that inherits from :class:`~sklearn.neighbors.base.KNeighborsMixin` that will be used to find the nearest-neighbors. Note that if you want to examine the 3 closest neighbours of a sample for the undersampling, you need to pass a 4-KNN. kind_sel : {{'all', 'mode'}}, default='all' Strategy to use to exclude samples. - If ``'all'``, all neighbours should be of the same class of the examined sample for it not be excluded. - If ``'mode'``, most neighbours should be of the same class of the examined sample for it not be excluded. The strategy `"all"` will be less conservative than `'mode'`. Thus, more samples will be removed when `kind_sel="all"`, generally. {n_jobs} Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys correspond to the class labels from which to sample and the values are the number of samples to sample. nn_ : estimator object Validated K-nearest Neighbours instance created from `n_neighbors` parameter. sample_indices_ : ndarray of shape (n_new_samples,) Indices of the samples selected. .. versionadded:: 0.4 n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- CondensedNearestNeighbour : Undersample by condensing samples. RepeatedEditedNearestNeighbours : Undersample by repeating the ENN algorithm. AllKNN : Undersample using ENN with varying neighbours. Notes ----- The method is based on [1]_. Supports multi-class resampling. A one-vs.-rest scheme is used when sampling a class as proposed in [1]_. References ---------- .. [1] D. Wilson, Asymptotic" Properties of Nearest Neighbor Rules Using Edited Data," In IEEE Transactions on Systems, Man, and Cybernetrics, vol. 2 (3), pp. 408-421, 1972. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from imblearn.under_sampling import EditedNearestNeighbours >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> enn = EditedNearestNeighbours() >>> X_res, y_res = enn.fit_resample(X, y) >>> print('Resampled dataset shape %s' % Counter(y_res)) Resampled dataset shape Counter({{1: 887, 0: 100}}) """ _parameter_constraints: dict = { **BaseCleaningSampler._parameter_constraints, "n_neighbors": [ Interval(numbers.Integral, 1, None, closed="left"), HasMethods(["kneighbors", "kneighbors_graph"]), ], "kind_sel": [StrOptions({"all", "mode"})], "n_jobs": [numbers.Integral, None], } def __init__( self, *, sampling_strategy="auto", n_neighbors=3, kind_sel="all", n_jobs=None, ): super().__init__(sampling_strategy=sampling_strategy) self.n_neighbors = n_neighbors self.kind_sel = kind_sel self.n_jobs = n_jobs def _validate_estimator(self): """Validate the estimator created in the ENN.""" self.nn_ = check_neighbors_object( "n_neighbors", self.n_neighbors, additional_neighbor=1 ) self.nn_.set_params(**{"n_jobs": self.n_jobs}) def _fit_resample(self, X, y): self._validate_estimator() idx_under = np.empty((0,), dtype=int) self.nn_.fit(X) for target_class in np.unique(y): if target_class in self.sampling_strategy_.keys(): target_class_indices = np.flatnonzero(y == target_class) X_class = _safe_indexing(X, target_class_indices) y_class = _safe_indexing(y, target_class_indices) nnhood_idx = self.nn_.kneighbors(X_class, return_distance=False)[:, 1:] nnhood_label = y[nnhood_idx] if self.kind_sel == "mode": nnhood_label, _ = mode(nnhood_label, axis=1, keepdims=False) nnhood_bool = np.ravel(nnhood_label) == y_class elif self.kind_sel == "all": nnhood_label = nnhood_label == target_class nnhood_bool = np.all(nnhood_label, axis=1) index_target_class = np.flatnonzero(nnhood_bool) else: index_target_class = slice(None) idx_under = np.concatenate( ( idx_under, np.flatnonzero(y == target_class)[index_target_class], ), axis=0, ) self.sample_indices_ = idx_under return _safe_indexing(X, idx_under), _safe_indexing(y, idx_under) def _more_tags(self): return {"sample_indices": True} def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.sampler_tags.sample_indices = True return tags @Substitution( sampling_strategy=BaseCleaningSampler._sampling_strategy_docstring, n_jobs=_n_jobs_docstring, ) class RepeatedEditedNearestNeighbours(BaseCleaningSampler): """Undersample based on the repeated edited nearest neighbour method. This method repeats the :class:`EditedNearestNeighbours` algorithm several times. The repetitions will stop when i) the maximum number of iterations is reached, or ii) no more observations are being removed, or iii) one of the majority classes becomes a minority class or iv) one of the majority classes disappears during undersampling. Read more in the :ref:`User Guide `. Parameters ---------- {sampling_strategy} n_neighbors : int or object, default=3 If ``int``, size of the neighbourhood to consider for the undersampling, i.e., if `n_neighbors=3`, a sample will be removed when any or most of its 3 closest neighbours are from a different class. If object, an estimator that inherits from :class:`~sklearn.neighbors.base.KNeighborsMixin` that will be used to find the nearest-neighbors. Note that if you want to examine the 3 closest neighbours of a sample for the undersampling, you need to pass a 4-KNN. max_iter : int, default=100 Maximum number of iterations of the edited nearest neighbours. kind_sel : {{'all', 'mode'}}, default='all' Strategy to use to exclude samples. - If ``'all'``, all neighbours should be of the same class of the examined sample for it not be excluded. - If ``'mode'``, most neighbours should be of the same class of the examined sample for it not be excluded. The strategy `"all"` will be less conservative than `'mode'`. Thus, more samples will be removed when `kind_sel="all"`, generally. {n_jobs} Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys correspond to the class labels from which to sample and the values are the number of samples to sample. nn_ : estimator object Validated K-nearest Neighbours estimator linked to the parameter `n_neighbors`. enn_ : sampler object The validated :class:`~imblearn.under_sampling.EditedNearestNeighbours` instance. sample_indices_ : ndarray of shape (n_new_samples,) Indices of the samples selected. .. versionadded:: 0.4 n_iter_ : int Number of iterations run. .. versionadded:: 0.6 n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- CondensedNearestNeighbour : Undersample by condensing samples. EditedNearestNeighbours : Undersample by editing samples. AllKNN : Undersample using ENN with varying neighbours. Notes ----- The method is based on [1]_. A one-vs.-rest scheme is used when sampling a class as proposed in [1]_. Supports multi-class resampling. References ---------- .. [1] I. Tomek, "An Experiment with the Edited Nearest-Neighbor Rule," IEEE Transactions on Systems, Man, and Cybernetics, vol. 6(6), pp. 448-452, June 1976. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from imblearn.under_sampling import RepeatedEditedNearestNeighbours >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> renn = RepeatedEditedNearestNeighbours() >>> X_res, y_res = renn.fit_resample(X, y) >>> print('Resampled dataset shape %s' % Counter(y_res)) Resampled dataset shape Counter({{1: 887, 0: 100}}) """ _parameter_constraints: dict = { **BaseCleaningSampler._parameter_constraints, "n_neighbors": [ Interval(numbers.Integral, 1, None, closed="left"), HasMethods(["kneighbors", "kneighbors_graph"]), ], "max_iter": [Interval(numbers.Integral, 1, None, closed="left")], "kind_sel": [StrOptions({"all", "mode"})], "n_jobs": [numbers.Integral, None], } def __init__( self, *, sampling_strategy="auto", n_neighbors=3, max_iter=100, kind_sel="all", n_jobs=None, ): super().__init__(sampling_strategy=sampling_strategy) self.n_neighbors = n_neighbors self.kind_sel = kind_sel self.n_jobs = n_jobs self.max_iter = max_iter def _validate_estimator(self): """Private function to create the NN estimator""" self.nn_ = check_neighbors_object( "n_neighbors", self.n_neighbors, additional_neighbor=1 ) self.enn_ = EditedNearestNeighbours( sampling_strategy=self.sampling_strategy, n_neighbors=self.nn_, kind_sel=self.kind_sel, n_jobs=self.n_jobs, ) def _fit_resample(self, X, y): self._validate_estimator() X_, y_ = X, y self.sample_indices_ = np.arange(X.shape[0], dtype=int) target_stats = Counter(y) class_minority = min(target_stats, key=target_stats.get) for n_iter in range(self.max_iter): prev_len = y_.shape[0] X_enn, y_enn = self.enn_.fit_resample(X_, y_) # Check the stopping criterion # 1. If there is no changes for the vector y # 2. If the number of samples in the other class become inferior to # the number of samples in the majority class # 3. If one of the class is disappearing # Case 1 b_conv = prev_len == y_enn.shape[0] # Case 2 stats_enn = Counter(y_enn) count_non_min = np.array( [ val for val, key in zip(stats_enn.values(), stats_enn.keys()) if key != class_minority ] ) b_min_bec_maj = np.any(count_non_min < target_stats[class_minority]) # Case 3 b_remove_maj_class = len(stats_enn) < len(target_stats) ( X_, y_, ) = ( X_enn, y_enn, ) self.sample_indices_ = self.sample_indices_[self.enn_.sample_indices_] if b_conv or b_min_bec_maj or b_remove_maj_class: if b_conv: ( X_, y_, ) = ( X_enn, y_enn, ) self.sample_indices_ = self.sample_indices_[ self.enn_.sample_indices_ ] break self.n_iter_ = n_iter + 1 X_resampled, y_resampled = X_, y_ return X_resampled, y_resampled def _more_tags(self): return {"sample_indices": True} def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.sampler_tags.sample_indices = True return tags @Substitution( sampling_strategy=BaseCleaningSampler._sampling_strategy_docstring, n_jobs=_n_jobs_docstring, ) class AllKNN(BaseCleaningSampler): """Undersample based on the AllKNN method. This method will apply :class:`EditedNearestNeighbours` several times varying the number of nearest neighbours at each round. It begins by examining 1 closest neighbour, and it incrases the neighbourhood by 1 at each round. The algorithm stops when the maximum number of neighbours are examined or when the majority class becomes the minority class, whichever comes first. Read more in the :ref:`User Guide `. Parameters ---------- {sampling_strategy} n_neighbors : int or estimator object, default=3 If ``int``, size of the maximum neighbourhood to examine for the undersampling. If `n_neighbors=3`, in the first iteration the algorithm will examine 1 closest neigbhour, in the second round 2, and in the final round 3. If object, an estimator that inherits from :class:`~sklearn.neighbors.base.KNeighborsMixin` that will be used to find the nearest-neighbors. Note that if you want to examine the 3 closest neighbours of a sample, you need to pass a 4-KNN. kind_sel : {{'all', 'mode'}}, default='all' Strategy to use to exclude samples. - If ``'all'``, all neighbours should be of the same class of the examined sample for it not be excluded. - If ``'mode'``, most neighbours should be of the same class of the examined sample for it not be excluded. The strategy `"all"` will be less conservative than `'mode'`. Thus, more samples will be removed when `kind_sel="all"`, generally. allow_minority : bool, default=False If ``True``, it allows the majority classes to become the minority class without early stopping. .. versionadded:: 0.3 {n_jobs} Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys correspond to the class labels from which to sample and the values are the number of samples to sample. nn_ : estimator object Validated K-nearest Neighbours estimator linked to the parameter `n_neighbors`. enn_ : sampler object The validated :class:`~imblearn.under_sampling.EditedNearestNeighbours` instance. sample_indices_ : ndarray of shape (n_new_samples,) Indices of the samples selected. .. versionadded:: 0.4 n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- CondensedNearestNeighbour: Under-sampling by condensing samples. EditedNearestNeighbours: Under-sampling by editing samples. RepeatedEditedNearestNeighbours: Under-sampling by repeating ENN. Notes ----- The method is based on [1]_. Supports multi-class resampling. A one-vs.-rest scheme is used when sampling a class as proposed in [1]_. References ---------- .. [1] I. Tomek, "An Experiment with the Edited Nearest-Neighbor Rule," IEEE Transactions on Systems, Man, and Cybernetics, vol. 6(6), pp. 448-452, June 1976. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from imblearn.under_sampling import AllKNN >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> allknn = AllKNN() >>> X_res, y_res = allknn.fit_resample(X, y) >>> print('Resampled dataset shape %s' % Counter(y_res)) Resampled dataset shape Counter({{1: 887, 0: 100}}) """ _parameter_constraints: dict = { **BaseCleaningSampler._parameter_constraints, "n_neighbors": [ Interval(numbers.Integral, 1, None, closed="left"), HasMethods(["kneighbors", "kneighbors_graph"]), ], "kind_sel": [StrOptions({"all", "mode"})], "allow_minority": ["boolean"], "n_jobs": [numbers.Integral, None], } def __init__( self, *, sampling_strategy="auto", n_neighbors=3, kind_sel="all", allow_minority=False, n_jobs=None, ): super().__init__(sampling_strategy=sampling_strategy) self.n_neighbors = n_neighbors self.kind_sel = kind_sel self.allow_minority = allow_minority self.n_jobs = n_jobs def _validate_estimator(self): """Create objects required by AllKNN""" self.nn_ = check_neighbors_object( "n_neighbors", self.n_neighbors, additional_neighbor=1 ) self.enn_ = EditedNearestNeighbours( sampling_strategy=self.sampling_strategy, n_neighbors=self.nn_, kind_sel=self.kind_sel, n_jobs=self.n_jobs, ) def _fit_resample(self, X, y): self._validate_estimator() X_, y_ = X, y target_stats = Counter(y) class_minority = min(target_stats, key=target_stats.get) self.sample_indices_ = np.arange(X.shape[0], dtype=int) for curr_size_ngh in range(1, self.nn_.n_neighbors): self.enn_.n_neighbors = curr_size_ngh X_enn, y_enn = self.enn_.fit_resample(X_, y_) # Check the stopping criterion # 1. If the number of samples in the other class become inferior to # the number of samples in the majority class # 2. If one of the class is disappearing # Case 1else: stats_enn = Counter(y_enn) count_non_min = np.array( [ val for val, key in zip(stats_enn.values(), stats_enn.keys()) if key != class_minority ] ) b_min_bec_maj = np.any(count_non_min < target_stats[class_minority]) if self.allow_minority: # overwrite b_min_bec_maj b_min_bec_maj = False # Case 2 b_remove_maj_class = len(stats_enn) < len(target_stats) ( X_, y_, ) = ( X_enn, y_enn, ) self.sample_indices_ = self.sample_indices_[self.enn_.sample_indices_] if b_min_bec_maj or b_remove_maj_class: break X_resampled, y_resampled = X_, y_ return X_resampled, y_resampled def _more_tags(self): return {"sample_indices": True} def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.sampler_tags.sample_indices = True return tags _instance_hardness_threshold.py000066400000000000000000000150121521136113700404470ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection"""Class to perform under-sampling based on the instance hardness threshold.""" # Authors: Guillaume Lemaitre # Dayvid Oliveira # Christos Aridas # License: MIT import numbers from collections import Counter import numpy as np from sklearn.base import clone, is_classifier from sklearn.ensemble import RandomForestClassifier from sklearn.ensemble._base import _set_random_states from sklearn.model_selection import StratifiedKFold, cross_val_predict from sklearn.utils import _safe_indexing, check_random_state from sklearn.utils._param_validation import HasMethods from imblearn.under_sampling.base import BaseUnderSampler from imblearn.utils import Substitution from imblearn.utils._docstring import _n_jobs_docstring, _random_state_docstring @Substitution( sampling_strategy=BaseUnderSampler._sampling_strategy_docstring, n_jobs=_n_jobs_docstring, random_state=_random_state_docstring, ) class InstanceHardnessThreshold(BaseUnderSampler): """Undersample based on the instance hardness threshold. Read more in the :ref:`User Guide `. Parameters ---------- estimator : estimator object, default=None Classifier to be used to estimate instance hardness of the samples. This classifier should implement `predict_proba`. {sampling_strategy} {random_state} cv : int, default=5 Number of folds to be used when estimating samples' instance hardness. {n_jobs} Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys correspond to the class labels from which to sample and the values are the number of samples to sample. estimator_ : estimator object The validated classifier used to estimate the instance hardness of the samples. sample_indices_ : ndarray of shape (n_new_samples,) Indices of the samples selected. .. versionadded:: 0.4 n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- NearMiss : Undersample based on near-miss search. RandomUnderSampler : Random under-sampling. Notes ----- The method is based on [1]_. Supports multi-class resampling: from each class to be under-sampled, it retains the observations with the highest probability of being correctly classified. References ---------- .. [1] D. Smith, Michael R., Tony Martinez, and Christophe Giraud-Carrier. "An instance level analysis of data complexity." Machine learning 95.2 (2014): 225-256. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from imblearn.under_sampling import InstanceHardnessThreshold >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> iht = InstanceHardnessThreshold(random_state=42) >>> X_res, y_res = iht.fit_resample(X, y) >>> print('Resampled dataset shape %s' % Counter(y_res)) Resampled dataset shape Counter({{1: 5..., 0: 100}}) """ _parameter_constraints: dict = { **BaseUnderSampler._parameter_constraints, "estimator": [ HasMethods(["fit", "predict_proba"]), None, ], "cv": ["cv_object"], "n_jobs": [numbers.Integral, None], "random_state": ["random_state"], } def __init__( self, *, estimator=None, sampling_strategy="auto", random_state=None, cv=5, n_jobs=None, ): super().__init__(sampling_strategy=sampling_strategy) self.random_state = random_state self.estimator = estimator self.cv = cv self.n_jobs = n_jobs def _validate_estimator(self, random_state): """Private function to create the classifier""" if ( self.estimator is not None and is_classifier(self.estimator) and hasattr(self.estimator, "predict_proba") ): self.estimator_ = clone(self.estimator) _set_random_states(self.estimator_, random_state) elif self.estimator is None: self.estimator_ = RandomForestClassifier( n_estimators=100, random_state=self.random_state, n_jobs=self.n_jobs, ) def _fit_resample(self, X, y): random_state = check_random_state(self.random_state) self._validate_estimator(random_state) target_stats = Counter(y) skf = StratifiedKFold( n_splits=self.cv, shuffle=True, random_state=random_state, ) probabilities = cross_val_predict( self.estimator_, X, y, cv=skf, n_jobs=self.n_jobs, method="predict_proba", ) probabilities = probabilities[range(len(y)), y] idx_under = np.empty((0,), dtype=int) for target_class in np.unique(y): if target_class in self.sampling_strategy_.keys(): n_samples = self.sampling_strategy_[target_class] threshold = np.percentile( probabilities[y == target_class], (1.0 - (n_samples / target_stats[target_class])) * 100.0, ) index_target_class = np.flatnonzero( probabilities[y == target_class] >= threshold ) else: index_target_class = slice(None) idx_under = np.concatenate( ( idx_under, np.flatnonzero(y == target_class)[index_target_class], ), axis=0, ) self.sample_indices_ = idx_under return _safe_indexing(X, idx_under), _safe_indexing(y, idx_under) def _more_tags(self): return {"sample_indices": True} def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.sampler_tags.sample_indices = True return tags _nearmiss.py000066400000000000000000000262411521136113700345270ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection"""Class to perform under-sampling based on nearmiss methods.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numbers import warnings from collections import Counter import numpy as np from sklearn.utils import _safe_indexing from sklearn.utils._param_validation import HasMethods, Interval from imblearn.under_sampling.base import BaseUnderSampler from imblearn.utils import Substitution, check_neighbors_object from imblearn.utils._docstring import _n_jobs_docstring @Substitution( sampling_strategy=BaseUnderSampler._sampling_strategy_docstring, n_jobs=_n_jobs_docstring, ) class NearMiss(BaseUnderSampler): """Class to perform under-sampling based on NearMiss methods. Read more in the :ref:`User Guide `. Parameters ---------- {sampling_strategy} version : int, default=1 Version of the NearMiss to use. Possible values are 1, 2 or 3. n_neighbors : int or estimator object, default=3 If ``int``, size of the neighbourhood to consider to compute the average distance to the minority point samples. If object, an estimator that inherits from :class:`~sklearn.neighbors.base.KNeighborsMixin` that will be used to find the k_neighbors. By default, it will be a 3-NN. n_neighbors_ver3 : int or estimator object, default=3 If ``int``, NearMiss-3 algorithm start by a phase of re-sampling. This parameter correspond to the number of neighbours selected create the subset in which the selection will be performed. If object, an estimator that inherits from :class:`~sklearn.neighbors.base.KNeighborsMixin` that will be used to find the k_neighbors. By default, it will be a 3-NN. {n_jobs} Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. nn_ : estimator object Validated K-nearest Neighbours object created from `n_neighbors` parameter. nn_ver3_ : estimator object Validated K-nearest Neighbours object created from `n_neighbors_ver3` parameter. sample_indices_ : ndarray of shape (n_new_samples,) Indices of the samples selected. .. versionadded:: 0.4 n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- RandomUnderSampler : Random undersample the dataset. InstanceHardnessThreshold : Use of classifier to undersample a dataset. Notes ----- The methods are based on [1]_. Supports multi-class resampling. References ---------- .. [1] I. Mani, I. Zhang. "kNN approach to unbalanced data distributions: a case study involving information extraction," In Proceedings of workshop on learning from imbalanced datasets, 2003. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from imblearn.under_sampling import NearMiss >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> nm = NearMiss() >>> X_res, y_res = nm.fit_resample(X, y) >>> print('Resampled dataset shape %s' % Counter(y_res)) Resampled dataset shape Counter({{0: 100, 1: 100}}) """ _parameter_constraints: dict = { **BaseUnderSampler._parameter_constraints, "version": [Interval(numbers.Integral, 1, 3, closed="both")], "n_neighbors": [ Interval(numbers.Integral, 1, None, closed="left"), HasMethods(["kneighbors", "kneighbors_graph"]), ], "n_neighbors_ver3": [ Interval(numbers.Integral, 1, None, closed="left"), HasMethods(["kneighbors", "kneighbors_graph"]), ], "n_jobs": [numbers.Integral, None], } def __init__( self, *, sampling_strategy="auto", version=1, n_neighbors=3, n_neighbors_ver3=3, n_jobs=None, ): super().__init__(sampling_strategy=sampling_strategy) self.version = version self.n_neighbors = n_neighbors self.n_neighbors_ver3 = n_neighbors_ver3 self.n_jobs = n_jobs def _selection_dist_based( self, X, y, dist_vec, num_samples, key, sel_strategy="nearest" ): """Select the appropriate samples depending of the strategy selected. Parameters ---------- X : {array-like, sparse matrix}, shape (n_samples, n_features) Original samples. y : array-like, shape (n_samples,) Associated label to X. dist_vec : ndarray, shape (n_samples, ) The distance matrix to the nearest neigbour. num_samples: int The desired number of samples to select. key : str or int, The target class. sel_strategy : str, optional (default='nearest') Strategy to select the samples. Either 'nearest' or 'farthest' Returns ------- idx_sel : ndarray, shape (num_samples,) The list of the indices of the selected samples. """ # Compute the distance considering the farthest neighbour dist_avg_vec = np.sum(dist_vec[:, -self.nn_.n_neighbors :], axis=1) target_class_indices = np.flatnonzero(y == key) if dist_vec.shape[0] != _safe_indexing(X, target_class_indices).shape[0]: raise RuntimeError( "The samples to be selected do not correspond" " to the distance matrix given. Ensure that" " both `X[y == key]` and `dist_vec` are" " related." ) # Sort the list of distance and get the index if sel_strategy == "nearest": sort_way = False else: # sel_strategy == "farthest": sort_way = True sorted_idx = sorted( range(len(dist_avg_vec)), key=dist_avg_vec.__getitem__, reverse=sort_way, ) # Throw a warning to tell the user that we did not have enough samples # to select and that we just select everything if len(sorted_idx) < num_samples: warnings.warn( "The number of the samples to be selected is larger" " than the number of samples available. The" " balancing ratio cannot be ensure and all samples" " will be returned." ) # Select the desired number of samples return sorted_idx[:num_samples] def _validate_estimator(self): """Private function to create the NN estimator""" self.nn_ = check_neighbors_object("n_neighbors", self.n_neighbors) self.nn_.set_params(**{"n_jobs": self.n_jobs}) if self.version == 3: self.nn_ver3_ = check_neighbors_object( "n_neighbors_ver3", self.n_neighbors_ver3 ) self.nn_ver3_.set_params(**{"n_jobs": self.n_jobs}) def _fit_resample(self, X, y): self._validate_estimator() idx_under = np.empty((0,), dtype=int) target_stats = Counter(y) class_minority = min(target_stats, key=target_stats.get) minority_class_indices = np.flatnonzero(y == class_minority) self.nn_.fit(_safe_indexing(X, minority_class_indices)) for target_class in np.unique(y): if target_class in self.sampling_strategy_.keys(): n_samples = self.sampling_strategy_[target_class] target_class_indices = np.flatnonzero(y == target_class) X_class = _safe_indexing(X, target_class_indices) y_class = _safe_indexing(y, target_class_indices) if self.version == 1: dist_vec, idx_vec = self.nn_.kneighbors( X_class, n_neighbors=self.nn_.n_neighbors ) index_target_class = self._selection_dist_based( X, y, dist_vec, n_samples, target_class, sel_strategy="nearest", ) elif self.version == 2: dist_vec, idx_vec = self.nn_.kneighbors( X_class, n_neighbors=target_stats[class_minority] ) index_target_class = self._selection_dist_based( X, y, dist_vec, n_samples, target_class, sel_strategy="nearest", ) elif self.version == 3: self.nn_ver3_.fit(X_class) dist_vec, idx_vec = self.nn_ver3_.kneighbors( _safe_indexing(X, minority_class_indices) ) idx_vec_farthest = np.unique(idx_vec.reshape(-1)) X_class_selected = _safe_indexing(X_class, idx_vec_farthest) y_class_selected = _safe_indexing(y_class, idx_vec_farthest) dist_vec, idx_vec = self.nn_.kneighbors( X_class_selected, n_neighbors=self.nn_.n_neighbors ) index_target_class = self._selection_dist_based( X_class_selected, y_class_selected, dist_vec, n_samples, target_class, sel_strategy="farthest", ) # idx_tmp is relative to the feature selected in the # previous step and we need to find the indirection index_target_class = idx_vec_farthest[index_target_class] else: index_target_class = slice(None) idx_under = np.concatenate( ( idx_under, np.flatnonzero(y == target_class)[index_target_class], ), axis=0, ) self.sample_indices_ = idx_under return _safe_indexing(X, idx_under), _safe_indexing(y, idx_under) # fmt: off def _more_tags(self): return { "sample_indices": True, "_xfail_checks": { "check_samplers_fit_resample": "Fails for NearMiss-3 with less samples than expected" } } # fmt: on def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.sampler_tags.sample_indices = True return tags _neighbourhood_cleaning_rule.py000066400000000000000000000207371521136113700404350ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection"""Class performing under-sampling based on the neighbourhood cleaning rule.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numbers from collections import Counter import numpy as np from sklearn.base import clone from sklearn.neighbors import KNeighborsClassifier, NearestNeighbors from sklearn.utils import _safe_indexing from sklearn.utils._param_validation import HasMethods, Interval from imblearn.under_sampling._prototype_selection._edited_nearest_neighbours import ( EditedNearestNeighbours, ) from imblearn.under_sampling.base import BaseCleaningSampler from imblearn.utils import Substitution from imblearn.utils._docstring import _n_jobs_docstring SEL_KIND = ("all", "mode") @Substitution( sampling_strategy=BaseCleaningSampler._sampling_strategy_docstring, n_jobs=_n_jobs_docstring, ) class NeighbourhoodCleaningRule(BaseCleaningSampler): """Undersample based on the neighbourhood cleaning rule. This class uses ENN and a k-NN to remove noisy samples from the datasets. Read more in the :ref:`User Guide `. Parameters ---------- {sampling_strategy} edited_nearest_neighbours : estimator object, default=None The :class:`~imblearn.under_sampling.EditedNearestNeighbours` (ENN) object to clean the dataset. If `None`, a default ENN is created with `kind_sel="mode"` and `n_neighbors=n_neighbors`. n_neighbors : int or estimator object, default=3 If ``int``, size of the neighbourhood to consider to compute the K-nearest neighbors. If object, an estimator that inherits from :class:`~sklearn.neighbors.base.KNeighborsMixin` that will be used to find the nearest-neighbors. By default, it will be a 3-NN. threshold_cleaning : float, default=0.5 Threshold used to whether consider a class or not during the cleaning after applying ENN. A class will be considered during cleaning when: Ci > C x T , where Ci and C is the number of samples in the class and the data set, respectively and theta is the threshold. {n_jobs} Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. edited_nearest_neighbours_ : estimator object The edited nearest neighbour object used to make the first resampling. nn_ : estimator object Validated K-nearest Neighbours object created from `n_neighbors` parameter. classes_to_clean_ : list The classes considered with under-sampling by `nn_` in the second cleaning phase. sample_indices_ : ndarray of shape (n_new_samples,) Indices of the samples selected. .. versionadded:: 0.4 n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- EditedNearestNeighbours : Undersample by editing noisy samples. Notes ----- See the original paper: [1]_. Supports multi-class resampling. A one-vs.-rest scheme is used when sampling a class as proposed in [1]_. References ---------- .. [1] J. Laurikkala, "Improving identification of difficult small classes by balancing class distribution," Springer Berlin Heidelberg, 2001. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from imblearn.under_sampling import NeighbourhoodCleaningRule >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> ncr = NeighbourhoodCleaningRule() >>> X_res, y_res = ncr.fit_resample(X, y) >>> print('Resampled dataset shape %s' % Counter(y_res)) Resampled dataset shape Counter({{1: 888, 0: 100}}) """ _parameter_constraints: dict = { **BaseCleaningSampler._parameter_constraints, "edited_nearest_neighbours": [ HasMethods(["fit_resample"]), None, ], "n_neighbors": [ Interval(numbers.Integral, 1, None, closed="left"), HasMethods(["kneighbors", "kneighbors_graph"]), ], "threshold_cleaning": [Interval(numbers.Real, 0, None, closed="neither")], "n_jobs": [numbers.Integral, None], } def __init__( self, *, sampling_strategy="auto", edited_nearest_neighbours=None, n_neighbors=3, threshold_cleaning=0.5, n_jobs=None, ): super().__init__(sampling_strategy=sampling_strategy) self.edited_nearest_neighbours = edited_nearest_neighbours self.n_neighbors = n_neighbors self.threshold_cleaning = threshold_cleaning self.n_jobs = n_jobs def _validate_estimator(self): """Create the objects required by NCR.""" if isinstance(self.n_neighbors, numbers.Integral): self.nn_ = KNeighborsClassifier( n_neighbors=self.n_neighbors, n_jobs=self.n_jobs ) elif isinstance(self.n_neighbors, NearestNeighbors): # backward compatibility when passing a NearestNeighbors object self.nn_ = KNeighborsClassifier( n_neighbors=self.n_neighbors.n_neighbors - 1, n_jobs=self.n_jobs ) else: self.nn_ = clone(self.n_neighbors) if self.edited_nearest_neighbours is None: self.edited_nearest_neighbours_ = EditedNearestNeighbours( sampling_strategy=self.sampling_strategy, n_neighbors=self.n_neighbors, kind_sel="mode", n_jobs=self.n_jobs, ) else: self.edited_nearest_neighbours_ = clone(self.edited_nearest_neighbours) def _fit_resample(self, X, y): self._validate_estimator() self.edited_nearest_neighbours_.fit_resample(X, y) index_not_a1 = self.edited_nearest_neighbours_.sample_indices_ index_a1 = np.ones(y.shape, dtype=bool) index_a1[index_not_a1] = False index_a1 = np.flatnonzero(index_a1) # clean the neighborhood target_stats = Counter(y) class_minority = min(target_stats, key=target_stats.get) # compute which classes to consider for cleaning for the A2 group self.classes_to_clean_ = [ c for c, n_samples in target_stats.items() if ( c in self.sampling_strategy_.keys() and (n_samples > target_stats[class_minority] * self.threshold_cleaning) ) ] self.nn_.fit(X, y) class_minority_indices = np.flatnonzero(y == class_minority) X_minority = _safe_indexing(X, class_minority_indices) y_minority = _safe_indexing(y, class_minority_indices) y_pred_minority = self.nn_.predict(X_minority) # add an additional sample since the query points contains the original dataset neighbors_to_minority_indices = self.nn_.kneighbors( X_minority, n_neighbors=self.nn_.n_neighbors + 1, return_distance=False )[:, 1:] mask_misclassified_minority = y_pred_minority != y_minority index_a2 = np.ravel(neighbors_to_minority_indices[mask_misclassified_minority]) index_a2 = np.array( [ index for index in np.unique(index_a2) if y[index] in self.classes_to_clean_ ] ) union_a1_a2 = np.union1d(index_a1, index_a2).astype(int) selected_samples = np.ones(y.shape, dtype=bool) selected_samples[union_a1_a2] = False self.sample_indices_ = np.flatnonzero(selected_samples) return ( _safe_indexing(X, self.sample_indices_), _safe_indexing(y, self.sample_indices_), ) def _more_tags(self): return {"sample_indices": True} def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.sampler_tags.sample_indices = True return tags _one_sided_selection.py000066400000000000000000000171561521136113700367110ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection"""Class to perform under-sampling based on one-sided selection method.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numbers from collections import Counter import numpy as np from sklearn.base import clone from sklearn.neighbors import KNeighborsClassifier from sklearn.utils import _safe_indexing, check_random_state from sklearn.utils._param_validation import HasMethods, Interval from imblearn.under_sampling._prototype_selection._tomek_links import TomekLinks from imblearn.under_sampling.base import BaseCleaningSampler from imblearn.utils import Substitution from imblearn.utils._docstring import _n_jobs_docstring, _random_state_docstring @Substitution( sampling_strategy=BaseCleaningSampler._sampling_strategy_docstring, n_jobs=_n_jobs_docstring, random_state=_random_state_docstring, ) class OneSidedSelection(BaseCleaningSampler): """Class to perform under-sampling based on one-sided selection method. Read more in the :ref:`User Guide `. Parameters ---------- {sampling_strategy} {random_state} n_neighbors : int or estimator object, default=None If ``int``, size of the neighbourhood to consider to compute the nearest neighbors. If object, an estimator that inherits from :class:`~sklearn.neighbors.base.KNeighborsMixin` that will be used to find the nearest-neighbors. If `None`, a :class:`~sklearn.neighbors.KNeighborsClassifier` with a 1-NN rules will be used. n_seeds_S : int, default=1 Number of samples to extract in order to build the set S. {n_jobs} Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. estimators_ : list of estimator objects of shape (n_resampled_classes - 1,) Contains the K-nearest neighbor estimator used for per of classes. .. versionadded:: 0.12 sample_indices_ : ndarray of shape (n_new_samples,) Indices of the samples selected. .. versionadded:: 0.4 n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- EditedNearestNeighbours : Undersample by editing noisy samples. Notes ----- The method is based on [1]_. Supports multi-class resampling. A one-vs.-one scheme is used when sampling a class as proposed in [1]_. For each class to be sampled, all samples of this class and the minority class are used during the sampling procedure. References ---------- .. [1] M. Kubat, S. Matwin, "Addressing the curse of imbalanced training sets: one-sided selection," In ICML, vol. 97, pp. 179-186, 1997. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from imblearn.under_sampling import OneSidedSelection >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> oss = OneSidedSelection(random_state=42) >>> X_res, y_res = oss.fit_resample(X, y) >>> print('Resampled dataset shape %s' % Counter(y_res)) Resampled dataset shape Counter({{1: 496, 0: 100}}) """ _parameter_constraints: dict = { **BaseCleaningSampler._parameter_constraints, "n_neighbors": [ Interval(numbers.Integral, 1, None, closed="left"), HasMethods(["kneighbors", "kneighbors_graph"]), None, ], "n_seeds_S": [Interval(numbers.Integral, 1, None, closed="left")], "n_jobs": [numbers.Integral, None], "random_state": ["random_state"], } def __init__( self, *, sampling_strategy="auto", random_state=None, n_neighbors=None, n_seeds_S=1, n_jobs=None, ): super().__init__(sampling_strategy=sampling_strategy) self.random_state = random_state self.n_neighbors = n_neighbors self.n_seeds_S = n_seeds_S self.n_jobs = n_jobs def _validate_estimator(self): """Private function to create the NN estimator""" if self.n_neighbors is None: estimator = KNeighborsClassifier(n_neighbors=1, n_jobs=self.n_jobs) elif isinstance(self.n_neighbors, int): estimator = KNeighborsClassifier( n_neighbors=self.n_neighbors, n_jobs=self.n_jobs ) elif isinstance(self.n_neighbors, KNeighborsClassifier): estimator = clone(self.n_neighbors) return estimator def _fit_resample(self, X, y): estimator = self._validate_estimator() random_state = check_random_state(self.random_state) target_stats = Counter(y) class_minority = min(target_stats, key=target_stats.get) idx_under = np.empty((0,), dtype=int) self.estimators_ = [] for target_class in np.unique(y): if target_class in self.sampling_strategy_.keys(): # select a sample from the current class idx_maj = np.flatnonzero(y == target_class) sel_idx_maj = random_state.randint( low=0, high=target_stats[target_class], size=self.n_seeds_S ) idx_maj_sample = idx_maj[sel_idx_maj] minority_class_indices = np.flatnonzero(y == class_minority) C_indices = np.append(minority_class_indices, idx_maj_sample) # create the set composed of all minority samples and one # sample from the current class. C_x = _safe_indexing(X, C_indices) C_y = _safe_indexing(y, C_indices) # create the set S with removing the seed from S # since that it will be added anyway idx_maj_extracted = np.delete(idx_maj, sel_idx_maj, axis=0) S_x = _safe_indexing(X, idx_maj_extracted) S_y = _safe_indexing(y, idx_maj_extracted) self.estimators_.append(clone(estimator).fit(C_x, C_y)) pred_S_y = self.estimators_[-1].predict(S_x) S_misclassified_indices = np.flatnonzero(pred_S_y != S_y) idx_tmp = idx_maj_extracted[S_misclassified_indices] idx_under = np.concatenate((idx_under, idx_maj_sample, idx_tmp), axis=0) else: idx_under = np.concatenate( (idx_under, np.flatnonzero(y == target_class)), axis=0 ) X_resampled = _safe_indexing(X, idx_under) y_resampled = _safe_indexing(y, idx_under) # apply Tomek cleaning tl = TomekLinks(sampling_strategy=list(self.sampling_strategy_.keys())) X_cleaned, y_cleaned = tl.fit_resample(X_resampled, y_resampled) self.sample_indices_ = _safe_indexing(idx_under, tl.sample_indices_) return X_cleaned, y_cleaned def _more_tags(self): return {"sample_indices": True} def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.sampler_tags.sample_indices = True return tags _random_under_sampler.py000066400000000000000000000114521521136113700371040ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection"""Class to perform random under-sampling.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numpy as np from sklearn.utils import _safe_indexing, check_random_state from sklearn_compat.utils.validation import validate_data from imblearn.under_sampling.base import BaseUnderSampler from imblearn.utils import Substitution, check_target_type from imblearn.utils._docstring import _random_state_docstring from imblearn.utils._validation import _check_X @Substitution( sampling_strategy=BaseUnderSampler._sampling_strategy_docstring, random_state=_random_state_docstring, ) class RandomUnderSampler(BaseUnderSampler): """Class to perform random under-sampling. Under-sample the majority class(es) by randomly picking samples with or without replacement. Read more in the :ref:`User Guide `. Parameters ---------- {sampling_strategy} {random_state} replacement : bool, default=False Whether the sample is with or without replacement. Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. sample_indices_ : ndarray of shape (n_new_samples,) Indices of the samples selected. .. versionadded:: 0.4 n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- NearMiss : Undersample using near-miss samples. Notes ----- Supports multi-class resampling by sampling each class independently. Supports heterogeneous data as object array containing string and numeric data. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from imblearn.under_sampling import RandomUnderSampler >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> rus = RandomUnderSampler(random_state=42) >>> X_res, y_res = rus.fit_resample(X, y) >>> print('Resampled dataset shape %s' % Counter(y_res)) Resampled dataset shape Counter({{0: 100, 1: 100}}) """ _parameter_constraints: dict = { **BaseUnderSampler._parameter_constraints, "replacement": ["boolean"], "random_state": ["random_state"], } def __init__( self, *, sampling_strategy="auto", random_state=None, replacement=False ): super().__init__(sampling_strategy=sampling_strategy) self.random_state = random_state self.replacement = replacement def _check_X_y(self, X, y): y, binarize_y = check_target_type(y, indicate_one_vs_all=True) X = _check_X(X) X, y = validate_data(self, X=X, y=y, reset=True, skip_check_array=True) return X, y, binarize_y def _fit_resample(self, X, y): random_state = check_random_state(self.random_state) idx_under = np.empty((0,), dtype=int) for target_class in np.unique(y): if target_class in self.sampling_strategy_.keys(): n_samples = self.sampling_strategy_[target_class] index_target_class = random_state.choice( range(np.count_nonzero(y == target_class)), size=n_samples, replace=self.replacement, ) else: index_target_class = slice(None) idx_under = np.concatenate( ( idx_under, np.flatnonzero(y == target_class)[index_target_class], ), axis=0, ) self.sample_indices_ = idx_under return _safe_indexing(X, idx_under), _safe_indexing(y, idx_under) def _more_tags(self): return { "X_types": ["2darray", "string", "sparse", "dataframe"], "sample_indices": True, "allow_nan": True, "_xfail_checks": { "check_complex_data": "Robust to this type of data.", }, } def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.input_tags.allow_nan = True tags.input_tags.string = True tags.sampler_tags.sample_indices = True return tags _tomek_links.py000066400000000000000000000122541521136113700352240ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection"""Class to perform under-sampling by removing Tomek's links.""" # Authors: Guillaume Lemaitre # Fernando Nogueira # Christos Aridas # License: MIT import numbers import numpy as np from sklearn.neighbors import NearestNeighbors from sklearn.utils import _safe_indexing from imblearn.under_sampling.base import BaseCleaningSampler from imblearn.utils import Substitution from imblearn.utils._docstring import _n_jobs_docstring @Substitution( sampling_strategy=BaseCleaningSampler._sampling_strategy_docstring, n_jobs=_n_jobs_docstring, ) class TomekLinks(BaseCleaningSampler): """Under-sampling by removing Tomek's links. Read more in the :ref:`User Guide `. Parameters ---------- {sampling_strategy} {n_jobs} Attributes ---------- sampling_strategy_ : dict Dictionary containing the information to sample the dataset. The keys corresponds to the class labels from which to sample and the values are the number of samples to sample. sample_indices_ : ndarray of shape (n_new_samples,) Indices of the samples selected. .. versionadded:: 0.4 n_features_in_ : int Number of features in the input dataset. .. versionadded:: 0.9 feature_names_in_ : ndarray of shape (`n_features_in_`,) Names of features seen during `fit`. Defined only when `X` has feature names that are all strings. .. versionadded:: 0.10 See Also -------- EditedNearestNeighbours : Undersample by samples edition. CondensedNearestNeighbour : Undersample by samples condensation. RandomUnderSampler : Randomly under-sample the dataset. Notes ----- This method is based on [1]_. Supports multi-class resampling. A one-vs.-rest scheme is used as originally proposed in [1]_. References ---------- .. [1] I. Tomek, "Two modifications of CNN," In Systems, Man, and Cybernetics, IEEE Transactions on, vol. 6, pp 769-772, 1976. Examples -------- >>> from collections import Counter >>> from sklearn.datasets import make_classification >>> from imblearn.under_sampling import TomekLinks >>> X, y = make_classification(n_classes=2, class_sep=2, ... weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, ... n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) >>> print('Original dataset shape %s' % Counter(y)) Original dataset shape Counter({{1: 900, 0: 100}}) >>> tl = TomekLinks() >>> X_res, y_res = tl.fit_resample(X, y) >>> print('Resampled dataset shape %s' % Counter(y_res)) Resampled dataset shape Counter({{1: 897, 0: 100}}) """ _parameter_constraints: dict = { **BaseCleaningSampler._parameter_constraints, "n_jobs": [numbers.Integral, None], } def __init__(self, *, sampling_strategy="auto", n_jobs=None): super().__init__(sampling_strategy=sampling_strategy) self.n_jobs = n_jobs @staticmethod def is_tomek(y, nn_index, class_type): """Detect if samples are Tomek's link. More precisely, it uses the target vector and the first neighbour of every sample point and looks for Tomek pairs. Returning a boolean vector with True for majority Tomek links. Parameters ---------- y : ndarray of shape (n_samples,) Target vector of the data set, necessary to keep track of whether a sample belongs to minority or not. nn_index : ndarray of shape (len(y),) The index of the closes nearest neighbour to a sample point. class_type : int or str The label of the minority class. Returns ------- is_tomek : ndarray of shape (len(y), ) Boolean vector on len( # samples ), with True for majority samples that are Tomek links. """ links = np.zeros(len(y), dtype=bool) # find which class to not consider class_excluded = [c for c in np.unique(y) if c not in class_type] # there is a Tomek link between two samples if they are both nearest # neighbors of each others. for index_sample, target_sample in enumerate(y): if target_sample in class_excluded: continue if y[nn_index[index_sample]] != target_sample: if nn_index[nn_index[index_sample]] == index_sample: links[index_sample] = True return links def _fit_resample(self, X, y): # Find the nearest neighbour of every point nn = NearestNeighbors(n_neighbors=2, n_jobs=self.n_jobs) nn.fit(X) nns = nn.kneighbors(X, return_distance=False)[:, 1] links = self.is_tomek(y, nns, self.sampling_strategy_) self.sample_indices_ = np.flatnonzero(np.logical_not(links)) return ( _safe_indexing(X, self.sample_indices_), _safe_indexing(y, self.sample_indices_), ) def _more_tags(self): return {"sample_indices": True} def __sklearn_tags__(self): tags = super().__sklearn_tags__() tags.sampler_tags.sample_indices = True return tags scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection/tests/000077500000000000000000000000001521136113700334115ustar00rootroot00000000000000__init__.py000066400000000000000000000000001521136113700354310ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection/teststest_allknn.py000066400000000000000000000211051521136113700362210ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection/tests"""Test the module repeated edited nearest neighbour.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numpy as np import pytest from sklearn.datasets import make_classification from sklearn.neighbors import NearestNeighbors from sklearn.utils._testing import assert_allclose, assert_array_equal from imblearn.under_sampling import AllKNN X = np.array( [ [-0.12840393, 0.66446571], [1.32319756, -0.13181616], [0.04296502, -0.37981873], [0.83631853, 0.18569783], [1.02956816, 0.36061601], [1.12202806, 0.33811558], [-0.53171468, -0.53735182], [1.3381556, 0.35956356], [-0.35946678, 0.72510189], [1.32326943, 0.28393874], [2.94290565, -0.13986434], [0.28294738, -1.00125525], [0.34218094, -0.58781961], [-0.88864036, -0.33782387], [-1.10146139, 0.91782682], [-0.7969716, -0.50493969], [0.73489726, 0.43915195], [0.2096964, -0.61814058], [-0.28479268, 0.70459548], [1.84864913, 0.14729596], [1.59068979, -0.96622933], [0.73418199, -0.02222847], [0.50307437, 0.498805], [0.84929742, 0.41042894], [0.62649535, 0.46600596], [0.79270821, -0.41386668], [1.16606871, -0.25641059], [1.57356906, 0.30390519], [1.0304995, -0.16955962], [1.67314371, 0.19231498], [0.98382284, 0.37184502], [0.48921682, -1.38504507], [-0.46226554, -0.50481004], [-0.03918551, -0.68540745], [0.24991051, -1.00864997], [0.80541964, -0.34465185], [0.1732627, -1.61323172], [0.69804044, 0.44810796], [-0.5506368, -0.42072426], [-0.34474418, 0.21969797], ] ) Y = np.array( [ 1, 2, 2, 2, 1, 1, 0, 2, 1, 1, 1, 2, 2, 0, 1, 2, 1, 2, 1, 1, 2, 2, 1, 1, 1, 2, 2, 2, 2, 1, 1, 2, 0, 2, 2, 2, 2, 1, 2, 0, ] ) R_TOL = 1e-4 def test_allknn_fit_resample(): allknn = AllKNN() X_resampled, y_resampled = allknn.fit_resample(X, Y) X_gt = np.array( [ [-0.53171468, -0.53735182], [-0.88864036, -0.33782387], [-0.46226554, -0.50481004], [-0.34474418, 0.21969797], [1.02956816, 0.36061601], [1.12202806, 0.33811558], [-1.10146139, 0.91782682], [0.73489726, 0.43915195], [0.50307437, 0.498805], [0.84929742, 0.41042894], [0.62649535, 0.46600596], [0.98382284, 0.37184502], [0.69804044, 0.44810796], [0.04296502, -0.37981873], [0.28294738, -1.00125525], [0.34218094, -0.58781961], [0.2096964, -0.61814058], [1.59068979, -0.96622933], [0.73418199, -0.02222847], [0.79270821, -0.41386668], [1.16606871, -0.25641059], [1.0304995, -0.16955962], [0.48921682, -1.38504507], [-0.03918551, -0.68540745], [0.24991051, -1.00864997], [0.80541964, -0.34465185], [0.1732627, -1.61323172], ] ) y_gt = np.array( [ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ] ) assert_allclose(X_resampled, X_gt, rtol=R_TOL) assert_allclose(y_resampled, y_gt, rtol=R_TOL) def test_all_knn_allow_minority(): X, y = make_classification( n_samples=10000, n_features=2, n_informative=2, n_redundant=0, n_repeated=0, n_classes=3, n_clusters_per_class=1, weights=[0.2, 0.3, 0.5], class_sep=0.4, random_state=0, ) allknn = AllKNN(allow_minority=True) X_res_1, y_res_1 = allknn.fit_resample(X, y) allknn = AllKNN() X_res_2, y_res_2 = allknn.fit_resample(X, y) assert len(y_res_1) < len(y_res_2) def test_allknn_fit_resample_mode(): allknn = AllKNN(kind_sel="mode") X_resampled, y_resampled = allknn.fit_resample(X, Y) X_gt = np.array( [ [-0.53171468, -0.53735182], [-0.88864036, -0.33782387], [-0.46226554, -0.50481004], [-0.34474418, 0.21969797], [-0.12840393, 0.66446571], [1.02956816, 0.36061601], [1.12202806, 0.33811558], [-0.35946678, 0.72510189], [-1.10146139, 0.91782682], [0.73489726, 0.43915195], [-0.28479268, 0.70459548], [0.50307437, 0.498805], [0.84929742, 0.41042894], [0.62649535, 0.46600596], [0.98382284, 0.37184502], [0.69804044, 0.44810796], [1.32319756, -0.13181616], [0.04296502, -0.37981873], [0.28294738, -1.00125525], [0.34218094, -0.58781961], [0.2096964, -0.61814058], [1.59068979, -0.96622933], [0.73418199, -0.02222847], [0.79270821, -0.41386668], [1.16606871, -0.25641059], [1.0304995, -0.16955962], [0.48921682, -1.38504507], [-0.03918551, -0.68540745], [0.24991051, -1.00864997], [0.80541964, -0.34465185], [0.1732627, -1.61323172], ] ) y_gt = np.array( [ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ] ) assert_array_equal(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) def test_allknn_fit_resample_with_nn_object(): nn = NearestNeighbors(n_neighbors=4) allknn = AllKNN(n_neighbors=nn, kind_sel="mode") X_resampled, y_resampled = allknn.fit_resample(X, Y) X_gt = np.array( [ [-0.53171468, -0.53735182], [-0.88864036, -0.33782387], [-0.46226554, -0.50481004], [-0.34474418, 0.21969797], [-0.12840393, 0.66446571], [1.02956816, 0.36061601], [1.12202806, 0.33811558], [-0.35946678, 0.72510189], [-1.10146139, 0.91782682], [0.73489726, 0.43915195], [-0.28479268, 0.70459548], [0.50307437, 0.498805], [0.84929742, 0.41042894], [0.62649535, 0.46600596], [0.98382284, 0.37184502], [0.69804044, 0.44810796], [1.32319756, -0.13181616], [0.04296502, -0.37981873], [0.28294738, -1.00125525], [0.34218094, -0.58781961], [0.2096964, -0.61814058], [1.59068979, -0.96622933], [0.73418199, -0.02222847], [0.79270821, -0.41386668], [1.16606871, -0.25641059], [1.0304995, -0.16955962], [0.48921682, -1.38504507], [-0.03918551, -0.68540745], [0.24991051, -1.00864997], [0.80541964, -0.34465185], [0.1732627, -1.61323172], ] ) y_gt = np.array( [ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ] ) assert_array_equal(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) def test_alknn_not_good_object(): nn = "rnd" allknn = AllKNN(n_neighbors=nn, kind_sel="mode") with pytest.raises(ValueError): allknn.fit_resample(X, Y) test_condensed_nearest_neighbour.py000066400000000000000000000073761521136113700425050ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection/tests"""Test the module condensed nearest neighbour.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numpy as np import pytest from sklearn.datasets import make_classification from sklearn.neighbors import KNeighborsClassifier from sklearn.utils._testing import assert_array_equal from imblearn.under_sampling import CondensedNearestNeighbour RND_SEED = 0 X = np.array( [ [2.59928271, 0.93323465], [0.25738379, 0.95564169], [1.42772181, 0.526027], [1.92365863, 0.82718767], [-0.10903849, -0.12085181], [-0.284881, -0.62730973], [0.57062627, 1.19528323], [0.03394306, 0.03986753], [0.78318102, 2.59153329], [0.35831463, 1.33483198], [-0.14313184, -1.0412815], [0.01936241, 0.17799828], [-1.25020462, -0.40402054], [-0.09816301, -0.74662486], [-0.01252787, 0.34102657], [0.52726792, -0.38735648], [0.2821046, -0.07862747], [0.05230552, 0.09043907], [0.15198585, 0.12512646], [0.70524765, 0.39816382], ] ) Y = np.array([1, 2, 1, 1, 0, 2, 2, 2, 2, 2, 2, 0, 1, 2, 2, 2, 2, 1, 2, 1]) def test_cnn_init(): cnn = CondensedNearestNeighbour(random_state=RND_SEED) assert cnn.n_seeds_S == 1 assert cnn.n_jobs is None def test_cnn_fit_resample(): cnn = CondensedNearestNeighbour(random_state=RND_SEED) X_resampled, y_resampled = cnn.fit_resample(X, Y) X_gt = np.array( [ [-0.10903849, -0.12085181], [0.01936241, 0.17799828], [0.05230552, 0.09043907], [-1.25020462, -0.40402054], [0.70524765, 0.39816382], [0.35831463, 1.33483198], [-0.284881, -0.62730973], [0.03394306, 0.03986753], [-0.01252787, 0.34102657], [0.15198585, 0.12512646], ] ) y_gt = np.array([0, 0, 1, 1, 1, 2, 2, 2, 2, 2]) assert_array_equal(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) @pytest.mark.parametrize("n_neighbors", [1, KNeighborsClassifier(n_neighbors=1)]) def test_cnn_fit_resample_with_object(n_neighbors): cnn = CondensedNearestNeighbour(random_state=RND_SEED, n_neighbors=n_neighbors) X_resampled, y_resampled = cnn.fit_resample(X, Y) X_gt = np.array( [ [-0.10903849, -0.12085181], [0.01936241, 0.17799828], [0.05230552, 0.09043907], [-1.25020462, -0.40402054], [0.70524765, 0.39816382], [0.35831463, 1.33483198], [-0.284881, -0.62730973], [0.03394306, 0.03986753], [-0.01252787, 0.34102657], [0.15198585, 0.12512646], ] ) y_gt = np.array([0, 0, 1, 1, 1, 2, 2, 2, 2, 2]) assert_array_equal(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) cnn = CondensedNearestNeighbour(random_state=RND_SEED, n_neighbors=1) X_resampled, y_resampled = cnn.fit_resample(X, Y) assert_array_equal(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) def test_condensed_nearest_neighbour_multiclass(): """Check the validity of the fitted attributes `estimators_`.""" X, y = make_classification( n_samples=1_000, n_classes=4, weights=[0.1, 0.2, 0.2, 0.5], n_clusters_per_class=1, random_state=0, ) cnn = CondensedNearestNeighbour(random_state=RND_SEED) cnn.fit_resample(X, y) assert len(cnn.estimators_) == len(cnn.sampling_strategy_) other_classes = [] for est in cnn.estimators_: assert est.classes_[0] == 0 # minority class assert est.classes_[1] in {1, 2, 3} # other classes other_classes.append(est.classes_[1]) assert len(set(other_classes)) == len(other_classes) test_edited_nearest_neighbours.py000066400000000000000000000101641521136113700421510ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection/tests"""Test the module edited nearest neighbour.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numpy as np from sklearn.datasets import make_classification from sklearn.neighbors import NearestNeighbors from sklearn.utils._testing import assert_array_equal from imblearn.under_sampling import EditedNearestNeighbours X = np.array( [ [2.59928271, 0.93323465], [0.25738379, 0.95564169], [1.42772181, 0.526027], [1.92365863, 0.82718767], [-0.10903849, -0.12085181], [-0.284881, -0.62730973], [0.57062627, 1.19528323], [0.03394306, 0.03986753], [0.78318102, 2.59153329], [0.35831463, 1.33483198], [-0.14313184, -1.0412815], [0.01936241, 0.17799828], [-1.25020462, -0.40402054], [-0.09816301, -0.74662486], [-0.01252787, 0.34102657], [0.52726792, -0.38735648], [0.2821046, -0.07862747], [0.05230552, 0.09043907], [0.15198585, 0.12512646], [0.70524765, 0.39816382], ] ) Y = np.array([1, 2, 1, 1, 0, 2, 2, 2, 2, 2, 2, 0, 1, 2, 2, 2, 2, 1, 2, 1]) def test_enn_init(): enn = EditedNearestNeighbours() assert enn.n_neighbors == 3 assert enn.kind_sel == "all" assert enn.n_jobs is None def test_enn_fit_resample(): enn = EditedNearestNeighbours() X_resampled, y_resampled = enn.fit_resample(X, Y) X_gt = np.array( [ [-0.10903849, -0.12085181], [0.01936241, 0.17799828], [2.59928271, 0.93323465], [1.92365863, 0.82718767], [0.25738379, 0.95564169], [0.78318102, 2.59153329], [0.52726792, -0.38735648], ] ) y_gt = np.array([0, 0, 1, 1, 2, 2, 2]) assert_array_equal(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) def test_enn_fit_resample_mode(): enn = EditedNearestNeighbours(kind_sel="mode") X_resampled, y_resampled = enn.fit_resample(X, Y) X_gt = np.array( [ [-0.10903849, -0.12085181], [0.01936241, 0.17799828], [2.59928271, 0.93323465], [1.42772181, 0.526027], [1.92365863, 0.82718767], [0.25738379, 0.95564169], [-0.284881, -0.62730973], [0.57062627, 1.19528323], [0.78318102, 2.59153329], [0.35831463, 1.33483198], [-0.14313184, -1.0412815], [-0.09816301, -0.74662486], [0.52726792, -0.38735648], [0.2821046, -0.07862747], ] ) y_gt = np.array([0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2]) assert_array_equal(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) def test_enn_fit_resample_with_nn_object(): nn = NearestNeighbors(n_neighbors=4) enn = EditedNearestNeighbours(n_neighbors=nn, kind_sel="mode") X_resampled, y_resampled = enn.fit_resample(X, Y) X_gt = np.array( [ [-0.10903849, -0.12085181], [0.01936241, 0.17799828], [2.59928271, 0.93323465], [1.42772181, 0.526027], [1.92365863, 0.82718767], [0.25738379, 0.95564169], [-0.284881, -0.62730973], [0.57062627, 1.19528323], [0.78318102, 2.59153329], [0.35831463, 1.33483198], [-0.14313184, -1.0412815], [-0.09816301, -0.74662486], [0.52726792, -0.38735648], [0.2821046, -0.07862747], ] ) y_gt = np.array([0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2]) assert_array_equal(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) def test_enn_check_kind_selection(): """Check that `check_sel="all"` is more conservative than `check_sel="mode"`.""" X, y = make_classification( n_samples=1000, n_classes=2, weights=[0.3, 0.7], random_state=0, ) enn_all = EditedNearestNeighbours(kind_sel="all") enn_mode = EditedNearestNeighbours(kind_sel="mode") enn_all.fit_resample(X, y) enn_mode.fit_resample(X, y) assert enn_all.sample_indices_.size < enn_mode.sample_indices_.size test_instance_hardness_threshold.py000066400000000000000000000072561521136113700425240ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection/tests"""Test the module .""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numpy as np from sklearn.ensemble import GradientBoostingClassifier, RandomForestClassifier from sklearn.naive_bayes import GaussianNB as NB from sklearn.pipeline import make_pipeline from sklearn.utils._testing import assert_array_equal from imblearn.under_sampling import InstanceHardnessThreshold RND_SEED = 0 X = np.array( [ [-0.3879569, 0.6894251], [-0.09322739, 1.28177189], [-0.77740357, 0.74097941], [0.91542919, -0.65453327], [-0.03852113, 0.40910479], [-0.43877303, 1.07366684], [-0.85795321, 0.82980738], [-0.18430329, 0.52328473], [-0.30126957, -0.66268378], [-0.65571327, 0.42412021], [-0.28305528, 0.30284991], [0.20246714, -0.34727125], [1.06446472, -1.09279772], [0.30543283, -0.02589502], [-0.00717161, 0.00318087], ] ) Y = np.array([0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0]) ESTIMATOR = GradientBoostingClassifier(random_state=RND_SEED) def test_iht_init(): sampling_strategy = "auto" iht = InstanceHardnessThreshold( estimator=ESTIMATOR, sampling_strategy=sampling_strategy, random_state=RND_SEED, ) assert iht.sampling_strategy == sampling_strategy assert iht.random_state == RND_SEED def test_iht_fit_resample(): iht = InstanceHardnessThreshold(estimator=ESTIMATOR, random_state=RND_SEED) X_resampled, y_resampled = iht.fit_resample(X, Y) assert X_resampled.shape == (12, 2) assert y_resampled.shape == (12,) def test_iht_fit_resample_half(): sampling_strategy = {0: 3, 1: 3} iht = InstanceHardnessThreshold( estimator=NB(), sampling_strategy=sampling_strategy, random_state=RND_SEED, ) X_resampled, y_resampled = iht.fit_resample(X, Y) assert X_resampled.shape == (6, 2) assert y_resampled.shape == (6,) def test_iht_fit_resample_class_obj(): est = GradientBoostingClassifier(random_state=RND_SEED) iht = InstanceHardnessThreshold(estimator=est, random_state=RND_SEED) X_resampled, y_resampled = iht.fit_resample(X, Y) assert X_resampled.shape == (12, 2) assert y_resampled.shape == (12,) def test_iht_reproducibility(): from sklearn.datasets import load_digits X_digits, y_digits = load_digits(return_X_y=True) idx_sampled = [] for seed in range(5): est = RandomForestClassifier(n_estimators=10, random_state=seed) iht = InstanceHardnessThreshold(estimator=est, random_state=RND_SEED) iht.fit_resample(X_digits, y_digits) idx_sampled.append(iht.sample_indices_.copy()) for idx_1, idx_2 in zip(idx_sampled, idx_sampled[1:]): assert_array_equal(idx_1, idx_2) def test_iht_fit_resample_default_estimator(): iht = InstanceHardnessThreshold(estimator=None, random_state=RND_SEED) X_resampled, y_resampled = iht.fit_resample(X, Y) assert isinstance(iht.estimator_, RandomForestClassifier) assert X_resampled.shape == (12, 2) assert y_resampled.shape == (12,) def test_iht_estimator_pipeline(): """Check that we can pass a pipeline containing a classifier. Checking if we have a classifier should not be based on inheriting from `ClassifierMixin`. Non-regression test for: https://github.com/scikit-learn-contrib/imbalanced-learn/pull/1049 """ model = make_pipeline(GradientBoostingClassifier(random_state=RND_SEED)) iht = InstanceHardnessThreshold(estimator=model, random_state=RND_SEED) X_resampled, y_resampled = iht.fit_resample(X, Y) assert X_resampled.shape == (12, 2) assert y_resampled.shape == (12,) test_nearmiss.py000066400000000000000000000155151521136113700365730ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection/tests"""Test the module nearmiss.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numpy as np from sklearn.neighbors import NearestNeighbors from sklearn.utils._testing import assert_array_equal from imblearn.under_sampling import NearMiss X = np.array( [ [1.17737838, -0.2002118], [0.4960075, 0.86130762], [-0.05903827, 0.10947647], [0.91464286, 1.61369212], [-0.54619583, 1.73009918], [-0.60413357, 0.24628718], [0.45713638, 1.31069295], [-0.04032409, 3.01186964], [0.03142011, 0.12323596], [0.50701028, -0.17636928], [-0.80809175, -1.09917302], [-0.20497017, -0.26630228], [0.99272351, -0.11631728], [-1.95581933, 0.69609604], [1.15157493, -1.2981518], ] ) Y = np.array([1, 2, 1, 0, 2, 1, 2, 2, 1, 2, 0, 0, 2, 1, 2]) VERSION_NEARMISS = (1, 2, 3) def test_nm_fit_resample_auto(): sampling_strategy = "auto" X_gt = [ np.array( [ [0.91464286, 1.61369212], [-0.80809175, -1.09917302], [-0.20497017, -0.26630228], [-0.05903827, 0.10947647], [0.03142011, 0.12323596], [-0.60413357, 0.24628718], [0.50701028, -0.17636928], [0.4960075, 0.86130762], [0.45713638, 1.31069295], ] ), np.array( [ [0.91464286, 1.61369212], [-0.80809175, -1.09917302], [-0.20497017, -0.26630228], [-0.05903827, 0.10947647], [0.03142011, 0.12323596], [-0.60413357, 0.24628718], [0.50701028, -0.17636928], [0.4960075, 0.86130762], [0.45713638, 1.31069295], ] ), np.array( [ [0.91464286, 1.61369212], [-0.80809175, -1.09917302], [-0.20497017, -0.26630228], [1.17737838, -0.2002118], [-0.60413357, 0.24628718], [0.03142011, 0.12323596], [1.15157493, -1.2981518], [-0.54619583, 1.73009918], [0.99272351, -0.11631728], ] ), ] y_gt = [ np.array([0, 0, 0, 1, 1, 1, 2, 2, 2]), np.array([0, 0, 0, 1, 1, 1, 2, 2, 2]), np.array([0, 0, 0, 1, 1, 1, 2, 2, 2]), ] for version_idx, version in enumerate(VERSION_NEARMISS): nm = NearMiss(sampling_strategy=sampling_strategy, version=version) X_resampled, y_resampled = nm.fit_resample(X, Y) assert_array_equal(X_resampled, X_gt[version_idx]) assert_array_equal(y_resampled, y_gt[version_idx]) def test_nm_fit_resample_float_sampling_strategy(): sampling_strategy = {0: 3, 1: 4, 2: 4} X_gt = [ np.array( [ [-0.20497017, -0.26630228], [-0.80809175, -1.09917302], [0.91464286, 1.61369212], [-0.05903827, 0.10947647], [0.03142011, 0.12323596], [-0.60413357, 0.24628718], [1.17737838, -0.2002118], [0.50701028, -0.17636928], [0.4960075, 0.86130762], [0.45713638, 1.31069295], [0.99272351, -0.11631728], ] ), np.array( [ [-0.20497017, -0.26630228], [-0.80809175, -1.09917302], [0.91464286, 1.61369212], [-0.05903827, 0.10947647], [0.03142011, 0.12323596], [-0.60413357, 0.24628718], [1.17737838, -0.2002118], [0.50701028, -0.17636928], [0.4960075, 0.86130762], [0.45713638, 1.31069295], [0.99272351, -0.11631728], ] ), np.array( [ [0.91464286, 1.61369212], [-0.80809175, -1.09917302], [-0.20497017, -0.26630228], [1.17737838, -0.2002118], [-0.60413357, 0.24628718], [0.03142011, 0.12323596], [-0.05903827, 0.10947647], [1.15157493, -1.2981518], [-0.54619583, 1.73009918], [0.99272351, -0.11631728], [0.45713638, 1.31069295], ] ), ] y_gt = [ np.array([0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2]), np.array([0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2]), np.array([0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2]), ] for version_idx, version in enumerate(VERSION_NEARMISS): nm = NearMiss(sampling_strategy=sampling_strategy, version=version) X_resampled, y_resampled = nm.fit_resample(X, Y) assert_array_equal(X_resampled, X_gt[version_idx]) assert_array_equal(y_resampled, y_gt[version_idx]) def test_nm_fit_resample_nn_obj(): sampling_strategy = "auto" nn = NearestNeighbors(n_neighbors=3) X_gt = [ np.array( [ [0.91464286, 1.61369212], [-0.80809175, -1.09917302], [-0.20497017, -0.26630228], [-0.05903827, 0.10947647], [0.03142011, 0.12323596], [-0.60413357, 0.24628718], [0.50701028, -0.17636928], [0.4960075, 0.86130762], [0.45713638, 1.31069295], ] ), np.array( [ [0.91464286, 1.61369212], [-0.80809175, -1.09917302], [-0.20497017, -0.26630228], [-0.05903827, 0.10947647], [0.03142011, 0.12323596], [-0.60413357, 0.24628718], [0.50701028, -0.17636928], [0.4960075, 0.86130762], [0.45713638, 1.31069295], ] ), np.array( [ [0.91464286, 1.61369212], [-0.80809175, -1.09917302], [-0.20497017, -0.26630228], [1.17737838, -0.2002118], [-0.60413357, 0.24628718], [0.03142011, 0.12323596], [1.15157493, -1.2981518], [-0.54619583, 1.73009918], [0.99272351, -0.11631728], ] ), ] y_gt = [ np.array([0, 0, 0, 1, 1, 1, 2, 2, 2]), np.array([0, 0, 0, 1, 1, 1, 2, 2, 2]), np.array([0, 0, 0, 1, 1, 1, 2, 2, 2]), ] for version_idx, version in enumerate(VERSION_NEARMISS): nm = NearMiss( sampling_strategy=sampling_strategy, version=version, n_neighbors=nn, ) X_resampled, y_resampled = nm.fit_resample(X, Y) assert_array_equal(X_resampled, X_gt[version_idx]) assert_array_equal(y_resampled, y_gt[version_idx]) test_neighbourhood_cleaning_rule.py000066400000000000000000000043551521136113700424750ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection/tests"""Test the module neighbourhood cleaning rule.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT from collections import Counter import numpy as np import pytest from sklearn.datasets import make_classification from sklearn.utils._testing import assert_array_equal from imblearn.under_sampling import EditedNearestNeighbours, NeighbourhoodCleaningRule @pytest.fixture(scope="module") def data(): return make_classification( n_samples=200, n_features=2, n_informative=2, n_redundant=0, n_repeated=0, n_clusters_per_class=1, n_classes=3, weights=[0.1, 0.3, 0.6], random_state=0, ) def test_ncr_threshold_cleaning(data): """Test the effect of the `threshold_cleaning` parameter.""" X, y = data # with a large `threshold_cleaning`, the algorithm is equivalent to ENN enn = EditedNearestNeighbours() ncr = NeighbourhoodCleaningRule( edited_nearest_neighbours=enn, n_neighbors=10, threshold_cleaning=10 ) enn.fit_resample(X, y) ncr.fit_resample(X, y) assert_array_equal(np.sort(enn.sample_indices_), np.sort(ncr.sample_indices_)) assert ncr.classes_to_clean_ == [] # set a threshold that we should consider only the class #2 counter = Counter(y) threshold = counter[1] / counter[0] ncr.set_params(threshold_cleaning=threshold) ncr.fit_resample(X, y) assert set(ncr.classes_to_clean_) == {2} # making the threshold slightly smaller to take into account class #1 ncr.set_params(threshold_cleaning=threshold - np.finfo(np.float32).eps) ncr.fit_resample(X, y) assert set(ncr.classes_to_clean_) == {1, 2} def test_ncr_n_neighbors(data): """Check the effect of the NN on the cleaning of the second phase.""" X, y = data enn = EditedNearestNeighbours() ncr = NeighbourhoodCleaningRule(edited_nearest_neighbours=enn, n_neighbors=3) ncr.fit_resample(X, y) sample_indices_3_nn = ncr.sample_indices_ ncr.set_params(n_neighbors=10).fit_resample(X, y) sample_indices_10_nn = ncr.sample_indices_ # we should have a more aggressive cleaning with n_neighbors is larger assert len(sample_indices_3_nn) > len(sample_indices_10_nn) test_one_sided_selection.py000066400000000000000000000073441521136113700407510ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection/tests"""Test the module one-sided selection.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numpy as np import pytest from sklearn.datasets import make_classification from sklearn.neighbors import KNeighborsClassifier from sklearn.utils._testing import assert_array_equal from imblearn.under_sampling import OneSidedSelection RND_SEED = 0 X = np.array( [ [-0.3879569, 0.6894251], [-0.09322739, 1.28177189], [-0.77740357, 0.74097941], [0.91542919, -0.65453327], [-0.03852113, 0.40910479], [-0.43877303, 1.07366684], [-0.85795321, 0.82980738], [-0.18430329, 0.52328473], [-0.30126957, -0.66268378], [-0.65571327, 0.42412021], [-0.28305528, 0.30284991], [0.20246714, -0.34727125], [1.06446472, -1.09279772], [0.30543283, -0.02589502], [-0.00717161, 0.00318087], ] ) Y = np.array([0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0]) def test_oss_init(): oss = OneSidedSelection(random_state=RND_SEED) assert oss.n_seeds_S == 1 assert oss.n_jobs is None assert oss.random_state == RND_SEED def test_oss_fit_resample(): oss = OneSidedSelection(random_state=RND_SEED) X_resampled, y_resampled = oss.fit_resample(X, Y) X_gt = np.array( [ [-0.3879569, 0.6894251], [0.91542919, -0.65453327], [-0.65571327, 0.42412021], [1.06446472, -1.09279772], [0.30543283, -0.02589502], [-0.00717161, 0.00318087], [-0.09322739, 1.28177189], [-0.77740357, 0.74097941], [-0.43877303, 1.07366684], [-0.85795321, 0.82980738], [-0.30126957, -0.66268378], [0.20246714, -0.34727125], ] ) y_gt = np.array([0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]) assert_array_equal(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) @pytest.mark.parametrize("n_neighbors", [1, KNeighborsClassifier(n_neighbors=1)]) def test_oss_with_object(n_neighbors): oss = OneSidedSelection(random_state=RND_SEED, n_neighbors=n_neighbors) X_resampled, y_resampled = oss.fit_resample(X, Y) X_gt = np.array( [ [-0.3879569, 0.6894251], [0.91542919, -0.65453327], [-0.65571327, 0.42412021], [1.06446472, -1.09279772], [0.30543283, -0.02589502], [-0.00717161, 0.00318087], [-0.09322739, 1.28177189], [-0.77740357, 0.74097941], [-0.43877303, 1.07366684], [-0.85795321, 0.82980738], [-0.30126957, -0.66268378], [0.20246714, -0.34727125], ] ) y_gt = np.array([0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]) assert_array_equal(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) knn = 1 oss = OneSidedSelection(random_state=RND_SEED, n_neighbors=knn) X_resampled, y_resampled = oss.fit_resample(X, Y) assert_array_equal(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) def test_one_sided_selection_multiclass(): """Check the validity of the fitted attributes `estimators_`.""" X, y = make_classification( n_samples=1_000, n_classes=4, weights=[0.1, 0.2, 0.2, 0.5], n_clusters_per_class=1, random_state=0, ) oss = OneSidedSelection(random_state=RND_SEED) oss.fit_resample(X, y) assert len(oss.estimators_) == len(oss.sampling_strategy_) other_classes = [] for est in oss.estimators_: assert est.classes_[0] == 0 # minority class assert est.classes_[1] in {1, 2, 3} # other classes other_classes.append(est.classes_[1]) assert len(set(other_classes)) == len(other_classes) test_random_under_sampler.py000066400000000000000000000127121521136113700411460ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection/tests"""Test the module random under sampler.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT from collections import Counter from datetime import datetime import numpy as np import pytest from sklearn.datasets import make_classification from sklearn.utils._testing import assert_array_equal from imblearn.under_sampling import RandomUnderSampler RND_SEED = 0 X = np.array( [ [0.04352327, -0.20515826], [0.92923648, 0.76103773], [0.20792588, 1.49407907], [0.47104475, 0.44386323], [0.22950086, 0.33367433], [0.15490546, 0.3130677], [0.09125309, -0.85409574], [0.12372842, 0.6536186], [0.13347175, 0.12167502], [0.094035, -2.55298982], ] ) Y = np.array([1, 0, 1, 0, 1, 1, 1, 1, 0, 1]) @pytest.mark.parametrize("as_frame", [True, False], ids=["dataframe", "array"]) def test_rus_fit_resample(as_frame): if as_frame: pd = pytest.importorskip("pandas") X_ = pd.DataFrame(X) else: X_ = X rus = RandomUnderSampler(random_state=RND_SEED, replacement=True) X_resampled, y_resampled = rus.fit_resample(X_, Y) X_gt = np.array( [ [0.92923648, 0.76103773], [0.47104475, 0.44386323], [0.13347175, 0.12167502], [0.09125309, -0.85409574], [0.12372842, 0.6536186], [0.04352327, -0.20515826], ] ) y_gt = np.array([0, 0, 0, 1, 1, 1]) if as_frame: assert hasattr(X_resampled, "loc") X_resampled = X_resampled.to_numpy() assert_array_equal(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) def test_rus_fit_resample_half(): sampling_strategy = {0: 3, 1: 6} rus = RandomUnderSampler( sampling_strategy=sampling_strategy, random_state=RND_SEED, replacement=True, ) X_resampled, y_resampled = rus.fit_resample(X, Y) X_gt = np.array( [ [0.92923648, 0.76103773], [0.47104475, 0.44386323], [0.92923648, 0.76103773], [0.15490546, 0.3130677], [0.15490546, 0.3130677], [0.15490546, 0.3130677], [0.20792588, 1.49407907], [0.15490546, 0.3130677], [0.12372842, 0.6536186], ] ) y_gt = np.array([0, 0, 0, 1, 1, 1, 1, 1, 1]) assert_array_equal(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) def test_multiclass_fit_resample(): y = Y.copy() y[5] = 2 y[6] = 2 rus = RandomUnderSampler(random_state=RND_SEED) X_resampled, y_resampled = rus.fit_resample(X, y) count_y_res = Counter(y_resampled) assert count_y_res[0] == 2 assert count_y_res[1] == 2 assert count_y_res[2] == 2 def test_random_under_sampling_heterogeneous_data(): X_hetero = np.array( [["xxx", 1, 1.0], ["yyy", 2, 2.0], ["zzz", 3, 3.0]], dtype=object ) y = np.array([0, 0, 1]) rus = RandomUnderSampler(random_state=RND_SEED) X_res, y_res = rus.fit_resample(X_hetero, y) assert X_res.shape[0] == 2 assert y_res.shape[0] == 2 assert X_res.dtype == object def test_random_under_sampling_nan_inf(): # check that we can undersample even with missing or infinite data # regression tests for #605 rng = np.random.RandomState(42) n_not_finite = X.shape[0] // 3 row_indices = rng.choice(np.arange(X.shape[0]), size=n_not_finite) col_indices = rng.randint(0, X.shape[1], size=n_not_finite) not_finite_values = rng.choice([np.nan, np.inf], size=n_not_finite) X_ = X.copy() X_[row_indices, col_indices] = not_finite_values rus = RandomUnderSampler(random_state=0) X_res, y_res = rus.fit_resample(X_, Y) assert y_res.shape == (6,) assert X_res.shape == (6, 2) assert np.any(~np.isfinite(X_res)) @pytest.mark.parametrize( "sampling_strategy", ["auto", "majority", "not minority", "not majority", "all"] ) def test_random_under_sampler_strings(sampling_strategy): """Check that we support all supposed strings as `sampling_strategy` in a sampler inheriting from `BaseUnderSampler`.""" X, y = make_classification( n_samples=100, n_clusters_per_class=1, n_classes=3, weights=[0.1, 0.3, 0.6], random_state=0, ) RandomUnderSampler(sampling_strategy=sampling_strategy).fit_resample(X, y) def test_random_under_sampling_datetime(): """Check that we don't convert input data and only sample from it.""" pd = pytest.importorskip("pandas") X = pd.DataFrame({"label": [0, 0, 0, 1], "td": [datetime.now()] * 4}) y = X["label"] rus = RandomUnderSampler(random_state=0) X_res, y_res = rus.fit_resample(X, y) pd.testing.assert_series_equal(X_res.dtypes, X.dtypes) pd.testing.assert_index_equal(X_res.index, y_res.index) assert_array_equal(y_res.to_numpy(), np.array([0, 1])) def test_random_under_sampler_full_nat(): """Check that we can return timedelta columns full of NaT. Non-regression test for: https://github.com/scikit-learn-contrib/imbalanced-learn/issues/1055 """ pd = pytest.importorskip("pandas") X = pd.DataFrame( { "col_str": ["abc", "def", "xyz"], "col_timedelta": pd.to_timedelta([np.nan, np.nan, np.nan]), } ) y = np.array([0, 0, 1]) X_res, y_res = RandomUnderSampler().fit_resample(X, y) assert X_res.shape == (2, 2) assert y_res.shape == (2,) assert X_res["col_timedelta"].dtype.kind == "m" # timedelta test_repeated_edited_nearest_neighbours.py000066400000000000000000000210271521136113700440220ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection/tests"""Test the module repeated edited nearest neighbour.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numpy as np import pytest from sklearn.neighbors import NearestNeighbors from sklearn.utils._testing import assert_array_equal from imblearn.under_sampling import RepeatedEditedNearestNeighbours X = np.array( [ [-0.12840393, 0.66446571], [1.32319756, -0.13181616], [0.04296502, -0.37981873], [0.83631853, 0.18569783], [1.02956816, 0.36061601], [1.12202806, 0.33811558], [-0.53171468, -0.53735182], [1.3381556, 0.35956356], [-0.35946678, 0.72510189], [1.32326943, 0.28393874], [2.94290565, -0.13986434], [0.28294738, -1.00125525], [0.34218094, -0.58781961], [-0.88864036, -0.33782387], [-1.10146139, 0.91782682], [-0.7969716, -0.50493969], [0.73489726, 0.43915195], [0.2096964, -0.61814058], [-0.28479268, 0.70459548], [1.84864913, 0.14729596], [1.59068979, -0.96622933], [0.73418199, -0.02222847], [0.50307437, 0.498805], [0.84929742, 0.41042894], [0.62649535, 0.46600596], [0.79270821, -0.41386668], [1.16606871, -0.25641059], [1.57356906, 0.30390519], [1.0304995, -0.16955962], [1.67314371, 0.19231498], [0.98382284, 0.37184502], [0.48921682, -1.38504507], [-0.46226554, -0.50481004], [-0.03918551, -0.68540745], [0.24991051, -1.00864997], [0.80541964, -0.34465185], [0.1732627, -1.61323172], [0.69804044, 0.44810796], [-0.5506368, -0.42072426], [-0.34474418, 0.21969797], ] ) Y = np.array( [ 1, 2, 2, 2, 1, 1, 0, 2, 1, 1, 1, 2, 2, 0, 1, 2, 1, 2, 1, 1, 2, 2, 1, 1, 1, 2, 2, 2, 2, 1, 1, 2, 0, 2, 2, 2, 2, 1, 2, 0, ] ) def test_renn_init(): renn = RepeatedEditedNearestNeighbours() assert renn.n_neighbors == 3 assert renn.kind_sel == "all" assert renn.n_jobs is None def test_renn_iter_wrong(): max_iter = -1 renn = RepeatedEditedNearestNeighbours(max_iter=max_iter) with pytest.raises(ValueError): renn.fit_resample(X, Y) def test_renn_fit_resample(): renn = RepeatedEditedNearestNeighbours() X_resampled, y_resampled = renn.fit_resample(X, Y) X_gt = np.array( [ [-0.53171468, -0.53735182], [-0.88864036, -0.33782387], [-0.46226554, -0.50481004], [-0.34474418, 0.21969797], [1.02956816, 0.36061601], [1.12202806, 0.33811558], [0.73489726, 0.43915195], [0.50307437, 0.498805], [0.84929742, 0.41042894], [0.62649535, 0.46600596], [0.98382284, 0.37184502], [0.69804044, 0.44810796], [0.04296502, -0.37981873], [0.28294738, -1.00125525], [0.34218094, -0.58781961], [0.2096964, -0.61814058], [1.59068979, -0.96622933], [0.73418199, -0.02222847], [0.79270821, -0.41386668], [1.16606871, -0.25641059], [1.0304995, -0.16955962], [0.48921682, -1.38504507], [-0.03918551, -0.68540745], [0.24991051, -1.00864997], [0.80541964, -0.34465185], [0.1732627, -1.61323172], ] ) y_gt = np.array( [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] ) assert_array_equal(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) assert 0 < renn.n_iter_ <= renn.max_iter def test_renn_fit_resample_mode_object(): renn = RepeatedEditedNearestNeighbours(kind_sel="mode") X_resampled, y_resampled = renn.fit_resample(X, Y) X_gt = np.array( [ [-0.53171468, -0.53735182], [-0.88864036, -0.33782387], [-0.46226554, -0.50481004], [-0.34474418, 0.21969797], [-0.12840393, 0.66446571], [1.02956816, 0.36061601], [1.12202806, 0.33811558], [-0.35946678, 0.72510189], [2.94290565, -0.13986434], [-1.10146139, 0.91782682], [0.73489726, 0.43915195], [-0.28479268, 0.70459548], [1.84864913, 0.14729596], [0.50307437, 0.498805], [0.84929742, 0.41042894], [0.62649535, 0.46600596], [1.67314371, 0.19231498], [0.98382284, 0.37184502], [0.69804044, 0.44810796], [1.32319756, -0.13181616], [0.04296502, -0.37981873], [0.28294738, -1.00125525], [0.34218094, -0.58781961], [0.2096964, -0.61814058], [1.59068979, -0.96622933], [0.73418199, -0.02222847], [0.79270821, -0.41386668], [1.16606871, -0.25641059], [1.0304995, -0.16955962], [0.48921682, -1.38504507], [-0.03918551, -0.68540745], [0.24991051, -1.00864997], [0.80541964, -0.34465185], [0.1732627, -1.61323172], ] ) y_gt = np.array( [ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ] ) assert_array_equal(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) assert 0 < renn.n_iter_ <= renn.max_iter def test_renn_fit_resample_mode(): nn = NearestNeighbors(n_neighbors=4) renn = RepeatedEditedNearestNeighbours(n_neighbors=nn, kind_sel="mode") X_resampled, y_resampled = renn.fit_resample(X, Y) X_gt = np.array( [ [-0.53171468, -0.53735182], [-0.88864036, -0.33782387], [-0.46226554, -0.50481004], [-0.34474418, 0.21969797], [-0.12840393, 0.66446571], [1.02956816, 0.36061601], [1.12202806, 0.33811558], [-0.35946678, 0.72510189], [2.94290565, -0.13986434], [-1.10146139, 0.91782682], [0.73489726, 0.43915195], [-0.28479268, 0.70459548], [1.84864913, 0.14729596], [0.50307437, 0.498805], [0.84929742, 0.41042894], [0.62649535, 0.46600596], [1.67314371, 0.19231498], [0.98382284, 0.37184502], [0.69804044, 0.44810796], [1.32319756, -0.13181616], [0.04296502, -0.37981873], [0.28294738, -1.00125525], [0.34218094, -0.58781961], [0.2096964, -0.61814058], [1.59068979, -0.96622933], [0.73418199, -0.02222847], [0.79270821, -0.41386668], [1.16606871, -0.25641059], [1.0304995, -0.16955962], [0.48921682, -1.38504507], [-0.03918551, -0.68540745], [0.24991051, -1.00864997], [0.80541964, -0.34465185], [0.1732627, -1.61323172], ] ) y_gt = np.array( [ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, ] ) assert_array_equal(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) assert 0 < renn.n_iter_ <= renn.max_iter @pytest.mark.parametrize( "max_iter, n_iter", [(2, 2), (5, 3)], ) def test_renn_iter_attribute(max_iter, n_iter): renn = RepeatedEditedNearestNeighbours(max_iter=max_iter) renn.fit_resample(X, Y) assert renn.n_iter_ == n_iter test_tomek_links.py000066400000000000000000000052121521136113700372620ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/_prototype_selection/tests"""Test the module Tomek's links.""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numpy as np import pytest from sklearn.datasets import make_classification from sklearn.utils._testing import assert_array_equal from imblearn.under_sampling import TomekLinks X = np.array( [ [0.31230513, 0.1216318], [0.68481731, 0.51935141], [1.34192108, -0.13367336], [0.62366841, -0.21312976], [1.61091956, -0.40283504], [-0.37162401, -2.19400981], [0.74680821, 1.63827342], [0.2184254, 0.24299982], [0.61472253, -0.82309052], [0.19893132, -0.47761769], [1.06514042, -0.0770537], [0.97407872, 0.44454207], [1.40301027, -0.83648734], [-1.20515198, -1.02689695], [-0.27410027, -0.54194484], [0.8381014, 0.44085498], [-0.23374509, 0.18370049], [-0.32635887, -0.29299653], [-0.00288378, 0.84259929], [1.79580611, -0.02219234], ] ) Y = np.array([1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0]) def test_tl_init(): tl = TomekLinks() assert tl.n_jobs is None def test_tl_fit_resample(): tl = TomekLinks() X_resampled, y_resampled = tl.fit_resample(X, Y) X_gt = np.array( [ [0.31230513, 0.1216318], [0.68481731, 0.51935141], [1.34192108, -0.13367336], [0.62366841, -0.21312976], [1.61091956, -0.40283504], [-0.37162401, -2.19400981], [0.74680821, 1.63827342], [0.2184254, 0.24299982], [0.61472253, -0.82309052], [0.19893132, -0.47761769], [0.97407872, 0.44454207], [1.40301027, -0.83648734], [-1.20515198, -1.02689695], [-0.23374509, 0.18370049], [-0.32635887, -0.29299653], [-0.00288378, 0.84259929], [1.79580611, -0.02219234], ] ) y_gt = np.array([1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0]) assert_array_equal(X_resampled, X_gt) assert_array_equal(y_resampled, y_gt) @pytest.mark.parametrize( "sampling_strategy", ["auto", "majority", "not minority", "not majority", "all"] ) def test_tomek_links_strings(sampling_strategy): """Check that we support all supposed strings as `sampling_strategy` in a sampler inheriting from `BaseCleaningSampler`.""" X, y = make_classification( n_samples=100, n_clusters_per_class=1, n_classes=3, weights=[0.1, 0.3, 0.6], random_state=0, ) TomekLinks(sampling_strategy=sampling_strategy).fit_resample(X, y) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/under_sampling/base.py000066400000000000000000000075361521136113700272750ustar00rootroot00000000000000""" Base class for the under-sampling method. """ # Authors: Guillaume Lemaitre # License: MIT import numbers from collections.abc import Mapping from sklearn.utils._param_validation import Interval, StrOptions from imblearn.base import BaseSampler class BaseUnderSampler(BaseSampler): """Base class for under-sampling algorithms. Warning: This class should not be used directly. Use the derive classes instead. """ _sampling_type = "under-sampling" _sampling_strategy_docstring = ( """sampling_strategy : float, str, dict, callable, default='auto' Sampling information to sample the data set. - When ``float``, it corresponds to the desired ratio of the number of samples in the minority class over the number of samples in the majority class after resampling. Therefore, the ratio is expressed as :math:`\\alpha_{us} = N_{m} / N_{rM}` where :math:`N_{m}` is the number of samples in the minority class and :math:`N_{rM}` is the number of samples in the majority class after resampling. .. warning:: ``float`` is only available for **binary** classification. An error is raised for multi-class classification. - When ``str``, specify the class targeted by the resampling. The number of samples in the different classes will be equalized. Possible choices are: ``'majority'``: resample only the majority class; ``'not minority'``: resample all classes but the minority class; ``'not majority'``: resample all classes but the majority class; ``'all'``: resample all classes; ``'auto'``: equivalent to ``'not minority'``. - When ``dict``, the keys correspond to the targeted classes. The values correspond to the desired number of samples for each targeted class. - When callable, function taking ``y`` and returns a ``dict``. The keys correspond to the targeted classes. The values correspond to the desired number of samples for each class. """.rstrip() ) # noqa: E501 _parameter_constraints: dict = { "sampling_strategy": [ Interval(numbers.Real, 0, 1, closed="right"), StrOptions({"auto", "majority", "not minority", "not majority", "all"}), Mapping, callable, ], } class BaseCleaningSampler(BaseSampler): """Base class for under-sampling algorithms. Warning: This class should not be used directly. Use the derive classes instead. """ _sampling_type = "clean-sampling" _sampling_strategy_docstring = """sampling_strategy : str, list or callable Sampling information to sample the data set. - When ``str``, specify the class targeted by the resampling. Note the the number of samples will not be equal in each. Possible choices are: ``'majority'``: resample only the majority class; ``'not minority'``: resample all classes but the minority class; ``'not majority'``: resample all classes but the majority class; ``'all'``: resample all classes; ``'auto'``: equivalent to ``'not minority'``. - When ``list``, the list contains the classes targeted by the resampling. - When callable, function taking ``y`` and returns a ``dict``. The keys correspond to the targeted classes. The values correspond to the desired number of samples for each class. """.rstrip() _parameter_constraints: dict = { "sampling_strategy": [ Interval(numbers.Real, 0, 1, closed="right"), StrOptions({"auto", "majority", "not minority", "not majority", "all"}), list, callable, ], } scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/000077500000000000000000000000001521136113700241275ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/__init__.py000066400000000000000000000005551521136113700262450ustar00rootroot00000000000000""" The :mod:`imblearn.utils` module includes various utilities. """ from imblearn.utils._docstring import Substitution from imblearn.utils._validation import ( check_neighbors_object, check_sampling_strategy, check_target_type, ) __all__ = [ "check_neighbors_object", "check_sampling_strategy", "check_target_type", "Substitution", ] scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/_docstring.py000066400000000000000000000027501521136113700266400ustar00rootroot00000000000000"""Utilities for docstring in imbalanced-learn.""" # Authors: Guillaume Lemaitre # License: MIT class Substitution: """Decorate a function's or a class' docstring to perform string substitution on it. This decorator should be robust even if obj.__doc__ is None (for example, if -OO was passed to the interpreter) """ def __init__(self, *args, **kwargs): if args and kwargs: raise AssertionError("Only positional or keyword args are allowed") self.params = args or kwargs def __call__(self, obj): if obj.__doc__: obj.__doc__ = obj.__doc__.format(**self.params) return obj _random_state_docstring = """random_state : int, RandomState instance, default=None Control the randomization of the algorithm. - If int, ``random_state`` is the seed used by the random number generator; - If ``RandomState`` instance, random_state is the random number generator; - If ``None``, the random number generator is the ``RandomState`` instance used by ``np.random``. """.rstrip() _n_jobs_docstring = """n_jobs : int, default=None Number of CPU cores used during the cross-validation loop. ``None`` means 1 unless in a :obj:`joblib.parallel_backend` context. ``-1`` means using all processors. See `Glossary `_ for more details. """.rstrip() scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/_show_versions.py000066400000000000000000000042061521136113700275520ustar00rootroot00000000000000""" Utility method which prints system info to help with debugging, and filing issues on GitHub. Adapted from :func:`sklearn.show_versions`, which was adapted from :func:`pandas.show_versions` """ # Author: Alexander L. Hayes # License: MIT from imblearn import __version__ def _get_deps_info(): """Overview of the installed version of main dependencies Returns ------- deps_info: dict version information on relevant Python libraries """ deps = [ "imbalanced-learn", "pip", "setuptools", "numpy", "scipy", "scikit-learn", "Cython", "pandas", "keras", "tensorflow", "joblib", ] deps_info = { "imbalanced-learn": __version__, } from importlib.metadata import PackageNotFoundError, version for modname in deps: try: deps_info[modname] = version(modname) except PackageNotFoundError: deps_info[modname] = None return deps_info def show_versions(github=False): """Print debugging information. .. versionadded:: 0.5 Parameters ---------- github : bool, If true, wrap system info with GitHub markup. """ from sklearn.utils._show_versions import _get_sys_info _sys_info = _get_sys_info() _deps_info = _get_deps_info() _github_markup = ( "
" "System, Dependency Information\n\n" "**System Information**\n\n" "{0}\n" "**Python Dependencies**\n\n" "{1}\n" "
" ) if github: _sys_markup = "" _deps_markup = "" for k, stat in _sys_info.items(): _sys_markup += f"* {k:<10}: `{stat}`\n" for k, stat in _deps_info.items(): _deps_markup += f"* {k:<10}: `{stat}`\n" print(_github_markup.format(_sys_markup, _deps_markup)) else: print("\nSystem:") for k, stat in _sys_info.items(): print(f"{k:>11}: {stat}") print("\nPython dependencies:") for k, stat in _deps_info.items(): print(f"{k:>11}: {stat}") scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/_tags.py000066400000000000000000000204031521136113700255750ustar00rootroot00000000000000from dataclasses import dataclass, field from sklearn_compat.utils._tags import ( ClassifierTags, RegressorTags, TargetTags, TransformerTags, ) from sklearn_compat.utils._tags import ( InputTags as SklearnInputTags, ) # tags infrastructure def _dataclass_args(): return {"slots": True} @dataclass(**_dataclass_args()) class InputTags(SklearnInputTags): """Tags for the input data. Parameters ---------- one_d_array : bool, default=False Whether the input can be a 1D array. two_d_array : bool, default=True Whether the input can be a 2D array. Note that most common tests currently run only if this flag is set to ``True``. three_d_array : bool, default=False Whether the input can be a 3D array. sparse : bool, default=False Whether the input can be a sparse matrix. categorical : bool, default=False Whether the input can be categorical. string : bool, default=False Whether the input can be an array-like of strings. dict : bool, default=False Whether the input can be a dictionary. positive_only : bool, default=False Whether the estimator requires positive X. allow_nan : bool, default=False Whether the estimator supports data with missing values encoded as `np.nan`. pairwise : bool, default=False This boolean attribute indicates whether the data (`X`), :term:`fit` and similar methods consists of pairwise measures over samples rather than a feature representation for each sample. It is usually `True` where an estimator has a `metric` or `affinity` or `kernel` parameter with value 'precomputed'. Its primary purpose is to support a :term:`meta-estimator` or a cross validation procedure that extracts a sub-sample of data intended for a pairwise estimator, where the data needs to be indexed on both axes. Specifically, this tag is used by `sklearn.utils.metaestimators._safe_split` to slice rows and columns. """ one_d_array: bool = False two_d_array: bool = True three_d_array: bool = False sparse: bool = False categorical: bool = False string: bool = False dict: bool = False positive_only: bool = False allow_nan: bool = False pairwise: bool = False dataframe: bool = False @dataclass(**_dataclass_args()) class SamplerTags: """Tags for the sampler. Parameters ---------- sample_indices : bool, default=False Whether the sampler returns the indices of the samples that were selected. """ sample_indices: bool = False @dataclass(**_dataclass_args()) class Tags: """Tags for the estimator. See :ref:`estimator_tags` for more information. Parameters ---------- estimator_type : str or None The type of the estimator. Can be one of: - "classifier" - "regressor" - "transformer" - "clusterer" - "outlier_detector" - "density_estimator" target_tags : :class:`TargetTags` The target(y) tags. transformer_tags : :class:`TransformerTags` or None The transformer tags. classifier_tags : :class:`ClassifierTags` or None The classifier tags. regressor_tags : :class:`RegressorTags` or None The regressor tags. array_api_support : bool, default=False Whether the estimator supports Array API compatible inputs. no_validation : bool, default=False Whether the estimator skips input-validation. This is only meant for stateless and dummy transformers! non_deterministic : bool, default=False Whether the estimator is not deterministic given a fixed ``random_state``. requires_fit : bool, default=True Whether the estimator requires to be fitted before calling one of `transform`, `predict`, `predict_proba`, or `decision_function`. _skip_test : bool, default=False Whether to skip common tests entirely. Don't use this unless you have a *very good* reason. input_tags : :class:`InputTags` The input data(X) tags. """ estimator_type: str | None target_tags: TargetTags transformer_tags: TransformerTags | None = None classifier_tags: ClassifierTags | None = None regressor_tags: RegressorTags | None = None array_api_support: bool = False no_validation: bool = False non_deterministic: bool = False requires_fit: bool = True _skip_test: bool = False input_tags: InputTags = field(default_factory=InputTags) sampler_tags: SamplerTags | None = None def get_tags(estimator): """Get estimator tags in a consistent format across different sklearn versions. This function provides compatibility between sklearn versions before and after 1.6. It returns either a Tags object (sklearn >= 1.6) or a converted Tags object from the dictionary format (sklearn < 1.6) containing metadata about the estimator's requirements and capabilities. Parameters ---------- estimator : estimator object A scikit-learn estimator instance. Returns ------- tags : Tags An object containing metadata about the estimator's requirements and capabilities (e.g., input types, fitting requirements, classifier/regressor specific tags). """ try: from sklearn.utils._tags import get_tags return get_tags(estimator) except ImportError: from sklearn.utils._tags import _safe_tags return _to_new_tags(_safe_tags(estimator), estimator) def _to_new_tags(old_tags, estimator=None): """Utility function convert old tags (dictionary) to new tags (dataclass).""" input_tags = InputTags( one_d_array="1darray" in old_tags["X_types"], two_d_array="2darray" in old_tags["X_types"], three_d_array="3darray" in old_tags["X_types"], sparse="sparse" in old_tags["X_types"], categorical="categorical" in old_tags["X_types"], string="string" in old_tags["X_types"], dict="dict" in old_tags["X_types"], positive_only=old_tags["requires_positive_X"], allow_nan=old_tags["allow_nan"], pairwise=old_tags["pairwise"], dataframe="dataframe" in old_tags["X_types"], ) target_tags = TargetTags( required=old_tags["requires_y"], one_d_labels="1dlabels" in old_tags["X_types"], two_d_labels="2dlabels" in old_tags["X_types"], positive_only=old_tags["requires_positive_y"], multi_output=old_tags["multioutput"] or old_tags["multioutput_only"], single_output=not old_tags["multioutput_only"], ) if estimator is not None and ( hasattr(estimator, "transform") or hasattr(estimator, "fit_transform") ): transformer_tags = TransformerTags( preserves_dtype=old_tags["preserves_dtype"], ) else: transformer_tags = None estimator_type = getattr(estimator, "_estimator_type", None) if estimator_type == "classifier": classifier_tags = ClassifierTags( poor_score=old_tags["poor_score"], multi_class=not old_tags["binary_only"], multi_label=old_tags["multilabel"], ) else: classifier_tags = None if estimator_type == "regressor": regressor_tags = RegressorTags( poor_score=old_tags["poor_score"], multi_label=old_tags["multilabel"], ) else: regressor_tags = None if estimator_type == "sampler": sampler_tags = SamplerTags( sample_indices=old_tags.get("sample_indices", False), ) else: sampler_tags = None return Tags( estimator_type=estimator_type, target_tags=target_tags, transformer_tags=transformer_tags, classifier_tags=classifier_tags, regressor_tags=regressor_tags, sampler_tags=sampler_tags, input_tags=input_tags, # Array-API was introduced in 1.3, we need to default to False if not inside # the old-tags. array_api_support=old_tags.get("array_api_support", False), no_validation=old_tags["no_validation"], non_deterministic=old_tags["non_deterministic"], requires_fit=old_tags["requires_fit"], _skip_test=old_tags["_skip_test"], ) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/_test_common/000077500000000000000000000000001521136113700266155ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/_test_common/__init__.py000066400000000000000000000000001521136113700307140ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/_test_common/instance_generator.py000066400000000000000000000176471521136113700330600ustar00rootroot00000000000000# Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import re import warnings from contextlib import suppress from functools import partial from inspect import isfunction from sklearn import clone, config_context from sklearn.exceptions import SkipTestWarning from sklearn.linear_model import LogisticRegression from sklearn.tree import DecisionTreeClassifier from sklearn.utils._testing import SkipTest from sklearn.utils.fixes import parse_version from sklearn_compat._sklearn_compat import sklearn_version from imblearn.combine import SMOTEENN, SMOTETomek from imblearn.ensemble import ( BalancedBaggingClassifier, BalancedRandomForestClassifier, EasyEnsembleClassifier, RUSBoostClassifier, ) from imblearn.over_sampling import ( ADASYN, SMOTE, SMOTEN, SMOTENC, SVMSMOTE, BorderlineSMOTE, KMeansSMOTE, RandomOverSampler, ) from imblearn.pipeline import Pipeline from imblearn.under_sampling import ( ClusterCentroids, CondensedNearestNeighbour, InstanceHardnessThreshold, NearMiss, OneSidedSelection, RandomUnderSampler, ) from imblearn.utils.testing import all_estimators # The following dictionary is to indicate constructor arguments suitable for the test # suite, which uses very small datasets, and is intended to run rather quickly. INIT_PARAMS = { # estimator BalancedBaggingClassifier: dict(random_state=42), BalancedRandomForestClassifier: dict(random_state=42), EasyEnsembleClassifier: [ # AdaBoostClassifier does not allow nan values dict(random_state=42), # DecisionTreeClassifier allows nan values dict(estimator=DecisionTreeClassifier(random_state=42), random_state=42), ], Pipeline: dict( steps=[ ("sampler", RandomUnderSampler(random_state=0)), ("logistic", LogisticRegression()), ] ), # over-sampling ADASYN: dict(random_state=42), BorderlineSMOTE: dict(random_state=42), KMeansSMOTE: dict(random_state=0), RandomOverSampler: dict(random_state=42), SMOTE: dict(random_state=42), SMOTEN: dict(random_state=42), SMOTENC: dict(categorical_features=[0], random_state=42), SVMSMOTE: dict(random_state=42), # under-sampling ClusterCentroids: dict(random_state=42), CondensedNearestNeighbour: dict(random_state=42), InstanceHardnessThreshold: dict(random_state=42), NearMiss: [dict(version=1), dict(version=2), dict(version=3)], OneSidedSelection: dict(random_state=42), RandomUnderSampler: dict(random_state=42), # combination SMOTEENN: dict(random_state=42), SMOTETomek: dict(random_state=42), } # This dictionary stores parameters for specific checks. It also enables running the # same check with multiple instances of the same estimator with different parameters. # The special key "*" allows to apply the parameters to all checks. # TODO(devtools): allow third-party developers to pass test specific params to checks PER_ESTIMATOR_CHECK_PARAMS: dict = { Pipeline: { "check_classifiers_with_encoded_labels": dict( sampler__sampling_strategy={"setosa": 20, "virginica": 20} ) } } SKIPPED_ESTIMATORS = [SMOTENC] def _tested_estimators(type_filter=None): for _, Estimator in all_estimators(type_filter=type_filter): with suppress(SkipTest): yield from _construct_instances(Estimator) def _construct_instances(Estimator): """Construct Estimator instances if possible. If parameter sets in INIT_PARAMS are provided, use them. If there are a list of parameter sets, return one instance for each set. """ if Estimator in SKIPPED_ESTIMATORS: msg = f"Can't instantiate estimator {Estimator.__name__}" # raise additional warning to be shown by pytest warnings.warn(msg, SkipTestWarning) raise SkipTest(msg) if Estimator in INIT_PARAMS: param_sets = INIT_PARAMS[Estimator] if not isinstance(param_sets, list): param_sets = [param_sets] for params in param_sets: est = Estimator(**params) yield est else: yield Estimator() def _get_check_estimator_ids(obj): """Create pytest ids for checks. When `obj` is an estimator, this returns the pprint version of the estimator (with `print_changed_only=True`). When `obj` is a function, the name of the function is returned with its keyword arguments. `_get_check_estimator_ids` is designed to be used as the `id` in `pytest.mark.parametrize` where `check_estimator(..., generate_only=True)` is yielding estimators and checks. Parameters ---------- obj : estimator or function Items generated by `check_estimator`. Returns ------- id : str or None See Also -------- check_estimator """ if isfunction(obj): return obj.__name__ if isinstance(obj, partial): if not obj.keywords: return obj.func.__name__ kwstring = ",".join([f"{k}={v}" for k, v in obj.keywords.items()]) return f"{obj.func.__name__}({kwstring})" if hasattr(obj, "get_params"): with config_context(print_changed_only=True): return re.sub(r"\s", "", str(obj)) def _yield_instances_for_check(check, estimator_orig): """Yield instances for a check. For most estimators, this is a no-op. For estimators which have an entry in PER_ESTIMATOR_CHECK_PARAMS, this will yield an estimator for each parameter set in PER_ESTIMATOR_CHECK_PARAMS[estimator]. """ # TODO(devtools): enable this behavior for third party estimators as well if type(estimator_orig) not in PER_ESTIMATOR_CHECK_PARAMS: yield estimator_orig return check_params = PER_ESTIMATOR_CHECK_PARAMS[type(estimator_orig)] try: check_name = check.__name__ except AttributeError: # partial tests check_name = check.func.__name__ if check_name not in check_params: yield estimator_orig return param_set = check_params[check_name] if isinstance(param_set, dict): param_set = [param_set] for params in param_set: estimator = clone(estimator_orig) estimator.set_params(**params) yield estimator PER_ESTIMATOR_XFAIL_CHECKS = { BalancedRandomForestClassifier: { "check_sample_weight_equivalence": "FIXME", "check_sample_weight_equivalence_on_sparse_data": "FIXME", "check_sample_weight_equivalence_on_dense_data": "FIXME", }, NearMiss: { "check_samplers_fit_resample": "FIXME", }, Pipeline: { "check_classifiers_train": "FIXME", "check_supervised_y_2d": "FIXME", "check_dont_overwrite_parameters": ( "Pipeline changes the `steps` parameter, which it shouldn't. " "Therefore this test is x-fail until we fix this." ), "check_estimators_overwrite_params": ( "Pipeline changes the `steps` parameter, which it shouldn't. " "Therefore this test is x-fail until we fix this." ), }, RUSBoostClassifier: { "check_sample_weight_equivalence": "FIXME", "check_sample_weight_equivalence_on_sparse_data": "FIXME", "check_sample_weight_equivalence_on_dense_data": "FIXME", "check_estimator_sparse_data": "FIXME", "check_estimator_sparse_matrix": "FIXME", "check_estimator_sparse_array": "FIXME", }, } if sklearn_version < parse_version("1.4"): for _, Estimator in all_estimators(): if Estimator in PER_ESTIMATOR_XFAIL_CHECKS: PER_ESTIMATOR_XFAIL_CHECKS[Estimator]["check_estimators_pickle"] = "FIXME" else: PER_ESTIMATOR_XFAIL_CHECKS[Estimator] = {"check_estimators_pickle": "FIXME"} def _get_expected_failed_checks(estimator): """Get the expected failed checks for all estimators in scikit-learn.""" failed_checks = PER_ESTIMATOR_XFAIL_CHECKS.get(type(estimator), {}) return failed_checks scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/_validation.py000066400000000000000000000565651521136113700270130ustar00rootroot00000000000000"""Utilities for input validation""" # Authors: Guillaume Lemaitre # License: MIT import warnings from collections import OrderedDict from functools import wraps from inspect import Parameter, signature from numbers import Integral, Real import numpy as np from scipy.sparse import issparse from sklearn.base import clone from sklearn.neighbors import NearestNeighbors from sklearn.utils import column_or_1d from sklearn.utils.multiclass import type_of_target from sklearn.utils.validation import _num_samples from sklearn_compat.utils._dataframe import is_pandas_df from sklearn_compat.utils.validation import check_array SAMPLING_KIND = ( "over-sampling", "under-sampling", "clean-sampling", "ensemble", "bypass", ) TARGET_KIND = ("binary", "multiclass", "multilabel-indicator") class ArraysTransformer: """A class to convert sampler output arrays to their original types.""" def __init__(self, X, y): self.x_props = self._gets_props(X) self.y_props = self._gets_props(y) def transform(self, X, y): X = self._transfrom_one(X, self.x_props) y = self._transfrom_one(y, self.y_props) if self.x_props["type"].lower() == "dataframe" and self.y_props[ "type" ].lower() in {"series", "dataframe"}: # We lost the y.index during resampling. We can safely use X.index to align # them. y.index = X.index return X, y def _gets_props(self, array): props = {} props["type"] = array.__class__.__name__ props["columns"] = getattr(array, "columns", None) props["name"] = getattr(array, "name", None) props["dtypes"] = getattr(array, "dtypes", None) return props def _transfrom_one(self, array, props): type_ = props["type"].lower() if type_ == "list": ret = array.tolist() elif type_ == "dataframe": import pandas as pd if issparse(array): ret = pd.DataFrame.sparse.from_spmatrix(array, columns=props["columns"]) else: ret = pd.DataFrame(array, columns=props["columns"]) try: ret = ret.astype(props["dtypes"]) except TypeError: # We special case the following error: # https://github.com/scikit-learn-contrib/imbalanced-learn/issues/1055 # There is no easy way to have a generic workaround. Here, we detect # that we have a column with only null values that is datetime64 # (resulting from the np.vstack of the resampling). for col in ret.columns: if ( ret[col].isnull().all() and ret[col].dtype.kind == "M" # datetime64 and props["dtypes"][col].kind == "m" # timedelta64 ): ret[col] = pd.to_timedelta(["NaT"] * len(ret[col])) # try again ret = ret.astype(props["dtypes"]) elif type_ == "series": import pandas as pd ret = pd.Series(array, dtype=props["dtypes"], name=props["name"]) else: ret = array return ret def _is_neighbors_object(estimator): """Check that the estimator exposes a KNeighborsMixin-like API. A KNeighborsMixin-like API exposes the following methods: (i) `kneighbors`, (ii) `kneighbors_graph`. Parameters ---------- estimator : object A scikit-learn compatible estimator. Returns ------- is_neighbors_object : bool True if the estimator exposes a KNeighborsMixin-like API. """ neighbors_attributes = ["kneighbors", "kneighbors_graph"] return all(hasattr(estimator, attr) for attr in neighbors_attributes) def check_neighbors_object(nn_name, nn_object, additional_neighbor=0): """Check the objects is consistent to be a k nearest neighbors. Several methods in `imblearn` relies on k nearest neighbors. These objects can be passed at initialisation as an integer or as an object that has KNeighborsMixin-like attributes. This utility will create or clone said object, ensuring it is KNeighbors-like. Parameters ---------- nn_name : str The name associated to the object to raise an error if needed. nn_object : int or KNeighborsMixin The object to be checked. additional_neighbor : int, default=0 Sometimes, some algorithm need an additional neighbors. Returns ------- nn_object : KNeighborsMixin The k-NN object. """ if isinstance(nn_object, Integral): return NearestNeighbors(n_neighbors=nn_object + additional_neighbor) # _is_neighbors_object(nn_object) return clone(nn_object) def _count_class_sample(y): unique, counts = np.unique(y, return_counts=True) return dict(zip(unique, counts)) def check_target_type(y, indicate_one_vs_all=False): """Check the target types to be conform to the current samplers. The current samplers should be compatible with ``'binary'``, ``'multilabel-indicator'`` and ``'multiclass'`` targets only. Parameters ---------- y : ndarray The array containing the target. indicate_one_vs_all : bool, default=False Either to indicate if the targets are encoded in a one-vs-all fashion. Returns ------- y : ndarray The returned target. is_one_vs_all : bool, optional Indicate if the target was originally encoded in a one-vs-all fashion. Only returned if ``indicate_multilabel=True``. """ type_y = type_of_target(y) if type_y == "multilabel-indicator": if np.any(y.sum(axis=1) > 1): raise ValueError( "Imbalanced-learn currently supports binary, multiclass and " "binarized encoded multiclasss targets. Multilabel and " "multioutput targets are not supported." ) y = y.argmax(axis=1) else: y = column_or_1d(y) return (y, type_y == "multilabel-indicator") if indicate_one_vs_all else y def _sampling_strategy_all(y, sampling_type): """Returns sampling target by targeting all classes.""" target_stats = _count_class_sample(y) if sampling_type == "over-sampling": n_sample_majority = max(target_stats.values()) sampling_strategy = { key: n_sample_majority - value for (key, value) in target_stats.items() } elif sampling_type == "under-sampling" or sampling_type == "clean-sampling": n_sample_minority = min(target_stats.values()) sampling_strategy = {key: n_sample_minority for key in target_stats.keys()} else: raise NotImplementedError return sampling_strategy def _sampling_strategy_majority(y, sampling_type): """Returns sampling target by targeting the majority class only.""" if sampling_type == "over-sampling": raise ValueError( "'sampling_strategy'='majority' cannot be used with over-sampler." ) elif sampling_type == "under-sampling" or sampling_type == "clean-sampling": target_stats = _count_class_sample(y) class_majority = max(target_stats, key=target_stats.get) n_sample_minority = min(target_stats.values()) sampling_strategy = { key: n_sample_minority for key in target_stats.keys() if key == class_majority } else: raise NotImplementedError return sampling_strategy def _sampling_strategy_not_majority(y, sampling_type): """Returns sampling target by targeting all classes but not the majority.""" target_stats = _count_class_sample(y) if sampling_type == "over-sampling": n_sample_majority = max(target_stats.values()) class_majority = max(target_stats, key=target_stats.get) sampling_strategy = { key: n_sample_majority - value for (key, value) in target_stats.items() if key != class_majority } elif sampling_type == "under-sampling" or sampling_type == "clean-sampling": n_sample_minority = min(target_stats.values()) class_majority = max(target_stats, key=target_stats.get) sampling_strategy = { key: n_sample_minority for key in target_stats.keys() if key != class_majority } else: raise NotImplementedError return sampling_strategy def _sampling_strategy_not_minority(y, sampling_type): """Returns sampling target by targeting all classes but not the minority.""" target_stats = _count_class_sample(y) if sampling_type == "over-sampling": n_sample_majority = max(target_stats.values()) class_minority = min(target_stats, key=target_stats.get) sampling_strategy = { key: n_sample_majority - value for (key, value) in target_stats.items() if key != class_minority } elif sampling_type == "under-sampling" or sampling_type == "clean-sampling": n_sample_minority = min(target_stats.values()) class_minority = min(target_stats, key=target_stats.get) sampling_strategy = { key: n_sample_minority for key in target_stats.keys() if key != class_minority } else: raise NotImplementedError return sampling_strategy def _sampling_strategy_minority(y, sampling_type): """Returns sampling target by targeting the minority class only.""" target_stats = _count_class_sample(y) if sampling_type == "over-sampling": n_sample_majority = max(target_stats.values()) class_minority = min(target_stats, key=target_stats.get) sampling_strategy = { key: n_sample_majority - value for (key, value) in target_stats.items() if key == class_minority } elif sampling_type == "under-sampling" or sampling_type == "clean-sampling": raise ValueError( "'sampling_strategy'='minority' cannot be used with" " under-sampler and clean-sampler." ) else: raise NotImplementedError return sampling_strategy def _sampling_strategy_auto(y, sampling_type): """Returns sampling target auto for over-sampling and not-minority for under-sampling.""" if sampling_type == "over-sampling": return _sampling_strategy_not_majority(y, sampling_type) elif sampling_type == "under-sampling" or sampling_type == "clean-sampling": return _sampling_strategy_not_minority(y, sampling_type) def _sampling_strategy_dict(sampling_strategy, y, sampling_type): """Returns sampling target by converting the dictionary depending of the sampling.""" target_stats = _count_class_sample(y) # check that all keys in sampling_strategy are also in y set_diff_sampling_strategy_target = set(sampling_strategy.keys()) - set( target_stats.keys() ) if len(set_diff_sampling_strategy_target) > 0: raise ValueError( f"The {set_diff_sampling_strategy_target} target class is/are not " "present in the data." ) # check that there is no negative number if any(n_samples < 0 for n_samples in sampling_strategy.values()): raise ValueError( "The number of samples in a class cannot be negative." f"'sampling_strategy' contains some negative value: {sampling_strategy}" ) sampling_strategy_ = {} if sampling_type == "over-sampling": for class_sample, n_samples in sampling_strategy.items(): if n_samples < target_stats[class_sample]: raise ValueError( "With over-sampling methods, the number" " of samples in a class should be greater" " or equal to the original number of samples." f" Originally, there is {target_stats[class_sample]} " f"samples and {n_samples} samples are asked." ) sampling_strategy_[class_sample] = n_samples - target_stats[class_sample] elif sampling_type == "under-sampling": for class_sample, n_samples in sampling_strategy.items(): if n_samples > target_stats[class_sample]: raise ValueError( "With under-sampling methods, the number of" " samples in a class should be less or equal" " to the original number of samples." f" Originally, there is {target_stats[class_sample]} " f"samples and {n_samples} samples are asked." ) sampling_strategy_[class_sample] = n_samples elif sampling_type == "clean-sampling": raise ValueError( "'sampling_strategy' as a dict for cleaning methods is " "not supported. Please give a list of the classes to be " "targeted by the sampling." ) else: raise NotImplementedError return sampling_strategy_ def _sampling_strategy_list(sampling_strategy, y, sampling_type): """With cleaning methods, sampling_strategy can be a list to target the class of interest.""" if sampling_type != "clean-sampling": raise ValueError( "'sampling_strategy' cannot be a list for samplers " "which are not cleaning methods." ) target_stats = _count_class_sample(y) # check that all keys in sampling_strategy are also in y set_diff_sampling_strategy_target = set(sampling_strategy) - set( target_stats.keys() ) if len(set_diff_sampling_strategy_target) > 0: raise ValueError( f"The {set_diff_sampling_strategy_target} target class is/are not " "present in the data." ) return { class_sample: min(target_stats.values()) for class_sample in sampling_strategy } def _sampling_strategy_float(sampling_strategy, y, sampling_type): """Take a proportion of the majority (over-sampling) or minority (under-sampling) class in binary classification.""" type_y = type_of_target(y) if type_y != "binary": raise ValueError( '"sampling_strategy" can be a float only when the type ' "of target is binary. For multi-class, use a dict." ) target_stats = _count_class_sample(y) if sampling_type == "over-sampling": n_sample_majority = max(target_stats.values()) class_majority = max(target_stats, key=target_stats.get) sampling_strategy_ = { key: int(n_sample_majority * sampling_strategy - value) for (key, value) in target_stats.items() if key != class_majority } if any(n_samples <= 0 for n_samples in sampling_strategy_.values()): raise ValueError( "The specified ratio required to remove samples " "from the minority class while trying to " "generate new samples. Please increase the " "ratio." ) elif sampling_type == "under-sampling": n_sample_minority = min(target_stats.values()) class_minority = min(target_stats, key=target_stats.get) sampling_strategy_ = { key: int(n_sample_minority / sampling_strategy) for (key, value) in target_stats.items() if key != class_minority } if any( n_samples > target_stats[target] for target, n_samples in sampling_strategy_.items() ): raise ValueError( "The specified ratio required to generate new " "sample in the majority class while trying to " "remove samples. Please increase the ratio." ) else: raise ValueError( "'clean-sampling' methods do let the user specify the sampling ratio." ) return sampling_strategy_ def check_sampling_strategy(sampling_strategy, y, sampling_type, **kwargs): """Sampling target validation for samplers. Checks that ``sampling_strategy`` is of consistent type and return a dictionary containing each targeted class with its corresponding number of sample. It is used in :class:`~imblearn.base.BaseSampler`. Parameters ---------- sampling_strategy : float, str, dict, list or callable, Sampling information to sample the data set. - When ``float``: For **under-sampling methods**, it corresponds to the ratio :math:`\\alpha_{us}` defined by :math:`N_{rM} = \\alpha_{us} \\times N_{m}` where :math:`N_{rM}` and :math:`N_{m}` are the number of samples in the majority class after resampling and the number of samples in the minority class, respectively; For **over-sampling methods**, it correspond to the ratio :math:`\\alpha_{os}` defined by :math:`N_{rm} = \\alpha_{os} \\times N_{m}` where :math:`N_{rm}` and :math:`N_{M}` are the number of samples in the minority class after resampling and the number of samples in the majority class, respectively. .. warning:: ``float`` is only available for **binary** classification. An error is raised for multi-class classification and with cleaning samplers. - When ``str``, specify the class targeted by the resampling. For **under- and over-sampling methods**, the number of samples in the different classes will be equalized. For **cleaning methods**, the number of samples will not be equal. Possible choices are: ``'minority'``: resample only the minority class; ``'majority'``: resample only the majority class; ``'not minority'``: resample all classes but the minority class; ``'not majority'``: resample all classes but the majority class; ``'all'``: resample all classes; ``'auto'``: for under-sampling methods, equivalent to ``'not minority'`` and for over-sampling methods, equivalent to ``'not majority'``. - When ``dict``, the keys correspond to the targeted classes. The values correspond to the desired number of samples for each targeted class. .. warning:: ``dict`` is available for both **under- and over-sampling methods**. An error is raised with **cleaning methods**. Use a ``list`` instead. - When ``list``, the list contains the targeted classes. It used only for **cleaning methods**. .. warning:: ``list`` is available for **cleaning methods**. An error is raised with **under- and over-sampling methods**. - When callable, function taking ``y`` and returns a ``dict``. The keys correspond to the targeted classes. The values correspond to the desired number of samples for each class. y : ndarray of shape (n_samples,) The target array. sampling_type : {{'over-sampling', 'under-sampling', 'clean-sampling'}} The type of sampling. Can be either ``'over-sampling'``, ``'under-sampling'``, or ``'clean-sampling'``. **kwargs : dict Dictionary of additional keyword arguments to pass to ``sampling_strategy`` when this is a callable. Returns ------- sampling_strategy_converted : dict The converted and validated sampling target. Returns a dictionary with the key being the class target and the value being the desired number of samples. """ if sampling_type not in SAMPLING_KIND: raise ValueError( f"'sampling_type' should be one of {SAMPLING_KIND}. " f"Got '{sampling_type} instead." ) if np.unique(y).size <= 1: raise ValueError( "The target 'y' needs to have more than 1 class. " f"Got {np.unique(y).size} class instead" ) if sampling_type in ("ensemble", "bypass"): return sampling_strategy if isinstance(sampling_strategy, str): if sampling_strategy not in SAMPLING_TARGET_KIND.keys(): raise ValueError( "When 'sampling_strategy' is a string, it needs" f" to be one of {SAMPLING_TARGET_KIND}. Got '{sampling_strategy}' " "instead." ) return OrderedDict( sorted(SAMPLING_TARGET_KIND[sampling_strategy](y, sampling_type).items()) ) elif isinstance(sampling_strategy, dict): return OrderedDict( sorted(_sampling_strategy_dict(sampling_strategy, y, sampling_type).items()) ) elif isinstance(sampling_strategy, list): return OrderedDict( sorted(_sampling_strategy_list(sampling_strategy, y, sampling_type).items()) ) elif isinstance(sampling_strategy, Real): if sampling_strategy <= 0 or sampling_strategy > 1: raise ValueError( "When 'sampling_strategy' is a float, it should be " f"in the range (0, 1]. Got {sampling_strategy} instead." ) return OrderedDict( sorted( _sampling_strategy_float(sampling_strategy, y, sampling_type).items() ) ) elif callable(sampling_strategy): sampling_strategy_ = sampling_strategy(y, **kwargs) return OrderedDict( sorted( _sampling_strategy_dict(sampling_strategy_, y, sampling_type).items() ) ) SAMPLING_TARGET_KIND = { "minority": _sampling_strategy_minority, "majority": _sampling_strategy_majority, "not minority": _sampling_strategy_not_minority, "not majority": _sampling_strategy_not_majority, "all": _sampling_strategy_all, "auto": _sampling_strategy_auto, } def _deprecate_positional_args(f): """Decorator for methods that issues warnings for positional arguments Using the keyword-only argument syntax in pep 3102, arguments after the * will issue a warning when passed as a positional argument. Parameters ---------- f : function function to check arguments on. """ sig = signature(f) kwonly_args = [] all_args = [] for name, param in sig.parameters.items(): if param.kind == Parameter.POSITIONAL_OR_KEYWORD: all_args.append(name) elif param.kind == Parameter.KEYWORD_ONLY: kwonly_args.append(name) @wraps(f) def inner_f(*args, **kwargs): extra_args = len(args) - len(all_args) if extra_args > 0: # ignore first 'self' argument for instance methods args_msg = [ f"{name}={arg}" for name, arg in zip(kwonly_args[:extra_args], args[-extra_args:]) ] warnings.warn( ( f"Pass {', '.join(args_msg)} as keyword args. From version 0.9 " "passing these as positional arguments will " "result in an error" ), FutureWarning, ) kwargs.update(dict(zip(sig.parameters, args))) return f(**kwargs) return inner_f def _check_X(X): """Check X and do not check it if a dataframe.""" n_samples = _num_samples(X) if n_samples < 1: raise ValueError( f"Found array with {n_samples} sample(s) while a minimum of 1 is required." ) if is_pandas_df(X): return X return check_array( X, dtype=None, accept_sparse=["csr", "csc"], ensure_all_finite=False ) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/deprecation.py000066400000000000000000000032771521136113700270070ustar00rootroot00000000000000"""Utilities for deprecation""" # Authors: Guillaume Lemaitre # License: MIT import warnings def deprecate_parameter(sampler, version_deprecation, param_deprecated, new_param=None): """Helper to deprecate a parameter by another one. Parameters ---------- sampler : sampler object, The object which will be inspected. version_deprecation : str, The version from which the parameter will be deprecated. The format should be ``'x.y'``. param_deprecated : str, The parameter being deprecated. new_param : str, The parameter used instead of the deprecated parameter. By default, no parameter is expected. """ x, y = version_deprecation.split(".") version_removed = x + "." + str(int(y) + 2) if new_param is None: if getattr(sampler, param_deprecated) is not None: warnings.warn( ( f"'{param_deprecated}' is deprecated from {version_deprecation} and" f" will be removed in {version_removed} for the estimator" f" {sampler.__class__}." ), category=FutureWarning, ) else: if getattr(sampler, param_deprecated) is not None: warnings.warn( ( f"'{param_deprecated}' is deprecated from {version_deprecation} and" f" will be removed in {version_removed} for the estimator" f" {sampler.__class__}. Use '{new_param}' instead." ), category=FutureWarning, ) setattr(sampler, new_param, getattr(sampler, param_deprecated)) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/estimator_checks.py000066400000000000000000001010741521136113700300330ustar00rootroot00000000000000"""Utils to check the samplers and compatibility with scikit-learn""" # Adapated from scikit-learn # Authors: Guillaume Lemaitre # License: MIT import re import sys import traceback import warnings from collections import Counter from functools import partial, wraps import numpy as np from scipy import sparse from sklearn.base import clone, is_classifier, is_regressor from sklearn.cluster import KMeans from sklearn.datasets import ( # noqa load_iris, make_blobs, make_classification, make_multilabel_classification, ) from sklearn.exceptions import SkipTestWarning from sklearn.preprocessing import StandardScaler, label_binarize from sklearn.utils._param_validation import generate_invalid_param_val, make_constraint from sklearn.utils._testing import ( SkipTest, assert_allclose, assert_array_equal, raises, set_random_state, ) from sklearn.utils.estimator_checks import ( _enforce_estimator_tags_X, _enforce_estimator_tags_y, ) from sklearn.utils.multiclass import type_of_target from imblearn.datasets import make_imbalance from imblearn.over_sampling.base import BaseOverSampler from imblearn.under_sampling.base import BaseCleaningSampler, BaseUnderSampler from imblearn.utils._tags import get_tags from imblearn.utils._test_common.instance_generator import ( _get_check_estimator_ids, _yield_instances_for_check, ) def sample_dataset_generator(): X, y = make_classification( n_samples=1000, n_classes=3, n_informative=4, weights=[0.2, 0.3, 0.5], random_state=0, ) return X, y def _set_checking_parameters(estimator): params = estimator.get_params() name = estimator.__class__.__name__ if "n_estimators" in params: estimator.set_params(n_estimators=min(5, estimator.n_estimators)) if name == "ClusterCentroids": algorithm = "lloyd" estimator.set_params( voting="soft", estimator=KMeans(random_state=0, algorithm=algorithm, n_init=1), ) if name == "KMeansSMOTE": estimator.set_params(kmeans_estimator=12) def _yield_sampler_checks(sampler): tags = get_tags(sampler) accept_sparse = tags.input_tags.sparse accept_dataframe = tags.input_tags.dataframe accept_string = tags.input_tags.string allow_nan = tags.input_tags.allow_nan yield check_target_type yield check_samplers_one_label yield check_samplers_fit yield check_samplers_fit_resample yield check_samplers_sampling_strategy_fit_resample if accept_sparse: yield check_samplers_sparse if accept_dataframe: yield check_samplers_pandas yield check_samplers_pandas_sparse if accept_string: yield check_samplers_string if allow_nan: yield check_samplers_nan yield check_samplers_list yield check_samplers_multiclass_ova yield check_samplers_preserve_dtype # we don't filter samplers based on their tag here because we want to make # sure that the fitted attribute does not exist if the tag is not # stipulated yield check_samplers_sample_indices yield check_samplers_2d_target yield check_sampler_get_feature_names_out yield check_sampler_get_feature_names_out_pandas def _yield_classifier_checks(classifier): yield check_classifier_on_multilabel_or_multioutput_targets yield check_classifiers_with_encoded_labels def _yield_all_checks(estimator, legacy=True): name = estimator.__class__.__name__ tags = get_tags(estimator) skip_test = tags._skip_test if skip_test: warnings.warn( f"Explicit SKIP via _skip_test tag for estimator {name}.", SkipTestWarning, ) return # trigger our checks if this is a SamplerMixin if hasattr(estimator, "fit_resample"): for check in _yield_sampler_checks(estimator): yield check if hasattr(estimator, "predict"): for check in _yield_classifier_checks(estimator): yield check def _check_name(check): if hasattr(check, "__wrapped__"): return _check_name(check.__wrapped__) return check.func.__name__ if isinstance(check, partial) else check.__name__ def _maybe_mark(estimator, check, expected_failed_checks=None, mark=None, pytest=None): """Mark the test as xfail or skip if needed. Parameters ---------- estimator : estimator object Estimator instance for which to generate checks. check : partial or callable Check to be marked. expected_failed_checks : dict[str, str], default=None Dictionary of the form {check_name: reason} for checks that are expected to fail. mark : "xfail" or "skip" or None Whether to mark the check as xfail or skip. pytest : pytest module, default=None Pytest module to use to mark the check. This is only needed if ``mark`` is `"xfail"`. Note that one can run `check_estimator` without having `pytest` installed. This is used in combination with `parametrize_with_checks` only. """ should_be_marked, reason = _should_be_skipped_or_marked( estimator, check, expected_failed_checks ) if not should_be_marked or mark is None: return estimator, check estimator_name = estimator.__class__.__name__ if mark == "xfail": return pytest.param(estimator, check, marks=pytest.mark.xfail(reason=reason)) else: @wraps(check) def wrapped(*args, **kwargs): raise SkipTest( f"Skipping {_check_name(check)} for {estimator_name}: {reason}" ) return estimator, wrapped def _should_be_skipped_or_marked( estimator, check, expected_failed_checks: dict[str, str] | None = None ) -> tuple[bool, str]: """Check whether a check should be skipped or marked as xfail. Parameters ---------- estimator : estimator object Estimator instance for which to generate checks. check : partial or callable Check to be marked. expected_failed_checks : dict[str, str], default=None Dictionary of the form {check_name: reason} for checks that are expected to fail. Returns ------- should_be_marked : bool Whether the check should be marked as xfail or skipped. reason : str Reason for skipping the check. """ expected_failed_checks = expected_failed_checks or {} check_name = _check_name(check) if check_name in expected_failed_checks: return True, expected_failed_checks[check_name] return False, "Check is not expected to fail" def estimator_checks_generator( estimator, *, legacy=True, expected_failed_checks=None, mark=None ): """Iteratively yield all check callables for an estimator. .. versionadded:: 1.6 Parameters ---------- estimator : estimator object Estimator instance for which to generate checks. legacy : bool, default=True Whether to include legacy checks. Over time we remove checks from this category and move them into their specific category. expected_failed_checks : dict[str, str], default=None Dictionary of the form {check_name: reason} for checks that are expected to fail. mark : {"xfail", "skip"} or None, default=None Whether to mark the checks that are expected to fail as xfail(`pytest.mark.xfail`) or skip. Marking a test as "skip" is done via wrapping the check in a function that raises a :class:`~sklearn.exceptions.SkipTest` exception. Returns ------- estimator_checks_generator : generator Generator that yields (estimator, check) tuples. """ if mark == "xfail": import pytest else: pytest = None # type: ignore name = type(estimator).__name__ for check in _yield_all_checks(estimator, legacy=legacy): check_with_name = partial(check, name) for check_instance in _yield_instances_for_check(check, estimator): yield _maybe_mark( check_instance, check_with_name, expected_failed_checks=expected_failed_checks, mark=mark, pytest=pytest, ) def parametrize_with_checks(estimators, *, legacy=True, expected_failed_checks=None): """Pytest specific decorator for parametrizing estimator checks. Checks are categorised into the following groups: - API checks: a set of checks to ensure API compatibility with scikit-learn. Refer to https://scikit-learn.org/dev/developers/develop.html a requirement of scikit-learn estimators. - legacy: a set of checks which gradually will be grouped into other categories. The `id` of each check is set to be a pprint version of the estimator and the name of the check with its keyword arguments. This allows to use `pytest -k` to specify which tests to run:: pytest test_check_estimators.py -k check_estimators_fit_returns_self Parameters ---------- estimators : list of estimators instances Estimators to generated checks for. .. versionchanged:: 0.24 Passing a class was deprecated in version 0.23, and support for classes was removed in 0.24. Pass an instance instead. .. versionadded:: 0.24 legacy : bool, default=True Whether to include legacy checks. Over time we remove checks from this category and move them into their specific category. .. versionadded:: 1.6 expected_failed_checks : callable, default=None A callable that takes an estimator as input and returns a dictionary of the form:: { "check_name": "my reason", } Where `"check_name"` is the name of the check, and `"my reason"` is why the check fails. These tests will be marked as xfail if the check fails. .. versionadded:: 1.6 Returns ------- decorator : `pytest.mark.parametrize` See Also -------- check_estimator : Check if estimator adheres to scikit-learn conventions. Examples -------- >>> from sklearn.utils.estimator_checks import parametrize_with_checks >>> from sklearn.linear_model import LogisticRegression >>> from sklearn.tree import DecisionTreeRegressor >>> @parametrize_with_checks([LogisticRegression(), ... DecisionTreeRegressor()]) ... def test_sklearn_compatible_estimator(estimator, check): ... check(estimator) """ import pytest if any(isinstance(est, type) for est in estimators): msg = ( "Passing a class was deprecated in version 0.23 " "and isn't supported anymore from 0.24." "Please pass an instance instead." ) raise TypeError(msg) def _checks_generator(estimators, legacy, expected_failed_checks): for estimator in estimators: args = {"estimator": estimator, "legacy": legacy, "mark": "xfail"} if callable(expected_failed_checks): args["expected_failed_checks"] = expected_failed_checks(estimator) yield from estimator_checks_generator(**args) return pytest.mark.parametrize( "estimator, check", _checks_generator(estimators, legacy, expected_failed_checks), ids=_get_check_estimator_ids, ) def check_target_type(name, estimator_orig): estimator = clone(estimator_orig) # should raise warning if the target is continuous (we cannot raise error) X = np.random.random((20, 2)) y = np.linspace(0, 1, 20) msg = "Unknown label type:" with raises(ValueError, err_msg=msg): estimator.fit_resample(X, y) # if the target is multilabel then we should raise an error rng = np.random.RandomState(42) y = rng.randint(2, size=(20, 3)) msg = "Multilabel and multioutput targets are not supported." with raises(ValueError, err_msg=msg): estimator.fit_resample(X, y) def check_samplers_one_label(name, sampler_orig): sampler = clone(sampler_orig) error_string_fit = "Sampler can't balance when only one class is present." X = np.random.random((20, 2)) y = np.zeros(20) try: sampler.fit_resample(X, y) except ValueError as e: if "class" not in repr(e): print(error_string_fit, sampler.__class__.__name__, e) traceback.print_exc(file=sys.stdout) raise e else: return except Exception as exc: print(error_string_fit, traceback, exc) traceback.print_exc(file=sys.stdout) raise exc raise AssertionError(error_string_fit) def check_samplers_fit(name, sampler_orig): sampler = clone(sampler_orig) np.random.seed(42) # Make this test reproducible X = np.random.random((30, 2)) y = np.array([1] * 20 + [0] * 10) sampler.fit_resample(X, y) assert hasattr( sampler, "sampling_strategy_" ), "No fitted attribute sampling_strategy_" def check_samplers_fit_resample(name, sampler_orig): sampler = clone(sampler_orig) X, y = sample_dataset_generator() target_stats = Counter(y) X_res, y_res = sampler.fit_resample(X, y) if isinstance(sampler, BaseOverSampler): target_stats_res = Counter(y_res) n_samples = max(target_stats.values()) assert all(value >= n_samples for value in Counter(y_res).values()) elif isinstance(sampler, BaseUnderSampler): n_samples = min(target_stats.values()) if name == "InstanceHardnessThreshold": # IHT does not enforce the number of samples but provide a number # of samples the closest to the desired target. assert all( Counter(y_res)[k] <= target_stats[k] for k in target_stats.keys() ) else: assert all(value == n_samples for value in Counter(y_res).values()) elif isinstance(sampler, BaseCleaningSampler): target_stats_res = Counter(y_res) class_minority = min(target_stats, key=target_stats.get) assert all( target_stats[class_sample] > target_stats_res[class_sample] for class_sample in target_stats.keys() if class_sample != class_minority ) def check_samplers_sampling_strategy_fit_resample(name, sampler_orig): sampler = clone(sampler_orig) # in this test we will force all samplers to not change the class 1 X, y = sample_dataset_generator() expected_stat = Counter(y)[1] if isinstance(sampler, BaseOverSampler): sampling_strategy = {2: 498, 0: 498} sampler.set_params(sampling_strategy=sampling_strategy) X_res, y_res = sampler.fit_resample(X, y) assert Counter(y_res)[1] == expected_stat elif isinstance(sampler, BaseUnderSampler): sampling_strategy = {2: 201, 0: 201} sampler.set_params(sampling_strategy=sampling_strategy) X_res, y_res = sampler.fit_resample(X, y) assert Counter(y_res)[1] == expected_stat elif isinstance(sampler, BaseCleaningSampler): sampling_strategy = [2, 0] sampler.set_params(sampling_strategy=sampling_strategy) X_res, y_res = sampler.fit_resample(X, y) assert Counter(y_res)[1] == expected_stat def check_samplers_sparse(name, sampler_orig): sampler = clone(sampler_orig) # check that sparse matrices can be passed through the sampler leading to # the same results than dense X, y = sample_dataset_generator() X_sparse = sparse.csr_matrix(X) X_res_sparse, y_res_sparse = sampler.fit_resample(X_sparse, y) sampler = clone(sampler) X_res, y_res = sampler.fit_resample(X, y) assert sparse.issparse(X_res_sparse) assert_allclose(X_res_sparse.toarray(), X_res, rtol=1e-5) assert_allclose(y_res_sparse, y_res) def check_samplers_pandas_sparse(name, sampler_orig): try: import pandas as pd except ImportError: raise SkipTest( "pandas is not installed: not checking column name consistency for pandas" ) sampler = clone(sampler_orig) # Check that the samplers handle pandas dataframe and pandas series X, y = sample_dataset_generator() X_df = pd.DataFrame( X, columns=[str(i) for i in range(X.shape[1])], dtype=pd.SparseDtype(float, 0) ) y_s = pd.Series(y, name="class") X_res_df, y_res_s = sampler.fit_resample(X_df, y_s) X_res, y_res = sampler.fit_resample(X, y) # check that we return the same type for dataframes or series types assert isinstance(X_res_df, pd.DataFrame) assert isinstance(y_res_s, pd.Series) for column_dtype in X_res_df.dtypes: assert isinstance(column_dtype, pd.SparseDtype) assert X_df.columns.tolist() == X_res_df.columns.tolist() assert y_s.name == y_res_s.name assert_allclose(X_res_df.to_numpy(), X_res) assert_allclose(y_res_s.to_numpy(), y_res) def check_samplers_pandas(name, sampler_orig): try: import pandas as pd except ImportError: raise SkipTest( "pandas is not installed: not checking column name consistency for pandas" ) sampler = clone(sampler_orig) # Check that the samplers handle pandas dataframe and pandas series X, y = sample_dataset_generator() X_df = pd.DataFrame(X, columns=[str(i) for i in range(X.shape[1])]) y_df = pd.DataFrame(y) y_s = pd.Series(y, name="class") X_res_df, y_res_s = sampler.fit_resample(X_df, y_s) X_res_df, y_res_df = sampler.fit_resample(X_df, y_df) X_res, y_res = sampler.fit_resample(X, y) # check that we return the same type for dataframes or series types assert isinstance(X_res_df, pd.DataFrame) assert isinstance(y_res_df, pd.DataFrame) assert isinstance(y_res_s, pd.Series) assert X_df.columns.tolist() == X_res_df.columns.tolist() assert y_df.columns.tolist() == y_res_df.columns.tolist() assert y_s.name == y_res_s.name assert_allclose(X_res_df.to_numpy(), X_res) assert_allclose(y_res_df.to_numpy().ravel(), y_res) assert_allclose(y_res_s.to_numpy(), y_res) def check_samplers_list(name, sampler_orig): sampler = clone(sampler_orig) # Check that the can samplers handle simple lists X, y = sample_dataset_generator() X_list = X.tolist() y_list = y.tolist() X_res, y_res = sampler.fit_resample(X, y) X_res_list, y_res_list = sampler.fit_resample(X_list, y_list) assert isinstance(X_res_list, list) assert isinstance(y_res_list, list) assert_allclose(X_res, X_res_list) assert_allclose(y_res, y_res_list) def check_samplers_multiclass_ova(name, sampler_orig): sampler = clone(sampler_orig) # Check that multiclass target lead to the same results than OVA encoding X, y = sample_dataset_generator() y_ova = label_binarize(y, classes=np.unique(y)) X_res, y_res = sampler.fit_resample(X, y) X_res_ova, y_res_ova = sampler.fit_resample(X, y_ova) assert_allclose(X_res, X_res_ova) assert type_of_target(y_res_ova) == type_of_target(y_ova) assert_allclose(y_res, y_res_ova.argmax(axis=1)) def check_samplers_2d_target(name, sampler_orig): sampler = clone(sampler_orig) X, y = sample_dataset_generator() y = y.reshape(-1, 1) # Make the target 2d sampler.fit_resample(X, y) def check_samplers_preserve_dtype(name, sampler_orig): sampler = clone(sampler_orig) X, y = sample_dataset_generator() # Cast X and y to not default dtype X = X.astype(np.float32) y = y.astype(np.int32) X_res, y_res = sampler.fit_resample(X, y) assert X.dtype == X_res.dtype, "X dtype is not preserved" assert y.dtype == y_res.dtype, "y dtype is not preserved" def check_samplers_sample_indices(name, sampler_orig): sampler = clone(sampler_orig) X, y = sample_dataset_generator() sampler.fit_resample(X, y) tags = get_tags(sampler) if tags.sampler_tags.sample_indices: assert hasattr(sampler, "sample_indices_") is tags.sampler_tags.sample_indices else: assert not hasattr(sampler, "sample_indices_") def check_samplers_string(name, sampler_orig): rng = np.random.RandomState(0) sampler = clone(sampler_orig) categories = np.array(["A", "B", "C"], dtype=object) n_samples = 30 X = rng.randint(low=0, high=3, size=n_samples).reshape(-1, 1) X = categories[X] y = rng.permutation([0] * 10 + [1] * 20) X_res, y_res = sampler.fit_resample(X, y) assert X_res.dtype == object assert X_res.shape[0] == y_res.shape[0] assert_array_equal(np.unique(X_res.ravel()), categories) def check_samplers_nan(name, sampler_orig): rng = np.random.RandomState(0) sampler = clone(sampler_orig) categories = np.array([0, 1, np.nan], dtype=np.float64) n_samples = 100 X = rng.randint(low=0, high=3, size=n_samples).reshape(-1, 1) X = categories[X] y = rng.permutation([0] * 40 + [1] * 60) X_res, y_res = sampler.fit_resample(X, y) assert X_res.dtype == np.float64 assert X_res.shape[0] == y_res.shape[0] assert np.any(np.isnan(X_res.ravel())) def check_classifier_on_multilabel_or_multioutput_targets(name, estimator_orig): estimator = clone(estimator_orig) X, y = make_multilabel_classification(n_samples=30) msg = "Multilabel and multioutput targets are not supported." with raises(ValueError, match=msg): estimator.fit(X, y) def check_classifiers_with_encoded_labels(name, classifier_orig): # Non-regression test for #709 # https://github.com/scikit-learn-contrib/imbalanced-learn/issues/709 try: import pandas as pd except ImportError: raise SkipTest( "pandas is not installed: not checking column name consistency for pandas" ) classifier = clone(classifier_orig) iris = load_iris(as_frame=True) df, y = iris.data, iris.target y = pd.Series(iris.target_names[iris.target], dtype="category") df, y = make_imbalance( df, y, sampling_strategy={ "setosa": 30, "versicolor": 20, "virginica": 50, }, ) classifier.fit(df, y) assert set(classifier.classes_) == set(y.cat.categories.tolist()) y_pred = classifier.predict(df) assert set(y_pred) == set(y.cat.categories.tolist()) def check_param_validation(name, estimator_orig): # Check that an informative error is raised when the value of a constructor # parameter does not have an appropriate type or value. rng = np.random.RandomState(0) X = rng.uniform(size=(20, 5)) y = rng.randint(0, 2, size=20) y = _enforce_estimator_tags_y(estimator_orig, y) estimator_params = estimator_orig.get_params(deep=False).keys() # check that there is a constraint for each parameter if estimator_params: validation_params = estimator_orig._parameter_constraints.keys() unexpected_params = set(validation_params) - set(estimator_params) missing_params = set(estimator_params) - set(validation_params) err_msg = ( f"Mismatch between _parameter_constraints and the parameters of {name}." f"\nConsider the unexpected parameters {unexpected_params} and expected but" f" missing parameters {missing_params}" ) assert validation_params == estimator_params, err_msg # this object does not have a valid type for sure for all params param_with_bad_type = type("BadType", (), {})() fit_methods = ["fit", "partial_fit", "fit_transform", "fit_predict", "fit_resample"] for param_name in estimator_params: constraints = estimator_orig._parameter_constraints[param_name] if constraints == "no_validation": # This parameter is not validated continue # pragma: no cover match = rf"The '{param_name}' parameter of {name} must be .* Got .* instead." err_msg = ( f"{name} does not raise an informative error message when the " f"parameter {param_name} does not have a valid type or value." ) estimator = clone(estimator_orig) # First, check that the error is raised if param doesn't match any valid type. estimator.set_params(**{param_name: param_with_bad_type}) for method in fit_methods: if not hasattr(estimator, method): # the method is not accessible with the current set of parameters continue with raises(ValueError, match=match, err_msg=err_msg): getattr(estimator, method)(X, y) # Then, for constraints that are more than a type constraint, check that the # error is raised if param does match a valid type but does not match any valid # value for this type. constraints = [make_constraint(constraint) for constraint in constraints] for constraint in constraints: try: bad_value = generate_invalid_param_val(constraint) except NotImplementedError: continue estimator.set_params(**{param_name: bad_value}) for method in fit_methods: if not hasattr(estimator, method): # the method is not accessible with the current set of parameters continue with raises(ValueError, match=match, err_msg=err_msg): getattr(estimator, method)(X, y) def check_dataframe_column_names_consistency(name, estimator_orig): try: import pandas as pd except ImportError: raise SkipTest( "pandas is not installed: not checking column name consistency for pandas" ) tags = get_tags(estimator_orig) is_supported_X_types = tags.input_tags.two_d_array or tags.input_tags.categorical no_validation = tags.no_validation if not is_supported_X_types or no_validation: return rng = np.random.RandomState(0) estimator = clone(estimator_orig) set_random_state(estimator) X_orig = rng.normal(size=(150, 8)) X_orig = _enforce_estimator_tags_X(estimator, X_orig) n_samples, n_features = X_orig.shape names = np.array([f"col_{i}" for i in range(n_features)]) X = pd.DataFrame(X_orig, columns=names) if is_regressor(estimator): y = rng.normal(size=n_samples) else: y = rng.randint(low=0, high=2, size=n_samples) y = _enforce_estimator_tags_y(estimator, y) # Check that calling `fit` does not raise any warnings about feature names. with warnings.catch_warnings(): warnings.filterwarnings( "error", message="X does not have valid feature names", category=UserWarning, module="imblearn", ) estimator.fit(X, y) if not hasattr(estimator, "feature_names_in_"): raise ValueError( "Estimator does not have a feature_names_in_ " "attribute after fitting with a dataframe" ) assert isinstance(estimator.feature_names_in_, np.ndarray) assert estimator.feature_names_in_.dtype == object assert_array_equal(estimator.feature_names_in_, names) # Only check imblearn estimators for feature_names_in_ in docstring module_name = estimator_orig.__module__ if ( module_name.startswith("imblearn.") and not ("test_" in module_name or module_name.endswith("_testing")) and ("feature_names_in_" not in (estimator_orig.__doc__)) ): raise ValueError( f"Estimator {name} does not document its feature_names_in_ attribute" ) check_methods = [] for method in ( "predict", "transform", "decision_function", "predict_proba", "score", "score_samples", "predict_log_proba", ): if not hasattr(estimator, method): continue callable_method = getattr(estimator, method) if method == "score": callable_method = partial(callable_method, y=y) check_methods.append((method, callable_method)) for _, method in check_methods: with warnings.catch_warnings(): warnings.filterwarnings( "error", message="X does not have valid feature names", category=UserWarning, module="sklearn", ) method(X) # works without UserWarning for valid features invalid_names = [ (names[::-1], "Feature names must be in the same order as they were in fit."), ( [f"another_prefix_{i}" for i in range(n_features)], ( "Feature names unseen at fit time:\n- another_prefix_0\n-" " another_prefix_1\n" ), ), ( names[:3], f"Feature names seen at fit time, yet now missing:\n- {min(names[3:])}\n", ), ] params = { key: value for key, value in estimator.get_params().items() if "early_stopping" in key } early_stopping_enabled = any(value is True for value in params.values()) for invalid_name, additional_message in invalid_names: X_bad = pd.DataFrame(X, columns=invalid_name) for name, method in check_methods: expected_msg = re.escape( "The feature names should match those that were passed during fit." f"\n{additional_message}" ) with raises( ValueError, match=expected_msg, err_msg=f"{name} did not raise" ): method(X_bad) # partial_fit checks on second call # Do not call partial fit if early_stopping is on if not hasattr(estimator, "partial_fit") or early_stopping_enabled: continue estimator = clone(estimator_orig) if is_classifier(estimator): classes = np.unique(y) estimator.partial_fit(X, y, classes=classes) else: estimator.partial_fit(X, y) with raises(ValueError, match=expected_msg): estimator.partial_fit(X_bad, y) def check_sampler_get_feature_names_out(name, sampler_orig): tags = get_tags(sampler_orig) two_d_array = tags.input_tags.two_d_array no_validation = tags.no_validation if not two_d_array or no_validation: return X, y = make_blobs( n_samples=30, centers=[[0, 0, 0], [1, 1, 1]], random_state=0, n_features=2, cluster_std=0.1, ) X = StandardScaler().fit_transform(X) sampler = clone(sampler_orig) X = _enforce_estimator_tags_X(sampler, X) n_features = X.shape[1] set_random_state(sampler) y_ = y X_res, y_res = sampler.fit_resample(X, y=y_) input_features = [f"feature{i}" for i in range(n_features)] # input_features names is not the same length as n_features_in_ with raises(ValueError, match="input_features should have length equal"): sampler.get_feature_names_out(input_features[::2]) feature_names_out = sampler.get_feature_names_out(input_features) assert feature_names_out is not None assert isinstance(feature_names_out, np.ndarray) assert feature_names_out.dtype == object assert all(isinstance(name, str) for name in feature_names_out) n_features_out = X_res.shape[1] assert ( len(feature_names_out) == n_features_out ), f"Expected {n_features_out} feature names, got {len(feature_names_out)}" def check_sampler_get_feature_names_out_pandas(name, sampler_orig): try: import pandas as pd except ImportError: raise SkipTest( "pandas is not installed: not checking column name consistency for pandas" ) tags = get_tags(sampler_orig) two_d_array = tags.input_tags.two_d_array no_validation = tags.no_validation if not two_d_array or no_validation: return X, y = make_blobs( n_samples=30, centers=[[0, 0, 0], [1, 1, 1]], random_state=0, n_features=2, cluster_std=0.1, ) X = StandardScaler().fit_transform(X) sampler = clone(sampler_orig) X = _enforce_estimator_tags_X(sampler, X) n_features = X.shape[1] set_random_state(sampler) y_ = y feature_names_in = [f"col{i}" for i in range(n_features)] df = pd.DataFrame(X, columns=feature_names_in) X_res, y_res = sampler.fit_resample(df, y=y_) # error is raised when `input_features` do not match feature_names_in invalid_feature_names = [f"bad{i}" for i in range(n_features)] with raises(ValueError, match="input_features is not equal to feature_names_in_"): sampler.get_feature_names_out(invalid_feature_names) feature_names_out_default = sampler.get_feature_names_out() feature_names_in_explicit_names = sampler.get_feature_names_out(feature_names_in) assert_array_equal(feature_names_out_default, feature_names_in_explicit_names) n_features_out = X_res.shape[1] assert ( len(feature_names_out_default) == n_features_out ), f"Expected {n_features_out} feature names, got {len(feature_names_out_default)}" scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/testing.py000066400000000000000000000122451521136113700261620ustar00rootroot00000000000000"""Test utilities.""" # Adapted from scikit-learn # Authors: Guillaume Lemaitre # License: MIT import inspect import pkgutil from importlib import import_module from operator import itemgetter from pathlib import Path import numpy as np from scipy import sparse from sklearn.base import BaseEstimator from sklearn.neighbors import KDTree from sklearn.utils._testing import ignore_warnings def all_estimators( type_filter=None, ): """Get a list of all estimators from imblearn. This function crawls the module and gets all classes that inherit from BaseEstimator. Classes that are defined in test-modules are not included. By default meta_estimators are also not included. This function is adapted from sklearn. Parameters ---------- type_filter : str, list of str, or None, default=None Which kind of estimators should be returned. If None, no filter is applied and all estimators are returned. Possible values are 'sampler' to get estimators only of these specific types, or a list of these to get the estimators that fit at least one of the types. Returns ------- estimators : list of tuples List of (name, class), where ``name`` is the class name as string and ``class`` is the actual type of the class. """ from imblearn.base import SamplerMixin def is_abstract(c): if not (hasattr(c, "__abstractmethods__")): return False if not len(c.__abstractmethods__): return False return True all_classes = [] modules_to_ignore = {"tests"} root = str(Path(__file__).parent.parent) # Ignore deprecation warnings triggered at import time and from walking # packages with ignore_warnings(category=FutureWarning): for importer, modname, ispkg in pkgutil.walk_packages( path=[root], prefix="imblearn." ): mod_parts = modname.split(".") if any(part in modules_to_ignore for part in mod_parts) or "._" in modname: continue module = import_module(modname) classes = inspect.getmembers(module, inspect.isclass) classes = [ (name, est_cls) for name, est_cls in classes if not name.startswith("_") ] all_classes.extend(classes) all_classes = set(all_classes) estimators = [ c for c in all_classes if (issubclass(c[1], BaseEstimator) and c[0] != "BaseEstimator") ] # get rid of abstract base classes estimators = [c for c in estimators if not is_abstract(c[1])] # get rid of sklearn estimators which have been imported in some classes estimators = [c for c in estimators if "sklearn" not in c[1].__module__] if type_filter is not None: if not isinstance(type_filter, list): type_filter = [type_filter] else: type_filter = list(type_filter) # copy filtered_estimators = [] filters = {"sampler": SamplerMixin} for name, mixin in filters.items(): if name in type_filter: type_filter.remove(name) filtered_estimators.extend( [est for est in estimators if issubclass(est[1], mixin)] ) estimators = filtered_estimators if type_filter: raise ValueError( f"Parameter type_filter must be 'sampler' or None, got {type_filter!r}." ) # drop duplicates, sort for reproducibility # itemgetter is used to ensure the sort does not extend to the 2nd item of # the tuple return sorted(set(estimators), key=itemgetter(0)) class _CustomNearestNeighbors(BaseEstimator): """Basic implementation of nearest neighbors not relying on scikit-learn. `kneighbors_graph` is ignored and `metric` does not have any impact. """ def __init__(self, n_neighbors=1, metric="euclidean"): self.n_neighbors = n_neighbors self.metric = metric def fit(self, X, y=None): X = X.toarray() if sparse.issparse(X) else X self._kd_tree = KDTree(X) return self def kneighbors(self, X, n_neighbors=None, return_distance=True): n_neighbors = n_neighbors if n_neighbors is not None else self.n_neighbors X = X.toarray() if sparse.issparse(X) else X distances, indices = self._kd_tree.query(X, k=n_neighbors) if return_distance: return distances, indices return indices def kneighbors_graph(X=None, n_neighbors=None, mode="connectivity"): """This method is not used within imblearn but it is required for duck-typing.""" pass class _CustomClusterer(BaseEstimator): """Class that mimics a cluster that does not expose `cluster_centers_`.""" def __init__(self, n_clusters=1, expose_cluster_centers=True): self.n_clusters = n_clusters self.expose_cluster_centers = expose_cluster_centers def fit(self, X, y=None): if self.expose_cluster_centers: self.cluster_centers_ = np.random.randn(self.n_clusters, X.shape[1]) return self def predict(self, X): return np.zeros(len(X), dtype=int) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/tests/000077500000000000000000000000001521136113700252715ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/tests/__init__.py000066400000000000000000000000001521136113700273700ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/tests/test_deprecation.py000066400000000000000000000010521521136113700311750ustar00rootroot00000000000000"""Test for the deprecation helper""" # Authors: Guillaume Lemaitre # License: MIT import pytest from imblearn.utils.deprecation import deprecate_parameter class Sampler: def __init__(self): self.a = "something" self.b = "something" def test_deprecate_parameter(): with pytest.warns(FutureWarning, match="is deprecated from"): deprecate_parameter(Sampler(), "0.2", "a") with pytest.warns(FutureWarning, match="Use 'b' instead."): deprecate_parameter(Sampler(), "0.2", "a", "b") scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/tests/test_docstring.py000066400000000000000000000037151521136113700307040ustar00rootroot00000000000000"""Test utilities for docstring.""" # Authors: Guillaume Lemaitre # License: MIT import sys import textwrap import pytest from imblearn.utils import Substitution from imblearn.utils._docstring import _n_jobs_docstring, _random_state_docstring def _dedent_docstring(docstring): """Compatibility with Python 3.13+. xref: https://github.com/python/cpython/issues/81283 """ return "\n".join([textwrap.dedent(line) for line in docstring.split("\n")]) func_docstring = """A function. Parameters ---------- xxx yyy """ def func(param_1, param_2): """A function. Parameters ---------- {param_1} {param_2} """ return param_1, param_2 cls_docstring = """A class. Parameters ---------- xxx yyy """ class cls: """A class. Parameters ---------- {param_1} {param_2} """ def __init__(self, param_1, param_2): self.param_1 = param_1 self.param_2 = param_2 if sys.version_info >= (3, 13): func_docstring = _dedent_docstring(func_docstring) cls_docstring = _dedent_docstring(cls_docstring) @pytest.mark.parametrize( "obj, obj_docstring", [(func, func_docstring), (cls, cls_docstring)] ) def test_docstring_inject(obj, obj_docstring): obj_injected_docstring = Substitution(param_1="xxx", param_2="yyy")(obj) assert obj_injected_docstring.__doc__ == obj_docstring def test_docstring_template(): assert "random_state" in _random_state_docstring assert "n_jobs" in _n_jobs_docstring def test_docstring_with_python_OO(): """Check that we don't raise a warning if the code is executed with -OO. Non-regression test for: https://github.com/scikit-learn-contrib/imbalanced-learn/issues/945 """ instance = cls(param_1="xxx", param_2="yyy") instance.__doc__ = None # simulate -OO instance = Substitution(param_1="xxx", param_2="yyy")(instance) assert instance.__doc__ is None scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/tests/test_estimator_checks.py000066400000000000000000000070171521136113700322360ustar00rootroot00000000000000import numpy as np import pytest from sklearn.base import BaseEstimator from sklearn.utils.multiclass import check_classification_targets from sklearn_compat.utils.validation import validate_data from imblearn.base import BaseSampler from imblearn.over_sampling.base import BaseOverSampler from imblearn.utils import check_target_type as target_check from imblearn.utils.estimator_checks import ( check_samplers_fit, check_samplers_nan, check_samplers_one_label, check_samplers_preserve_dtype, check_samplers_sparse, check_samplers_string, check_target_type, ) class BaseBadSampler(BaseEstimator): """Sampler without inputs checking.""" _sampling_type = "bypass" def fit(self, X, y): return self def fit_resample(self, X, y): check_classification_targets(y) self.fit(X, y) return X, y class SamplerSingleClass(BaseSampler): """Sampler that would sample even with a single class.""" _sampling_type = "bypass" def fit_resample(self, X, y): return self._fit_resample(X, y) def _fit_resample(self, X, y): return X, y class NotFittedSampler(BaseBadSampler): """Sampler without target checking.""" def fit(self, X, y): X, y = validate_data(self, X=X, y=y) return self class NoAcceptingSparseSampler(BaseBadSampler): """Sampler which does not accept sparse matrix.""" def fit(self, X, y): X, y = validate_data(self, X=X, y=y) self.sampling_strategy_ = "sampling_strategy_" return self class NotPreservingDtypeSampler(BaseSampler): _sampling_type = "bypass" _parameter_constraints: dict = {"sampling_strategy": "no_validation"} def _fit_resample(self, X, y): return X.astype(np.float64), y.astype(np.int64) class IndicesSampler(BaseOverSampler): def _check_X_y(self, X, y): y, binarize_y = target_check(y, indicate_one_vs_all=True) X, y = validate_data( self, X=X, y=y, reset=True, dtype=None, ensure_all_finite=False, ) return X, y, binarize_y def _fit_resample(self, X, y): n_max_count_class = np.bincount(y).max() indices = np.random.choice(np.arange(X.shape[0]), size=n_max_count_class * 2) return X[indices], y[indices] def test_check_samplers_string(): sampler = IndicesSampler() check_samplers_string(sampler.__class__.__name__, sampler) def test_check_samplers_nan(): sampler = IndicesSampler() check_samplers_nan(sampler.__class__.__name__, sampler) mapping_estimator_error = { "BaseBadSampler": (AssertionError, None), "SamplerSingleClass": (AssertionError, "Sampler can't balance when only"), "NotFittedSampler": (AssertionError, "No fitted attribute"), "NoAcceptingSparseSampler": (TypeError, "dense data is required"), "NotPreservingDtypeSampler": (AssertionError, "X dtype is not preserved"), } def _test_single_check(Estimator, check): estimator = Estimator() name = estimator.__class__.__name__ err_type, err_msg = mapping_estimator_error[name] with pytest.raises(err_type, match=err_msg): check(name, estimator) def test_all_checks(): _test_single_check(BaseBadSampler, check_target_type) _test_single_check(SamplerSingleClass, check_samplers_one_label) _test_single_check(NotFittedSampler, check_samplers_fit) _test_single_check(NoAcceptingSparseSampler, check_samplers_sparse) _test_single_check(NotPreservingDtypeSampler, check_samplers_preserve_dtype) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/tests/test_min_dependencies.py000066400000000000000000000052711521136113700322000ustar00rootroot00000000000000"""Tests for the minimum dependencies in the README.rst file.""" import os import platform import re from pathlib import Path import pytest from packaging.requirements import Requirement from packaging.version import parse import imblearn @pytest.mark.skipif( platform.system() == "Windows" or parse(platform.python_version()) < parse("3.11"), reason="This test is enough on unix system and requires Python >= 3.11", ) def test_min_dependencies_readme(): # local import to not import the file with Python < 3.11 import tomllib # Test that the minimum dependencies in the README.rst file are # consistent with the minimum dependencies defined at the file: # pyproject.toml pyproject_path = Path(imblearn.__path__[0]).parents[0] / "pyproject.toml" with open(pyproject_path, "rb") as f: pyproject_data = tomllib.load(f) def process_requirements(requirements): result = {} for req in requirements: req = Requirement(req) for specifier in req.specifier: if specifier.operator == ">=": result[req.name] = parse(specifier.version) return result min_dependencies = process_requirements( [f"python{pyproject_data['project']['requires-python']}"] ) min_dependencies.update( process_requirements(pyproject_data["project"]["dependencies"]) ) markers = ["docs", "optional", "tensorflow", "keras", "tests"] for marker_name in markers: min_dependencies.update( process_requirements( pyproject_data["project"]["optional-dependencies"][marker_name] ) ) pattern = re.compile( r"(\.\. \|)" + r"(([A-Za-z]+\-?)+)" + r"(MinVersion\| replace::)" + r"( [0-9]+\.[0-9]+(\.[0-9]+)?)" ) readme_path = Path(imblearn.__path__[0]).parents[0] readme_file = readme_path / "README.rst" if not os.path.exists(readme_file): # Skip the test if the README.rst file is not available. # For instance, when installing scikit-learn from wheels pytest.skip("The README.rst file is not available.") with readme_file.open("r") as f: for line in f: matched = pattern.match(line) if not matched: continue package, version = matched.group(2), matched.group(5) package = package.lower() if package == "scikitlearn": package = "scikit-learn" if package in min_dependencies: version = parse(version) min_version = min_dependencies[package] assert version == min_version, f"{package} has a mismatched version" scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/tests/test_show_versions.py000066400000000000000000000034321521136113700316140ustar00rootroot00000000000000"""Test for the show_versions helper. Based on the sklearn tests.""" # Author: Alexander L. Hayes # License: MIT from imblearn.utils._show_versions import _get_deps_info, show_versions def test_get_deps_info(): _deps_info = _get_deps_info() assert "pip" in _deps_info assert "setuptools" in _deps_info assert "imbalanced-learn" in _deps_info assert "scikit-learn" in _deps_info assert "numpy" in _deps_info assert "scipy" in _deps_info assert "Cython" in _deps_info assert "pandas" in _deps_info assert "joblib" in _deps_info def test_show_versions_default(capsys): show_versions() out, err = capsys.readouterr() assert "python" in out assert "executable" in out assert "machine" in out assert "pip" in out assert "setuptools" in out assert "imbalanced-learn" in out assert "scikit-learn" in out assert "numpy" in out assert "scipy" in out assert "Cython" in out assert "pandas" in out assert "keras" in out assert "tensorflow" in out assert "joblib" in out def test_show_versions_github(capsys): show_versions(github=True) out, err = capsys.readouterr() assert "
System, Dependency Information" in out assert "**System Information**" in out assert "* python" in out assert "* executable" in out assert "* machine" in out assert "**Python Dependencies**" in out assert "* pip" in out assert "* setuptools" in out assert "* imbalanced-learn" in out assert "* scikit-learn" in out assert "* numpy" in out assert "* scipy" in out assert "* Cython" in out assert "* pandas" in out assert "* keras" in out assert "* tensorflow" in out assert "* joblib" in out assert "
" in out scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/tests/test_testing.py000066400000000000000000000032341521136113700303610ustar00rootroot00000000000000"""Test for the testing module""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT import numpy as np import pytest from sklearn.neighbors._base import KNeighborsMixin from imblearn.base import SamplerMixin from imblearn.utils.testing import _CustomNearestNeighbors, all_estimators def test_all_estimators(): # check if the filtering is working with a list or a single string type_filter = "sampler" all_estimators(type_filter=type_filter) type_filter = ["sampler"] estimators = all_estimators(type_filter=type_filter) for estimator in estimators: # check that all estimators are sampler assert issubclass(estimator[1], SamplerMixin) # check that an error is raised when the type is unknown type_filter = "rnd" with pytest.raises(ValueError, match="Parameter type_filter must be 'sampler'"): all_estimators(type_filter=type_filter) def test_custom_nearest_neighbors(): """Check that our custom nearest neighbors can be used for our internal duck-typing.""" neareat_neighbors = _CustomNearestNeighbors(n_neighbors=3) assert not isinstance(neareat_neighbors, KNeighborsMixin) assert hasattr(neareat_neighbors, "kneighbors") assert hasattr(neareat_neighbors, "kneighbors_graph") rng = np.random.RandomState(42) X = rng.randn(150, 3) y = rng.randint(0, 2, 150) neareat_neighbors.fit(X, y) distances, indices = neareat_neighbors.kneighbors(X) assert distances.shape == (150, 3) assert indices.shape == (150, 3) np.testing.assert_allclose(distances[:, 0], 0.0) np.testing.assert_allclose(indices[:, 0], np.arange(150)) scikit-learn-contrib-imbalanced-learn-8504e95/imblearn/utils/tests/test_validation.py000066400000000000000000000324611521136113700310420ustar00rootroot00000000000000"""Test for the validation helper""" # Authors: Guillaume Lemaitre # Christos Aridas # License: MIT from collections import Counter, OrderedDict import numpy as np import pytest from sklearn.cluster import KMeans from sklearn.neighbors import NearestNeighbors from sklearn.neighbors._base import KNeighborsMixin from sklearn.utils._testing import assert_array_equal from imblearn.utils import ( check_neighbors_object, check_sampling_strategy, check_target_type, ) from imblearn.utils._validation import ( ArraysTransformer, _deprecate_positional_args, _is_neighbors_object, ) from imblearn.utils.testing import _CustomNearestNeighbors multiclass_target = np.array([1] * 50 + [2] * 100 + [3] * 25) binary_target = np.array([1] * 25 + [0] * 100) def test_check_neighbors_object(): name = "n_neighbors" n_neighbors = 1 estimator = check_neighbors_object(name, n_neighbors) assert issubclass(type(estimator), KNeighborsMixin) assert estimator.n_neighbors == 1 estimator = check_neighbors_object(name, n_neighbors, 1) assert issubclass(type(estimator), KNeighborsMixin) assert estimator.n_neighbors == 2 estimator = NearestNeighbors(n_neighbors=n_neighbors) estimator_cloned = check_neighbors_object(name, estimator) assert estimator.n_neighbors == estimator_cloned.n_neighbors estimator = _CustomNearestNeighbors() estimator_cloned = check_neighbors_object(name, estimator) assert isinstance(estimator_cloned, _CustomNearestNeighbors) @pytest.mark.parametrize( "target, output_target", [ (np.array([0, 1, 1]), np.array([0, 1, 1])), (np.array([0, 1, 2]), np.array([0, 1, 2])), (np.array([[0, 1], [1, 0]]), np.array([1, 0])), ], ) def test_check_target_type(target, output_target): converted_target = check_target_type(target.astype(int)) assert_array_equal(converted_target, output_target.astype(int)) @pytest.mark.parametrize( "target, output_target, is_ova", [ (np.array([0, 1, 1]), np.array([0, 1, 1]), False), (np.array([0, 1, 2]), np.array([0, 1, 2]), False), (np.array([[0, 1], [1, 0]]), np.array([1, 0]), True), ], ) def test_check_target_type_ova(target, output_target, is_ova): converted_target, binarize_target = check_target_type( target.astype(int), indicate_one_vs_all=True ) assert_array_equal(converted_target, output_target.astype(int)) assert binarize_target == is_ova def test_check_sampling_strategy_warning(): msg = "dict for cleaning methods is not supported" with pytest.raises(ValueError, match=msg): check_sampling_strategy({1: 0, 2: 0, 3: 0}, multiclass_target, "clean-sampling") @pytest.mark.parametrize( "ratio, y, type, err_msg", [ ( 0.5, binary_target, "clean-sampling", "'clean-sampling' methods do let the user specify the sampling ratio", # noqa ), ( 0.1, np.array([0] * 10 + [1] * 20), "over-sampling", "remove samples from the minority class while trying to generate new", # noqa ), ( 0.1, np.array([0] * 10 + [1] * 20), "under-sampling", "generate new sample in the majority class while trying to remove", ), ], ) def test_check_sampling_strategy_float_error(ratio, y, type, err_msg): with pytest.raises(ValueError, match=err_msg): check_sampling_strategy(ratio, y, type) def test_check_sampling_strategy_error(): with pytest.raises(ValueError, match="'sampling_type' should be one of"): check_sampling_strategy("auto", np.array([1, 2, 3]), "rnd") error_regex = "The target 'y' needs to have more than 1 class." with pytest.raises(ValueError, match=error_regex): check_sampling_strategy("auto", np.ones((10,)), "over-sampling") error_regex = "When 'sampling_strategy' is a string, it needs to be one of" with pytest.raises(ValueError, match=error_regex): check_sampling_strategy("rnd", np.array([1, 2, 3]), "over-sampling") @pytest.mark.parametrize( "sampling_strategy, sampling_type, err_msg", [ ("majority", "over-sampling", "over-sampler"), ("minority", "under-sampling", "under-sampler"), ], ) def test_check_sampling_strategy_error_wrong_string( sampling_strategy, sampling_type, err_msg ): with pytest.raises( ValueError, match=f"'{sampling_strategy}' cannot be used with {err_msg}", ): check_sampling_strategy(sampling_strategy, np.array([1, 2, 3]), sampling_type) @pytest.mark.parametrize( "sampling_strategy, sampling_method", [ ({10: 10}, "under-sampling"), ({10: 10}, "over-sampling"), ([10], "clean-sampling"), ], ) def test_sampling_strategy_class_target_unknown(sampling_strategy, sampling_method): y = np.array([1] * 50 + [2] * 100 + [3] * 25) with pytest.raises(ValueError, match="are not present in the data."): check_sampling_strategy(sampling_strategy, y, sampling_method) def test_sampling_strategy_dict_error(): y = np.array([1] * 50 + [2] * 100 + [3] * 25) sampling_strategy = {1: -100, 2: 50, 3: 25} with pytest.raises(ValueError, match="in a class cannot be negative."): check_sampling_strategy(sampling_strategy, y, "under-sampling") sampling_strategy = {1: 45, 2: 100, 3: 70} error_regex = ( "With over-sampling methods, the number of samples in a" " class should be greater or equal to the original number" " of samples. Originally, there is 50 samples and 45" " samples are asked." ) with pytest.raises(ValueError, match=error_regex): check_sampling_strategy(sampling_strategy, y, "over-sampling") error_regex = ( "With under-sampling methods, the number of samples in a" " class should be less or equal to the original number of" " samples. Originally, there is 25 samples and 70 samples" " are asked." ) with pytest.raises(ValueError, match=error_regex): check_sampling_strategy(sampling_strategy, y, "under-sampling") @pytest.mark.parametrize("sampling_strategy", [-10, 10]) def test_sampling_strategy_float_error_not_in_range(sampling_strategy): y = np.array([1] * 50 + [2] * 100) with pytest.raises(ValueError, match="it should be in the range"): check_sampling_strategy(sampling_strategy, y, "under-sampling") def test_sampling_strategy_float_error_not_binary(): y = np.array([1] * 50 + [2] * 100 + [3] * 25) with pytest.raises(ValueError, match="the type of target is binary"): sampling_strategy = 0.5 check_sampling_strategy(sampling_strategy, y, "under-sampling") @pytest.mark.parametrize("sampling_method", ["over-sampling", "under-sampling"]) def test_sampling_strategy_list_error_not_clean_sampling(sampling_method): y = np.array([1] * 50 + [2] * 100 + [3] * 25) with pytest.raises(ValueError, match="cannot be a list for samplers"): sampling_strategy = [1, 2, 3] check_sampling_strategy(sampling_strategy, y, sampling_method) def _sampling_strategy_func(y): # this function could create an equal number of samples target_stats = Counter(y) n_samples = max(target_stats.values()) return {key: int(n_samples) for key in target_stats.keys()} @pytest.mark.parametrize( "sampling_strategy, sampling_type, expected_sampling_strategy, target", [ ("auto", "under-sampling", {1: 25, 2: 25}, multiclass_target), ("auto", "clean-sampling", {1: 25, 2: 25}, multiclass_target), ("auto", "over-sampling", {1: 50, 3: 75}, multiclass_target), ("all", "over-sampling", {1: 50, 2: 0, 3: 75}, multiclass_target), ("all", "under-sampling", {1: 25, 2: 25, 3: 25}, multiclass_target), ("all", "clean-sampling", {1: 25, 2: 25, 3: 25}, multiclass_target), ("majority", "under-sampling", {2: 25}, multiclass_target), ("majority", "clean-sampling", {2: 25}, multiclass_target), ("minority", "over-sampling", {3: 75}, multiclass_target), ("not minority", "over-sampling", {1: 50, 2: 0}, multiclass_target), ("not minority", "under-sampling", {1: 25, 2: 25}, multiclass_target), ("not minority", "clean-sampling", {1: 25, 2: 25}, multiclass_target), ("not majority", "over-sampling", {1: 50, 3: 75}, multiclass_target), ("not majority", "under-sampling", {1: 25, 3: 25}, multiclass_target), ("not majority", "clean-sampling", {1: 25, 3: 25}, multiclass_target), ( {1: 70, 2: 100, 3: 70}, "over-sampling", {1: 20, 2: 0, 3: 45}, multiclass_target, ), ( {1: 30, 2: 45, 3: 25}, "under-sampling", {1: 30, 2: 45, 3: 25}, multiclass_target, ), ([1], "clean-sampling", {1: 25}, multiclass_target), ( _sampling_strategy_func, "over-sampling", {1: 50, 2: 0, 3: 75}, multiclass_target, ), (0.5, "over-sampling", {1: 25}, binary_target), (0.5, "under-sampling", {0: 50}, binary_target), ], ) def test_check_sampling_strategy( sampling_strategy, sampling_type, expected_sampling_strategy, target ): sampling_strategy_ = check_sampling_strategy( sampling_strategy, target, sampling_type ) assert sampling_strategy_ == expected_sampling_strategy def test_sampling_strategy_callable_args(): y = np.array([1] * 50 + [2] * 100 + [3] * 25) multiplier = {1: 1.5, 2: 1, 3: 3} def sampling_strategy_func(y, multiplier): """samples such that each class will be affected by the multiplier.""" target_stats = Counter(y) return { key: int(values * multiplier[key]) for key, values in target_stats.items() } sampling_strategy_ = check_sampling_strategy( sampling_strategy_func, y, "over-sampling", multiplier=multiplier ) assert sampling_strategy_ == {1: 25, 2: 0, 3: 50} @pytest.mark.parametrize( "sampling_strategy, sampling_type, expected_result", [ ( {3: 25, 1: 25, 2: 25}, "under-sampling", OrderedDict({1: 25, 2: 25, 3: 25}), ), ( {3: 100, 1: 100, 2: 100}, "over-sampling", OrderedDict({1: 50, 2: 0, 3: 75}), ), ], ) def test_sampling_strategy_check_order( sampling_strategy, sampling_type, expected_result ): # We pass on purpose a non sorted dictionary and check that the resulting # dictionary is sorted. Refer to issue #428. y = np.array([1] * 50 + [2] * 100 + [3] * 25) sampling_strategy_ = check_sampling_strategy(sampling_strategy, y, sampling_type) assert sampling_strategy_ == expected_result def test_arrays_transformer_plain_list(): X = np.array([[0, 0], [1, 1]]) y = np.array([[0, 0], [1, 1]]) arrays_transformer = ArraysTransformer(X.tolist(), y.tolist()) X_res, y_res = arrays_transformer.transform(X, y) assert isinstance(X_res, list) assert isinstance(y_res, list) def test_arrays_transformer_numpy(): X = np.array([[0, 0], [1, 1]]) y = np.array([[0, 0], [1, 1]]) arrays_transformer = ArraysTransformer(X, y) X_res, y_res = arrays_transformer.transform(X, y) assert isinstance(X_res, np.ndarray) assert isinstance(y_res, np.ndarray) def test_arrays_transformer_pandas(): pd = pytest.importorskip("pandas") X = np.array([[0, 0], [1, 1]]) y = np.array([0, 1]) X_df = pd.DataFrame(X, columns=["a", "b"]) X_df = X_df.astype(int) y_df = pd.DataFrame(y, columns=["target"]) y_df = y_df.astype(int) y_s = pd.Series(y, name="target", dtype=int) # DataFrame and DataFrame case arrays_transformer = ArraysTransformer(X_df, y_df) X_res, y_res = arrays_transformer.transform(X, y) assert isinstance(X_res, pd.DataFrame) assert_array_equal(X_res.columns, X_df.columns) assert_array_equal(X_res.dtypes, X_df.dtypes) assert isinstance(y_res, pd.DataFrame) assert_array_equal(y_res.columns, y_df.columns) assert_array_equal(y_res.dtypes, y_df.dtypes) # DataFrames and Series case arrays_transformer = ArraysTransformer(X_df, y_s) _, y_res = arrays_transformer.transform(X, y) assert isinstance(y_res, pd.Series) assert_array_equal(y_res.name, y_s.name) assert_array_equal(y_res.dtype, y_s.dtype) def test_deprecate_positional_args_warns_for_function(): @_deprecate_positional_args def f1(a, b, *, c=1, d=1): pass with pytest.warns(FutureWarning, match=r"Pass c=3 as keyword args"): f1(1, 2, 3) with pytest.warns(FutureWarning, match=r"Pass c=3, d=4 as keyword args"): f1(1, 2, 3, 4) @_deprecate_positional_args def f2(a=1, *, b=1, c=1, d=1): pass with pytest.warns(FutureWarning, match=r"Pass b=2 as keyword args"): f2(1, 2) # The * is place before a keyword only argument without a default value @_deprecate_positional_args def f3(a, *, b, c=1, d=1): pass with pytest.warns(FutureWarning, match=r"Pass b=2 as keyword args"): f3(1, 2) @pytest.mark.parametrize( "estimator, is_neighbor_estimator", [(NearestNeighbors(), True), (KMeans(), False)] ) def test_is_neighbors_object(estimator, is_neighbor_estimator): assert _is_neighbors_object(estimator) == is_neighbor_estimator scikit-learn-contrib-imbalanced-learn-8504e95/maint_tools/000077500000000000000000000000001521136113700235265ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-8504e95/maint_tools/test_docstring.py000066400000000000000000000212161521136113700271350ustar00rootroot00000000000000import importlib import inspect import pkgutil import re from inspect import signature import pytest import imblearn from imblearn.utils.testing import all_estimators numpydoc_validation = pytest.importorskip("numpydoc.validate") # List of whitelisted modules and methods; regexp are supported. # These docstrings will fail because they are inheriting from scikit-learn DOCSTRING_WHITELIST = [ "ADASYN$", "ADASYN.", "AllKNN$", "AllKNN.", "BalancedBaggingClassifier$", "BalancedBaggingClassifier.", "BalancedRandomForestClassifier$", "BalancedRandomForestClassifier.", "ClusterCentroids$", "ClusterCentroids.", "CondensedNearestNeighbour$", "CondensedNearestNeighbour.", "EasyEnsembleClassifier$", "EasyEnsembleClassifier.", "EditedNearestNeighbours$", "EditedNearestNeighbours.", "FunctionSampler$", "FunctionSampler.", "InstanceHardnessThreshold$", "InstanceHardnessThreshold.", "SMOTE$", "SMOTE.", "NearMiss$", "NearMiss.", "NeighbourhoodCleaningRule$", "NeighbourhoodCleaningRule.", "OneSidedSelection$", "OneSidedSelection.", "Pipeline$", "Pipeline.", "RUSBoostClassifier$", "RUSBoostClassifier.", "RandomOverSampler$", "RandomOverSampler.", "RandomUnderSampler$", "RandomUnderSampler.", "TomekLinks$", "TomekLinks", "ValueDifferenceMetric$", "ValueDifferenceMetric.", ] FUNCTION_DOCSTRING_IGNORE_LIST = [ "imblearn.tensorflow._generator.balanced_batch_generator", ] FUNCTION_DOCSTRING_IGNORE_LIST = set(FUNCTION_DOCSTRING_IGNORE_LIST) def get_all_methods(): estimators = all_estimators() for name, Estimator in estimators: if name.startswith("_"): # skip private classes continue methods = [] for name in dir(Estimator): if name.startswith("_"): continue method_obj = getattr(Estimator, name) if hasattr(method_obj, "__call__") or isinstance(method_obj, property): methods.append(name) methods.append(None) for method in sorted(methods, key=lambda x: str(x)): yield Estimator, method def _is_checked_function(item): if not inspect.isfunction(item): return False if item.__name__.startswith("_"): return False mod = item.__module__ if not mod.startswith("imblearn.") or mod.endswith("estimator_checks"): return False return True def get_all_functions_names(): """Get all public functions define in the imblearn module""" modules_to_ignore = { "tests", "estimator_checks", } all_functions_names = set() for module_finder, module_name, ispkg in pkgutil.walk_packages( path=imblearn.__path__, prefix="imblearn." ): module_parts = module_name.split(".") if ( any(part in modules_to_ignore for part in module_parts) or "._" in module_name ): continue module = importlib.import_module(module_name) functions = inspect.getmembers(module, _is_checked_function) for name, func in functions: full_name = f"{func.__module__}.{func.__name__}" all_functions_names.add(full_name) return sorted(all_functions_names) def filter_errors(errors, method, Estimator=None): """ Ignore some errors based on the method type. These rules are specific for scikit-learn.""" for code, message in errors: # We ignore following error code, # - RT02: The first line of the Returns section # should contain only the type, .. # (as we may need refer to the name of the returned # object) # - GL01: Docstring text (summary) should start in the line # immediately after the opening quotes (not in the same line, # or leaving a blank line in between) # - GL02: If there's a blank line, it should be before the # first line of the Returns section, not after (it allows to have # short docstrings for properties). if code in ["RT02", "GL01", "GL02"]: continue # Ignore PR02: Unknown parameters for properties. We sometimes use # properties for ducktyping, i.e. SGDClassifier.predict_proba if code == "PR02" and Estimator is not None and method is not None: method_obj = getattr(Estimator, method) if isinstance(method_obj, property): continue # Following codes are only taken into account for the # top level class docstrings: # - ES01: No extended summary found # - SA01: See Also section not found # - EX01: No examples section found if method is not None and code in ["EX01", "SA01", "ES01"]: continue yield code, message def repr_errors(res, estimator=None, method: str | None = None) -> str: """Pretty print original docstring and the obtained errors Parameters ---------- res : dict result of numpydoc.validate.validate estimator : {estimator, None} estimator object or None method : str if estimator is not None, either the method name or None. Returns ------- str String representation of the error. """ if method is None: if hasattr(estimator, "__init__"): method = "__init__" elif estimator is None: raise ValueError("At least one of estimator, method should be provided") else: raise NotImplementedError if estimator is not None: obj = getattr(estimator, method) try: obj_signature = signature(obj) except TypeError: # In particular we can't parse the signature of properties obj_signature = ( "\nParsing of the method signature failed, " "possibly because this is a property." ) obj_name = estimator.__name__ + "." + method else: obj_signature = "" obj_name = method msg = "\n\n" + "\n\n".join( [ str(res["file"]), obj_name + str(obj_signature), res["docstring"], "# Errors", "\n".join(f" - {code}: {message}" for code, message in res["errors"]), ] ) return msg @pytest.mark.parametrize("function_name", get_all_functions_names()) def test_function_docstring(function_name, request): """Check function docstrings using numpydoc.""" if function_name in FUNCTION_DOCSTRING_IGNORE_LIST: request.applymarker( pytest.mark.xfail(run=False, reason="TODO pass numpydoc validation") ) res = numpydoc_validation.validate(function_name) res["errors"] = list(filter_errors(res["errors"], method="function")) if res["errors"]: msg = repr_errors(res, method=f"Tested function: {function_name}") raise ValueError(msg) @pytest.mark.parametrize("Estimator, method", get_all_methods()) def test_docstring(Estimator, method, request): base_import_path = Estimator.__module__ import_path = [base_import_path, Estimator.__name__] if method is not None: import_path.append(method) import_path = ".".join(import_path) if not any(re.search(regex, import_path) for regex in DOCSTRING_WHITELIST): request.applymarker( pytest.mark.xfail(run=False, reason="TODO pass numpydoc validation") ) res = numpydoc_validation.validate(import_path) res["errors"] = list(filter_errors(res["errors"], method)) if res["errors"]: msg = repr_errors(res, Estimator, method) raise ValueError(msg) if __name__ == "__main__": import argparse import sys parser = argparse.ArgumentParser(description="Validate docstring with numpydoc.") parser.add_argument("import_path", help="Import path to validate") args = parser.parse_args() res = numpydoc_validation.validate(args.import_path) import_path_sections = args.import_path.split(".") # When applied to classes, detect class method. For functions # method = None. # TODO: this detection can be improved. Currently we assume that we have # class # methods if the second path element before last is in camel case. if len(import_path_sections) >= 2 and re.match( r"(?:[A-Z][a-z]*)+", import_path_sections[-2] ): method = import_path_sections[-1] else: method = None res["errors"] = list(filter_errors(res["errors"], method)) if res["errors"]: msg = repr_errors(res, method=args.import_path) print(msg) sys.exit(1) else: print(f"All docstring checks passed for {args.import_path}!") scikit-learn-contrib-imbalanced-learn-8504e95/pixi.lock000066400000000000000000035073051521136113700230360ustar00rootroot00000000000000version: 7 platforms: - name: linux-64 - name: osx-64 - name: osx-arm64 - name: win-64 environments: ci-py310-min-dependencies: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.1-py310h3406613_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-20_linux64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-20_linux64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-20_linux64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.25-pthreads_h413a1c8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.25.2-py310ha4c1d20_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.10.20-h3c07f61_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.4.2-py310h981052a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.11.4-py310hb13e2d6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.2.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-2.0.0-pyh5ca1d4c_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - pypi: . osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.2.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-2.0.0-pyh5ca1d4c_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.1-py310h399bfa0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.9.0-20_osx64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.9.0-20_osx64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.9.0-20_osx64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.25-openmp_hfef2a42_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h8f8c405_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-1.25.2-py310h7451ae0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.10.20-hea035f4_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.4.2-py310h9d65eca_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.11.4-py310h3f1db6d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda - pypi: . osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.2.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-2.0.0-pyh5ca1d4c_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.1-py310hb46c203_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-20_osxarm64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-20_osxarm64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-20_osxarm64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.25-openmp_h6c19121_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.25.2-py310haa1e00c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.10.20-h1b19095_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.4.2-py310h64e73be_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.11.4-py310h2b794db_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - pypi: . win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.2.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-2.0.0-pyh5ca1d4c_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coverage-7.14.1-py310hdb0e946_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-8_h8455456_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-8_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-8_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.2-hf5d6505_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h692994f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-hbc0d294_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.7-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.0.0-hac47afa_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-1.25.2-py310hd02465a_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2026.0.0-h57928b3_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.2-hf411b9b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.10.20-hc20f281_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.4.2-py310hf2a6c47_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.11.4-py310hf667824_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-h1b7c187_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_38.conda - pypi: . ci-py310-min-keras: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.3-h3aafcba_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h8e43964_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.13.1-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h16e98cb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.11.0-hcbcd92d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.26.3-h955231c_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.12.4-h00bea6e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h16e98cb_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h16e98cb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.1-py310h3406613_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.16.0-nompi_py310hb9a6f38_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-2.1.0-nompi_hee75f7e_106.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-ha1258a1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.20.0-hcf29cc6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ml_dtypes-0.5.4-np2py310h3d4ba91_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py310hb13e2d6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.19.1-py310h03d9f68_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.10.20-h3c07f61_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.3-hc5a330e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.7.2-py310h228f341_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.15.2-py310h1d65ade_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - pypi: . osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-auth-0.10.3-h32d7887_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-cal-0.9.14-hf143bd1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-common-0.13.1-ha1e9b39_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-compression-0.3.2-h36b48e2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-http-0.11.0-h2d71af0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-io-0.26.3-h7049b4b_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-s3-0.12.4-h8390798_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-sdkutils-0.2.4-h36b48e2_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-checksums-0.2.10-h36b48e2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/c-ares-1.34.6-hb5e19a0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.1-py310h399bfa0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.16.0-nompi_py310h05ade02_102.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-2.1.0-nompi_hf2f48e3_106.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.22.2-h207b36a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libaec-1.1.5-he7c3a48_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-8_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-8_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.20.0-h8f0b9e4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libedit-3.1.20250104-pl5321ha958ccf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-8_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h8f8c405_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libssh2-1.11.1-hed3591d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ml_dtypes-0.5.4-np2py310hafb1f31_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-1.26.4-py310h4bfa8fc_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/optree-0.19.1-py310h2b03148_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.10.20-hea035f4_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.7.2-py310h4e9a27a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.15.2-py310hef62574_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: . osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.3-h1e0905d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-h2370581_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.13.1-h84a0fba_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-h729ab01_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.11.0-h198760e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.26.3-hee75b50_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.12.4-h45371db_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.4-h729ab01_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-h729ab01_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.1-py310hb46c203_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.16.0-nompi_py310hcf53ec8_102.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-2.1.0-nompi_h262911d_106.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h385eeb1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.20.0-hd5a2499_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ml_dtypes-0.5.4-np2py310h4beac76_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py310hd45542a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.19.1-py310h1c35771_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.10.20-h1b19095_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.7.2-py310h660f142_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.15.2-py310h32ab4ed_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: . ci-py310-min-optional-dependencies: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.1-py310h3406613_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-20_linux64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-20_linux64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-20_linux64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.25-pthreads_h413a1c8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.25.2-py310ha4c1d20_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.0.3-py310h7cbd5c2_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.10.20-h3c07f61_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.4.2-py310h981052a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.11.4-py310hb13e2d6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.2.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-2.0.0-pyh5ca1d4c_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - pypi: . osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.2.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-2.0.0-pyh5ca1d4c_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.1-py310h399bfa0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.9.0-20_osx64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.9.0-20_osx64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.9.0-20_osx64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.25-openmp_hfef2a42_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h8f8c405_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-1.25.2-py310h7451ae0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pandas-2.0.3-py310h5e4fcda_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.10.20-hea035f4_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.4.2-py310h9d65eca_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.11.4-py310h3f1db6d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda - pypi: . osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.2.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-2.0.0-pyh5ca1d4c_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.1-py310hb46c203_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-20_osxarm64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-20_osxarm64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-20_osxarm64_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.25-openmp_h6c19121_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.25.2-py310haa1e00c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.0.3-py310h1cdf563_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.10.20-h1b19095_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.4.2-py310h64e73be_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.11.4-py310h2b794db_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - pypi: . win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.2.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-2.0.0-pyh5ca1d4c_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coverage-7.14.1-py310hdb0e946_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-8_h8455456_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-8_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-8_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.2-hf5d6505_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h692994f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-hbc0d294_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.7-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.0.0-hac47afa_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-1.25.2-py310hd02465a_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2026.0.0-h57928b3_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.2-hf411b9b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-2.0.3-py310h1c4a608_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.10.20-hc20f281_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.4.2-py310hf2a6c47_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.11.4-py310hf667824_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-h1b7c187_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_38.conda - pypi: . ci-py310-min-tensorflow: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.3-h3aafcba_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h8e43964_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.13.1-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h16e98cb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.11.0-hcbcd92d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.26.3-h955231c_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.12.4-h00bea6e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h16e98cb_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h16e98cb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.5.0-py310h69bd2ac_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py310hba01987_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.1-py310h3406613_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/flatbuffers-24.3.25-h59595ed_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.62.2-py310h1b8f574_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.16.0-nompi_py310hb9a6f38_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-2.1.0-nompi_hee75f7e_106.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-73.2-h59595ed_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-ha1258a1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240116.2-cxx17_he02047a_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.20.0-hcf29cc6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.62.2-h15f2491_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-4.25.3-hd5b35b9_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2023.09.01-h5a48ba9_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py310h3406613_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ml_dtypes-0.3.2-py310hcc13569_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py310hb13e2d6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.19.1-py310h03d9f68_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-4.25.3-py310h0e2eeba_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.10.20-h3c07f61_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2023.09.01-h7f4b329_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.3-hc5a330e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.7.2-py310h228f341_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.15.2-py310h1d65ade_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorboard-data-server-0.7.0-py310hf0f1799_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-2.16.1-cpu_py310h49b650b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-base-2.16.1-cpu_py310h224022f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-estimator-2.16.1-cpu_py310hc6dcfef_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.2.1-py310h7c4b9e2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/astunparse-1.6.3-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.17-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum-3.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-25.9.23-pyh1e1bc0e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.16.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.8-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: . osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/astunparse-1.6.3-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.17-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum-3.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-25.9.23-pyh1e1bc0e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.16.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.8-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-auth-0.10.3-h32d7887_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-cal-0.9.14-hf143bd1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-common-0.13.1-ha1e9b39_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-compression-0.3.2-h36b48e2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-http-0.11.0-h2d71af0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-io-0.26.3-h7049b4b_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-s3-0.12.4-h8390798_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-sdkutils-0.2.4-h36b48e2_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-checksums-0.2.10-h36b48e2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/backports.zstd-1.5.0-py310h3e16e02_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-python-1.2.0-py310hab27952_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/c-ares-1.34.6-hb5e19a0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.1-py310h399bfa0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/flatbuffers-24.3.25-h73e2aa4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/giflib-5.2.2-h10d778d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/grpcio-1.62.2-py310h271164d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.16.0-nompi_py310h05ade02_102.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-2.1.0-nompi_hf2f48e3_106.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-73.2-hf5e326d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.22.2-h207b36a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20240116.2-cxx17_hf036a51_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libaec-1.1.5-he7c3a48_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-8_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-8_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.20.0-h8f0b9e4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libedit-3.1.20250104-pl5321ha958ccf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgrpc-1.62.2-h384b2fc_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libjpeg-turbo-3.1.4.1-ha1e9b39_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-8_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.58-he930e7c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libprotobuf-4.25.3-hd4aba4c_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libre2-11-2023.09.01-h81f5012_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h77d7759_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libssh2-1.11.1-hed3591d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py310h399bfa0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ml_dtypes-0.3.2-py310h276d7da_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-1.26.4-py310h4bfa8fc_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/optree-0.19.1-py310h2b03148_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/protobuf-4.25.3-py310h533c97b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.10.20-hea035f4_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/re2-2023.09.01-hb168e87_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.7.2-py310h4e9a27a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.15.2-py310hef62574_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/snappy-1.2.2-h01f5ddf_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorboard-data-server-0.7.0-py310hdc94fca_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-2.16.1-cpu_py310hf35338a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-base-2.16.1-cpu_py310h3690a3c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-estimator-2.16.1-cpu_py310h0134bc7_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.2.1-py310h5cd8a12_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: . osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/astunparse-1.6.3-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.17-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.3.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum-3.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-25.9.23-pyh1e1bc0e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.16.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.8-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.3-h1e0905d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-h2370581_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.13.1-h84a0fba_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-h729ab01_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.11.0-h198760e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.26.3-hee75b50_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.12.4-h45371db_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.4-h729ab01_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-h729ab01_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.5.0-py310he3b5eba_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py310h6123dab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.1-py310hb46c203_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/flatbuffers-24.3.25-hebf3989_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/grpcio-1.62.2-py310hf7687f1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.16.0-nompi_py310hcf53ec8_102.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-2.1.0-nompi_h262911d_106.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-73.2-hc8870d7_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h385eeb1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240116.2-cxx17_h00cdb27_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.20.0-hd5a2499_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.62.2-h9c18a4f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-4.25.3-hc39d83c_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2023.09.01-h7b2c953_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1b79a29_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py310hb46c203_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ml_dtypes-0.3.2-py310h401b61c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py310hd45542a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.19.1-py310h1c35771_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-4.25.3-py310ha1b16c5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.10.20-h1b19095_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2023.09.01-h4cba328_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.7.2-py310h660f142_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.15.2-py310h32ab4ed_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorboard-data-server-0.7.0-py310h0cf7f6c_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-2.16.1-cpu_py310h5a6a72a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-base-2.16.1-cpu_py310hf600a1e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-estimator-2.16.1-cpu_py310h85e80fb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.2.1-py310h72544b6_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: . ci-py311-latest-keras: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.3-h3aafcba_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h8e43964_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.13.1-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h16e98cb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.11.0-hcbcd92d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.26.3-h955231c_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.12.4-h00bea6e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h16e98cb_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h16e98cb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.1-py311h3778330_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.16.0-nompi_py311hfef529e_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-2.1.0-nompi_hee75f7e_106.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-ha1258a1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.20.0-hcf29cc6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ml_dtypes-0.5.4-np2py311h912ec1f_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py311h2e04523_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.19.1-py311hdf67eae_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.15-hd63d673_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.3-hc5a330e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py311ha15b03d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py311hbe70eeb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.14.1-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - pypi: . osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.14.1-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-auth-0.10.3-h32d7887_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-cal-0.9.14-hf143bd1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-common-0.13.1-ha1e9b39_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-compression-0.3.2-h36b48e2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-http-0.11.0-h2d71af0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-io-0.26.3-h7049b4b_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-s3-0.12.4-h8390798_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-sdkutils-0.2.4-h36b48e2_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-checksums-0.2.10-h36b48e2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/c-ares-1.34.6-hb5e19a0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.1-py311ha8ae342_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.16.0-nompi_py311ha7e68cf_102.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-2.1.0-nompi_hf2f48e3_106.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.22.2-h207b36a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libaec-1.1.5-he7c3a48_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-8_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-8_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.20.0-h8f0b9e4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libedit-3.1.20250104-pl5321ha958ccf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-8_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h8f8c405_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libssh2-1.11.1-hed3591d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ml_dtypes-0.5.4-np2py311hca72124_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.4.6-py311h2c4eb96_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/optree-0.19.1-py311hd8befaf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.11.15-ha9537fe_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.9.0-np2py311he512aeb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.17.1-py311h556693a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: . osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.14.1-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.3-h1e0905d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-h2370581_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.13.1-h84a0fba_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-h729ab01_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.11.0-h198760e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.26.3-hee75b50_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.12.4-h45371db_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.4-h729ab01_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-h729ab01_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.1-py311hc290fe0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.16.0-nompi_py311hf10ccac_102.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-2.1.0-nompi_h262911d_106.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h385eeb1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.20.0-hd5a2499_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ml_dtypes-0.5.4-np2py311hb7ce6e1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py311hbd1492f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.19.1-py311h572238d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.15-h8561d8f_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py311hf1dd2ad_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py311h9a58382_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: . ci-py311-latest-tensorflow: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.3-h3aafcba_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h8e43964_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.13.1-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h16e98cb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.11.0-hcbcd92d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.26.3-h955231c_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.12.4-h00bea6e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h16e98cb_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h16e98cb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.5.0-py311h6b1f9c4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py311h66f275b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.1-py311h3778330_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/flatbuffers-25.9.23-hb7d4c21_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.78.1-py311h3aa0767_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.16.0-nompi_py311hfef529e_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-2.1.0-nompi_hee75f7e_106.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-ha1258a1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.20.0-hcf29cc6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.78.1-h1d1128b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.33.5-h6eeba95_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h0dc7533_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_cc-2.19.1-cpu_ha812d39_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_framework-2.19.1-cpu_h0a5eddc_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py311h3778330_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ml_dtypes-0.5.4-np2py311h912ec1f_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py311h2e04523_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.19.1-py311hdf67eae_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-6.33.5-py311h3f0a9aa_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.15-hd63d673_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.3-hc5a330e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py311ha15b03d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py311hbe70eeb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorboard-data-server-0.7.0-py311h97c413e_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-2.19.1-cpu_py311hae6871b_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-base-2.19.1-cpu_py311hca13484_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.2.1-py311h49ec1c0_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/_x86_64-microarch-level-1-3_x86_64.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/astunparse-1.6.3-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.17-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.8.0-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libml_dtypes-headers-0.5.4-h707e725_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum-3.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-25.9.23-pyh1e1bc0e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.19.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.8-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: . osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/astunparse-1.6.3-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.17-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.8.0-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libml_dtypes-headers-0.5.4-h707e725_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum-3.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-25.9.23-pyh1e1bc0e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.18.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.8-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-auth-0.10.3-h32d7887_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-cal-0.9.14-hf143bd1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-common-0.13.1-ha1e9b39_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-compression-0.3.2-h36b48e2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-http-0.11.0-h2d71af0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-io-0.26.3-h7049b4b_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-s3-0.12.4-h8390798_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-sdkutils-0.2.4-h36b48e2_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-checksums-0.2.10-h36b48e2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/backports.zstd-1.5.0-py311h1fd9c89_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-python-1.2.0-py311h7e844b6_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/c-ares-1.34.6-hb5e19a0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.1-py311ha8ae342_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/flatbuffers-24.12.23-hd70532e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/giflib-5.2.2-h10d778d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/grpcio-1.67.1-py311ha489736_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.16.0-nompi_py311ha7e68cf_102.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-2.1.0-nompi_hf2f48e3_106.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.22.2-h207b36a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20240722.0-cxx17_h0e468a2_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libaec-1.1.5-he7c3a48_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-8_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-8_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.20.0-h8f0b9e4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libedit-3.1.20250104-pl5321ha958ccf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgrpc-1.67.1-h4896ac0_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libjpeg-turbo-3.1.4.1-ha1e9b39_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-8_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.58-he930e7c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libprotobuf-5.28.3-h6401091_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libre2-11-2024.07.02-h0e468a2_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h77d7759_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libssh2-1.11.1-hed3591d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libtensorflow_cc-2.18.0-cpu_hce6ddfb_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libtensorflow_framework-2.18.0-cpu_h38b2b02_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py311ha8ae342_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ml_dtypes-0.4.0-py311haeb46be_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.4.6-py311h2c4eb96_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/optree-0.19.1-py311hd8befaf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/protobuf-5.28.3-py311hc356e98_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.11.15-ha9537fe_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/re2-2024.07.02-ha5e900a_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.9.0-np2py311he512aeb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.17.1-py311h556693a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/snappy-1.2.2-h01f5ddf_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorboard-data-server-0.7.0-py311h88b0467_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-2.18.0-cpu_py311h69da8e3_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-base-2.18.0-cpu_py311h8bba074_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-estimator-2.18.0-cpu_py311hc2375f7_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.2.1-py311h0a33410_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: . osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/astunparse-1.6.3-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.17-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.8.0-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libml_dtypes-headers-0.5.4-h707e725_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum-3.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-25.9.23-pyh1e1bc0e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.19.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.8-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.3-h1e0905d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-h2370581_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.13.1-h84a0fba_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-h729ab01_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.11.0-h198760e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.26.3-hee75b50_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.12.4-h45371db_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.4-h729ab01_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-h729ab01_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.5.0-py311h36d4fbb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py311hdc60ec4_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.1-py311hc290fe0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/flatbuffers-25.9.23-h9e8ef45_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/grpcio-1.78.1-py311h8fd116a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.16.0-nompi_py311hf10ccac_102.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-2.1.0-nompi_h262911d_106.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h385eeb1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.20.0-hd5a2499_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.78.1-h3e3f78d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-6.33.5-h2d4b707_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h4c27e2a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtensorflow_cc-2.19.1-cpu_he004e32_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtensorflow_framework-2.19.1-cpu_h3441331_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py311hc290fe0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ml_dtypes-0.5.4-np2py311hb7ce6e1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py311hbd1492f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.19.1-py311h572238d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-6.33.5-py311h12b56a6_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.15-h8561d8f_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-ha480c28_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py311hf1dd2ad_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py311h9a58382_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorboard-data-server-0.7.0-py311h806dddb_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-2.19.1-cpu_py311he75e561_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-base-2.19.1-cpu_py311hf3798af_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.2.1-py311hc949640_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: . ci-py311-sklearn-1-4: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.1-py311h3778330_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py311h2e04523_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.15-hd63d673_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.4.2-py311he08f58d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.15.2-py311h8f841c2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - pypi: . osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.1-py311ha8ae342_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-8_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-8_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-8_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h8f8c405_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.4.6-py311h2c4eb96_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.11.15-ha9537fe_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.4.2-py311h3c3ac6d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.15.2-py311h0c91ca8_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda - pypi: . osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.1-py311hc290fe0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py311hbd1492f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.15-h8561d8f_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.4.2-py311hbfb48bc_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.15.2-py311h0675101_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - pypi: . win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coverage-7.14.1-py311h3f79411_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-8_h8455456_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-8_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-8_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.2-hf5d6505_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h692994f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-hbc0d294_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.7-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.0.0-hac47afa_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.4.6-py311h65cb7f3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2026.0.0-h57928b3_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.2-hf411b9b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.11.15-h0159041_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.4.2-py311hdcb8d17_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.15.2-py311h99d06ae_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-h1b7c187_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_38.conda - pypi: . ci-py311-sklearn-1-5: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.1-py311h3778330_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py311h2e04523_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.15-hd63d673_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.5.2-py311h57cc02b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.15.2-py311h8f841c2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - pypi: . osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.1-py311ha8ae342_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-8_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-8_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-8_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h8f8c405_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.4.6-py311h2c4eb96_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.11.15-ha9537fe_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.5.2-py311ha1d5734_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.15.2-py311h0c91ca8_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda - pypi: . osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.1-py311hc290fe0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py311hbd1492f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.15-h8561d8f_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.5.2-py311h9e23f0f_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.15.2-py311h0675101_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - pypi: . win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coverage-7.14.1-py311h3f79411_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-8_h8455456_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-8_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-8_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.2-hf5d6505_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h692994f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-hbc0d294_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.7-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.0.0-hac47afa_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.4.6-py311h65cb7f3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2026.0.0-h57928b3_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.2-hf411b9b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.11.15-h0159041_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.5.2-py311hdcb8d17_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.15.2-py311h99d06ae_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-h1b7c187_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_38.conda - pypi: . ci-py312-sklearn-1-6: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.1-py312h8a5da7c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.6.1-py312h7a48858_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.15.2-py312ha707e6e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - pypi: . osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.1-py312heb39f77_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-8_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-8_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-8_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h8f8c405_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.4.6-py312h746d82c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.12.13-ha9537fe_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.6.1-py312he1a5313_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.15.2-py312hd04560d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda - pypi: . osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.1-py312h04c11ed_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.6.1-py312h39203ce_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.15.2-py312h99a188d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - pypi: . win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coverage-7.14.1-py312h05f76fc_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-8_h8455456_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-8_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-8_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.2-hf5d6505_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h692994f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-hbc0d294_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.7-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.0.0-hac47afa_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.4.6-py312ha3f287d_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2026.0.0-h57928b3_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.2-hf411b9b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.12.13-h0159041_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.6.1-py312h816cc57_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.15.2-py312h451d5c4_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-h1b7c187_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_38.conda - pypi: . ci-py314-latest-dependencies: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.1-py314h67df5f8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py314h2b28147_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.5-habeac84_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py314hf09ca88_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py314hf07bd8e_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - pypi: . osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.1-py314h77fa6c7_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-8_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-8_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-8_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-hf3981d6_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h8f8c405_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.4.6-py314h7b24d9b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.14.5-h7c6738f_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.9.0-np2py314h67cc4f9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.17.1-py314h5727af0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: . osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.1-py314h6e9b3f0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py314hb79c6fa_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.5-h4c637c5_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py314h15f0f0f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py314h18e1515_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: . win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coverage-7.14.1-py314h2359020_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-8_h8455456_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-8_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-8_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.2-hf5d6505_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h692994f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-hbc0d294_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.7-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.0.0-hac47afa_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.4.6-py314h02f10f6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2026.0.0-h57928b3_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.2-hf411b9b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.5-h4b44e0e_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314h1b5b07a_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.17.1-py314h221f224_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-h1b7c187_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - pypi: . ci-py314-latest-optional-dependencies: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.1-py314h67df5f8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py314h2b28147_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py314ha0b5721_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.5-habeac84_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py314hf09ca88_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py314hf07bd8e_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - pypi: . osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.1-py314h77fa6c7_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-8_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-8_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-8_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-hf3981d6_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h8f8c405_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.4.6-py314h7b24d9b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pandas-2.3.3-py314hc4308db_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.14.5-h7c6738f_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.9.0-np2py314h67cc4f9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.17.1-py314h5727af0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: . osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.1-py314h6e9b3f0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py314hb79c6fa_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.3.3-py314ha3d490a_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.5-h4c637c5_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py314h15f0f0f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py314h18e1515_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: . win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/win-64/coverage-7.14.1-py314h2359020_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-8_h8455456_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-8_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-8_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.2-hf5d6505_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h692994f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-hbc0d294_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.7-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.0.0-hac47afa_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.4.6-py314h02f10f6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2026.0.0-h57928b3_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.2-hf411b9b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-2.3.3-py314hd8fd7ce_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.5-h4b44e0e_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314h1b5b07a_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.17.1-py314h221f224_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-h1b7c187_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - pypi: . default: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py314h2b28147_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.5-habeac84_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py314hf09ca88_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py314hf07bd8e_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - pypi: . osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-8_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-8_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-8_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-hf3981d6_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h8f8c405_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.4.6-py314h7b24d9b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.14.5-h7c6738f_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.9.0-np2py314h67cc4f9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.17.1-py314h5727af0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: . osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py314hb79c6fa_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.5-h4c637c5_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py314h15f0f0f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py314h18e1515_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: . win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-8_h8455456_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-8_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-8_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.2-hf5d6505_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h692994f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-hbc0d294_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.7-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.0.0-hac47afa_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.4.6-py314h02f10f6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2026.0.0-h57928b3_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.2-hf411b9b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.5-h4b44e0e_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314h1b5b07a_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.17.1-py314h221f224_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-h1b7c187_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - pypi: . dev: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py311h49ec1c0_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.3-h3aafcba_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h8e43964_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.13.1-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h16e98cb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.11.0-hcbcd92d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.26.3-h955231c_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.12.4-h00bea6e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h16e98cb_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h16e98cb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.5.0-py311h6b1f9c4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/black-23.3.0-py311h38be061_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py311h66f275b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-he90730b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py311h03d9500_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cmarkgfm-2024.11.20-py311h49ec1c0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py311h724c32c_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.1-py311h3778330_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-48.0.0-py311h2005dd1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.21-py311hc665b79_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/flatbuffers-25.9.23-hb7d4c21_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.1-h27c8c51_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py311h3778330_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.78.1-py311h3aa0767_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.16.0-nompi_py311hfef529e_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.2.1-h6083320_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-2.1.0-nompi_hee75f7e_106.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.0-py311h724c32c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-ha1258a1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-22.1.7-default_h746c552_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.20.0-hcf29cc6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.1-h0d30a3d_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.78.1-h1d1128b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm22-22.1.7-hf7376ad_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.4-hd5a49e9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.33.5-h6eeba95_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h0dc7533_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.22-h280c20c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_cc-2.19.1-cpu_ha812d39_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_framework-2.19.1-cpu_h0a5eddc_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.341.0-h5279c79_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-hca5e8e5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py311h3778330_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.9-py311h38be061_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.9-py311h0f3be63_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ml_dtypes-0.5.4-np2py311h912ec1f_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nh3-0.3.5-py310hd8a072f_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py311h2e04523_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.13-hbde042b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.19.1-py311hdf67eae_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py311hed34c8f_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.2.0-py311hf88fc01_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-6.33.5-py311h3f0a9aa_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py311haee01d2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.1-py311hf27b23e_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.15-hd63d673_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py311h3778330_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py311hca8cfc1_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.11.1-pl5321h16c4a6b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.5.1-py311h1baac5b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.14.2-ha3a3aed_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.3-hc5a330e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py311ha15b03d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py311hbe70eeb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/secretstorage-3.5.0-py311h38be061_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/statsmodels-0.14.6-py311h0372a8f_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorboard-data-server-0.7.0-py311h97c413e_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-2.19.1-cpu_py311hae6871b_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-base-2.19.1-cpu_py311hca13484_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.6-py311h49ec1c0_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.1.0-py311hdf67eae_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py311h49ec1c0_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.25.0-hd6090a7_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.2.1-py311h49ec1c0_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.47-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h09e67af_11.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/_x86_64-microarch-level-1-3_x86_64.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/alabaster-1.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.13.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/arrow-1.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/astunparse-1.6.3-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/async-lru-2.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.18.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backports-1.0-pyhd8ed1ab_5.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backports.tarfile-1.2.0-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-6.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.4.0-hac0b51c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.11.15-py311hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.1-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/id-1.6.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.19-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.17-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-7.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.2.0-pyha191276_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.14.1-pyh53cf698_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.classes-3.4.0-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.context-6.1.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.functools-4.5.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jeepney-0.9.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/json5-0.14.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.1.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.26.0-hcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.9.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.19.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.5.8-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.28.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.8.0-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keyring-25.7.0-pyha804496_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/lark-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/latexcodec-2.0.1-pyh9f0ad1d_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/libml_dtypes-headers-0.5.4-h707e725_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/memory_profiler-0.61.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mistune-3.2.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-11.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbclient-0.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.17.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum-3.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.2-pyh8b19718_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.6.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.25.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybtex-0.26.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybtex-docutils-1.0.3-pyhcf101f3_4.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.18.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-discovery-1.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-25.9.23-pyh1e1bc0e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.11.15-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-3.2.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/readme_renderer-44.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-toolbelt-1.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-py-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/send2trash-2.1.0-pyha191276_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-8.2.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-design-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.21.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-applehelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-bibtex-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.10-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.19.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyhc90fa1f_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.15.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/twine-6.2.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-21.4.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/webcolors-25.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.9.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.8-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.47.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: . - pypi: https://files.pythonhosted.org/packages/0d/fe/6bea5c9162869c5beba5d9c8abbed835ec85bf1ec1fba05a3822325c45f3/build-1.5.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/alabaster-1.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.13.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/appnope-0.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/arrow-1.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/astunparse-1.6.3-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/async-lru-2.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.18.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backports-1.0-pyhd8ed1ab_5.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backports.tarfile-1.2.0-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-6.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.4.0-hac0b51c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.11.15-py311hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.1-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/id-1.6.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.19-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.17-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-7.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.2.0-pyh5552912_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.14.1-pyh53cf698_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.classes-3.4.0-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.context-6.1.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.functools-4.5.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/json5-0.14.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.1.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.26.0-hcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.9.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.19.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.5.8-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.28.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.8.0-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keyring-25.7.0-pyh534df25_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/lark-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/latexcodec-2.0.1-pyh9f0ad1d_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/libml_dtypes-headers-0.5.4-h707e725_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/memory_profiler-0.61.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mistune-3.2.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-11.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbclient-0.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.17.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum-3.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.2-pyh8b19718_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.6.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.25.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybtex-0.26.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybtex-docutils-1.0.3-pyhcf101f3_4.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.18.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-discovery-1.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-25.9.23-pyh1e1bc0e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.11.15-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-3.2.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/readme_renderer-44.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-toolbelt-1.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-py-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/send2trash-2.1.0-pyh5552912_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-8.2.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-design-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.21.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-applehelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-bibtex-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.10-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.18.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyhc90fa1f_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.15.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/twine-6.2.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-21.4.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/webcolors-25.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.9.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.8-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.47.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/argon2-cffi-bindings-25.1.0-py311hf197a57_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-auth-0.10.3-h32d7887_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-cal-0.9.14-hf143bd1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-common-0.13.1-ha1e9b39_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-compression-0.3.2-h36b48e2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-http-0.11.0-h2d71af0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-io-0.26.3-h7049b4b_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-s3-0.12.4-h8390798_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-sdkutils-0.2.4-h36b48e2_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-checksums-0.2.10-h36b48e2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/backports.zstd-1.5.0-py311h1fd9c89_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/black-23.3.0-py311h6eed73b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-1.2.0-hf139dec_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-bin-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-python-1.2.0-py311h7e844b6_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/c-ares-1.34.6-hb5e19a0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/cffi-2.0.0-py311h26bcf6e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/cmarkgfm-2024.11.20-py311h13e5629_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/contourpy-1.3.3-py311h9d13916_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.1-py311ha8ae342_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/debugpy-1.8.21-py311hc3ea09e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/flatbuffers-24.12.23-hd70532e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.63.0-py311ha8ae342_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/freetype-2.14.3-h694c41f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/giflib-5.2.2-h10d778d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/grpcio-1.67.1-py311ha489736_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.16.0-nompi_py311ha7e68cf_102.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-2.1.0-nompi_hf2f48e3_106.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/kiwisolver-1.5.0-py311h2886188_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.22.2-h207b36a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/lcms2-2.19.1-h5ea7634_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/lerc-4.1.0-h35c7297_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20240722.0-cxx17_h0e468a2_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libaec-1.1.5-he7c3a48_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-8_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlicommon-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlidec-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-8_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.20.0-h8f0b9e4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.25-h517ebb2_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libedit-3.1.20250104-pl5321ha958ccf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype-2.14.3-h694c41f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype6-2.14.3-h58fbd8d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgrpc-1.67.1-h4896ac0_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libjpeg-turbo-3.1.4.1-ha1e9b39_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-8_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.58-he930e7c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libprotobuf-5.28.3-h6401091_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libre2-11-2024.07.02-h0e468a2_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsodium-1.0.22-ha3d0635_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h77d7759_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libssh2-1.11.1-hed3591d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libtensorflow_cc-2.18.0-cpu_hce6ddfb_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libtensorflow_framework-2.18.0-cpu_h38b2b02_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.1-ha0a348c_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libwebp-base-1.6.0-hb807250_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libxcb-1.17.0-hf1f96e2_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py311ha8ae342_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.10.9-py311h6eed73b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.10.9-py311hc159c88_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ml_dtypes-0.4.0-py311haeb46be_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/nh3-0.3.5-py310hb9b2626_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.4.6-py311h2c4eb96_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openjpeg-2.5.4-h52bb76a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/optree-0.19.1-py311hd8befaf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pandas-2.3.3-py311hca9a5ca_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pillow-12.2.0-py311h27c473c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/protobuf-5.28.3-py311hc356e98_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/psutil-7.2.2-py311ha332486_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pthread-stubs-0.4-h00291cd_1002.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyobjc-core-12.1-py311h0e44a47_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyobjc-framework-cocoa-12.1-py311hbebd54f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.11.15-ha9537fe_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py311h53ebfaf_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyzmq-27.1.0-py311h83aef28_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/qhull-2020.2-h3c5361c_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/re2-2024.07.02-ha5e900a_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/rpds-py-2026.5.1-py311hcd3406c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ruff-0.14.2-hba89d1c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.9.0-np2py311he512aeb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.17.1-py311h556693a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/snappy-1.2.2-h01f5ddf_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/statsmodels-0.14.6-py311ha837bc1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorboard-data-server-0.7.0-py311h88b0467_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-2.18.0-cpu_py311h69da8e3_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-base-2.18.0-cpu_py311h8bba074_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-estimator-2.18.0-cpu_py311hc2375f7_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tornado-6.5.6-py311h0a33410_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ukkonen-1.1.0-py311ha2ec6bf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/unicodedata2-17.0.1-py311hc71d4c3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.2.1-py311h0a33410_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/xorg-libxau-1.0.12-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/xorg-libxdmcp-1.1.5-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/yaml-0.2.5-h4132b18_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zeromq-4.3.5-h84953be_11.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zlib-ng-2.3.3-h8bce59a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: . - pypi: https://files.pythonhosted.org/packages/0d/fe/6bea5c9162869c5beba5d9c8abbed835ec85bf1ec1fba05a3822325c45f3/build-1.5.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/alabaster-1.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.13.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/appnope-0.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/arrow-1.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/astunparse-1.6.3-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/async-lru-2.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.18.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backports-1.0-pyhd8ed1ab_5.conda - conda: https://conda.anaconda.org/conda-forge/noarch/backports.tarfile-1.2.0-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-6.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.4.0-hac0b51c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.11.15-py311hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.1-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/id-1.6.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.19-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.17-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-7.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.2.0-pyh5552912_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.14.1-pyh53cf698_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.classes-3.4.0-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.context-6.1.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.functools-4.5.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/json5-0.14.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.1.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.26.0-hcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.9.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.19.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.5.8-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.28.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.8.0-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keyring-25.7.0-pyh534df25_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/lark-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/latexcodec-2.0.1-pyh9f0ad1d_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/libml_dtypes-headers-0.5.4-h707e725_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/memory_profiler-0.61.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mistune-3.2.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-11.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbclient-0.11.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.17.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum-3.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.2-pyh8b19718_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.6.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.25.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybtex-0.26.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybtex-docutils-1.0.3-pyhcf101f3_4.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.18.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-discovery-1.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-25.9.23-pyh1e1bc0e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.11.15-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-3.2.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/readme_renderer-44.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-toolbelt-1.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-py-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/send2trash-2.1.0-pyh5552912_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-8.2.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-design-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.21.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-applehelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-bibtex-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.10-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.19.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyhc90fa1f_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.15.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/twine-6.2.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-21.4.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/webcolors-25.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.9.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.8-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.47.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/argon2-cffi-bindings-25.1.0-py311h9408147_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.3-h1e0905d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-h2370581_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.13.1-h84a0fba_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-h729ab01_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.11.0-h198760e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.26.3-hee75b50_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.12.4-h45371db_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.4-h729ab01_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-h729ab01_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.5.0-py311h36d4fbb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/black-23.3.0-py311h267d04e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-h7d5ae5b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py311hdc60ec4_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py311hd10dc20_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cmarkgfm-2024.11.20-py311h3696347_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py311h7d85929_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.1-py311hc290fe0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.21-py311h8948835_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/flatbuffers-25.9.23-h9e8ef45_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.63.0-py311hc290fe0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/grpcio-1.78.1-py311h8fd116a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.16.0-nompi_py311hf10ccac_102.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-2.1.0-nompi_h262911d_106.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py311h7d85929_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h385eeb1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hdfa7624_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.1.0-h1eee2c3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.20.0-hd5a2499_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.78.1-h3e3f78d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-6.33.5-h2d4b707_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h4c27e2a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.22-h1a92334_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtensorflow_cc-2.19.1-cpu_he004e32_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtensorflow_framework-2.19.1-cpu_h3441331_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h07db88b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py311hc290fe0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.9-py311ha1ab1f8_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.9-py311h68bafec_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ml_dtypes-0.5.4-np2py311hb7ce6e1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nh3-0.3.5-py310h3b8a9b8_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py311hbd1492f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.19.1-py311h572238d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.3.3-py311hdb8e4fa_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.2.0-py311hd37aea2_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-6.33.5-py311h12b56a6_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py311he363849_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-core-12.1-py311hce6e4fa_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-framework-cocoa-12.1-py311h9049b8e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.15-h8561d8f_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py311hc290fe0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.1.0-py311h8463d66_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-ha480c28_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.5.1-py311haff49d3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.14.2-h492a034_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py311hf1dd2ad_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py311h9a58382_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/statsmodels-0.14.6-py311h09efe57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorboard-data-server-0.7.0-py311h806dddb_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-2.19.1-cpu_py311he75e561_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-base-2.19.1-cpu_py311hf3798af_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.6-py311hc949640_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ukkonen-1.1.0-py311h572238d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py311hc949640_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.2.1-py311hc949640_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-h10816f8_11.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: . - pypi: https://files.pythonhosted.org/packages/0d/fe/6bea5c9162869c5beba5d9c8abbed835ec85bf1ec1fba05a3822325c45f3/build-1.5.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl docs: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.3-h3aafcba_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h8e43964_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.13.1-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h16e98cb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.11.0-hcbcd92d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.26.3-h955231c_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.12.4-h00bea6e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h16e98cb_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h16e98cb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.5.0-py312h90b7ffd_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312hdb49522_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-he90730b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/flatbuffers-25.9.23-hb7d4c21_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.1-h27c8c51_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.78.1-py312h39ee1c6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.16.0-nompi_py312ha829cd9_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.2.1-h6083320_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-2.1.0-nompi_hee75f7e_106.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.0-py312h0a2e395_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-ha1258a1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-22.1.7-default_h746c552_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.20.0-hcf29cc6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.1-h0d30a3d_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.78.1-h1d1128b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm22-22.1.7-hf7376ad_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.4-hd5a49e9_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.33.5-h6eeba95_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h0dc7533_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_cc-2.19.1-cpu_ha812d39_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_framework-2.19.1-cpu_h0a5eddc_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.341.0-h5279c79_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-hca5e8e5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.9-py312h7900ff3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.9-py312he3d6523_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ml_dtypes-0.5.4-np2py312h0f77346_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.13-hbde042b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.19.1-py312hd9148b4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py312hf79963d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.2.0-py312h50c33e8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-6.33.5-py312ha7b3241_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h5253ce2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.1-py312h50ac2ff_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.11.1-pl5321h16c4a6b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.3-hc5a330e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py312h3226591_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py312h54fa4ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/statsmodels-0.14.6-py312h4f23490_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorboard-data-server-0.7.0-py312h4eba8b5_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-2.19.1-cpu_py312h3336143_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-base-2.19.1-cpu_py312h9edca52_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.6-py312h4c3975b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.25.0-hd6090a7_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.2.1-py312h4c3975b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.47-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/_x86_64-microarch-level-1-3_x86_64.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/alabaster-1.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/astunparse-1.6.3-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.18.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.17-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.8.0-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/latexcodec-2.0.1-pyh9f0ad1d_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/libml_dtypes-headers-0.5.4-h707e725_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/memory_profiler-0.61.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum-3.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybtex-0.26.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybtex-docutils-1.0.3-pyhcf101f3_4.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.18.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-25.9.23-pyh1e1bc0e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-py-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-8.2.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-design-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.21.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-applehelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-bibtex-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.10-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.19.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.8-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: . osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/alabaster-1.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/astunparse-1.6.3-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.18.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.17-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.8.0-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/latexcodec-2.0.1-pyh9f0ad1d_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/libml_dtypes-headers-0.5.4-h707e725_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/memory_profiler-0.61.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum-3.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybtex-0.26.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybtex-docutils-1.0.3-pyhcf101f3_4.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.18.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-25.9.23-pyh1e1bc0e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-py-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-8.2.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-design-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.21.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-applehelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-bibtex-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.10-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.18.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.8-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-auth-0.10.3-h32d7887_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-cal-0.9.14-hf143bd1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-common-0.13.1-ha1e9b39_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-compression-0.3.2-h36b48e2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-http-0.11.0-h2d71af0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-io-0.26.3-h7049b4b_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-s3-0.12.4-h8390798_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-sdkutils-0.2.4-h36b48e2_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-checksums-0.2.10-h36b48e2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/backports.zstd-1.5.0-py312h5f4ecc6_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-1.2.0-hf139dec_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-bin-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-python-1.2.0-py312h4b46afd_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/c-ares-1.34.6-hb5e19a0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/contourpy-1.3.3-py312hb0c38da_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/flatbuffers-24.12.23-hd70532e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.63.0-py312heb39f77_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/freetype-2.14.3-h694c41f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/giflib-5.2.2-h10d778d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/grpcio-1.67.1-py312h145213c_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.16.0-nompi_py312h82c48cf_102.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-2.1.0-nompi_hf2f48e3_106.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/kiwisolver-1.5.0-py312hb1dc2e7_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.22.2-h207b36a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/lcms2-2.19.1-h5ea7634_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/lerc-4.1.0-h35c7297_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20240722.0-cxx17_h0e468a2_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libaec-1.1.5-he7c3a48_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-8_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlicommon-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlidec-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.2.0-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-8_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.20.0-h8f0b9e4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.25-h517ebb2_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libedit-3.1.20250104-pl5321ha958ccf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype-2.14.3-h694c41f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype6-2.14.3-h58fbd8d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgrpc-1.67.1-h4896ac0_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libjpeg-turbo-3.1.4.1-ha1e9b39_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-8_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.58-he930e7c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libprotobuf-5.28.3-h6401091_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libre2-11-2024.07.02-h0e468a2_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h77d7759_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libssh2-1.11.1-hed3591d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libtensorflow_cc-2.18.0-cpu_hce6ddfb_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libtensorflow_framework-2.18.0-cpu_h38b2b02_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.1-ha0a348c_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libwebp-base-1.6.0-hb807250_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libxcb-1.17.0-hf1f96e2_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py312heb39f77_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.10.9-py312hb401068_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.10.9-py312h7609456_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ml_dtypes-0.4.0-py312h98e817e_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.4.6-py312h746d82c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openjpeg-2.5.4-h52bb76a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/optree-0.19.1-py312hdb80668_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pandas-2.3.3-py312h86abcb1_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pillow-12.2.0-py312he84af14_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/protobuf-5.28.3-py312haafddd8_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/psutil-7.2.2-py312hf7082af_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pthread-stubs-0.4-h00291cd_1002.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.12.13-ha9537fe_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py312h51361c1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/qhull-2020.2-h3c5361c_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/re2-2024.07.02-ha5e900a_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.9.0-np2py312h691318a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.17.1-py312h6309490_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/snappy-1.2.2-h01f5ddf_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/statsmodels-0.14.6-py312h391ab28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorboard-data-server-0.7.0-py312hc363c91_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-2.18.0-cpu_py312hf9ba072_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-base-2.18.0-cpu_py312h7973d44_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-estimator-2.18.0-cpu_py312hd6a94b3_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tornado-6.5.6-py312h933eb07_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/unicodedata2-17.0.1-py312h1a1c95f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.2.1-py312h933eb07_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/xorg-libxau-1.0.12-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/xorg-libxdmcp-1.1.5-h8616949_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/yaml-0.2.5-h4132b18_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zlib-ng-2.3.3-h8bce59a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: . osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.5-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/alabaster-1.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/astunparse-1.6.3-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.18.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.17-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-2.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.8.0-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/latexcodec-2.0.1-pyh9f0ad1d_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/libml_dtypes-headers-0.5.4-h707e725_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/memory_profiler-0.61.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum-3.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybtex-0.26.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pybtex-docutils-1.0.3-pyhcf101f3_4.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.18.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-25.9.23-pyh1e1bc0e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-py-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-8.2.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-design-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.21.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-applehelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-bibtex-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.1.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-2.0.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.10-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.19.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.8-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.3-h1e0905d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-h2370581_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.13.1-h84a0fba_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-h729ab01_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.11.0-h198760e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.26.3-hee75b50_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.12.4-h45371db_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.4-h729ab01_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-h729ab01_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.5.0-py312h87c4bb7_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-h7d5ae5b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py312h0dfefe5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312h3093aea_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/flatbuffers-25.9.23-h9e8ef45_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.63.0-py312h04c11ed_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/grpcio-1.78.1-py312h0c23288_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.16.0-nompi_py312h585e8c8_102.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-2.1.0-nompi_h262911d_106.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py312h3093aea_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h385eeb1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hdfa7624_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.1.0-h1eee2c3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.20.0-hd5a2499_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.78.1-h3e3f78d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-6.33.5-h2d4b707_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h4c27e2a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtensorflow_cc-2.19.1-cpu_he004e32_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtensorflow_framework-2.19.1-cpu_h3441331_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h07db88b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h04c11ed_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.9-py312h1f38498_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.9-py312hf3defc7_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ml_dtypes-0.5.4-np2py312h60fbb24_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.19.1-py312h766f71e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.3.3-py312h5978115_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.2.0-py312h4e908a4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-6.33.5-py312h857ab9a_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py312hb3ab3e3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py312h04c11ed_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-ha480c28_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py312he5ca3e3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py312h4519d97_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/statsmodels-0.14.6-py312ha11c99a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorboard-data-server-0.7.0-py312h9536bd2_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-2.19.1-cpu_py312hc65d1cb_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-base-2.19.1-cpu_py312h66a5cad_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.6-py312h2bbb03f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py312h2bbb03f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.2.1-py312h2bbb03f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: . linters: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/black-23.3.0-py311h38be061_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py311h03d9500_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py311h2e04523_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.15-hd63d673_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py311h3778330_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.14.2-ha3a3aed_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py311ha15b03d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py311hbe70eeb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.1.0-py311hdf67eae_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.1-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.19-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.6.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-discovery-1.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-21.4.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: . osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.1-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.19-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.6.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-discovery-1.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-21.4.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/black-23.3.0-py311h6eed73b_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/cffi-2.0.0-py311h26bcf6e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-8_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-8_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-8_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h8f8c405_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.4.6-py311h2c4eb96_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.11.15-ha9537fe_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py311h53ebfaf_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ruff-0.14.2-hba89d1c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.9.0-np2py311he512aeb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.17.1-py311h556693a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ukkonen-1.1.0-py311ha2ec6bf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/yaml-0.2.5-h4132b18_3.conda - pypi: . osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyhc90fa1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.1-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.19-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.6.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-discovery-1.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-21.4.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/black-23.3.0-py311h267d04e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py311hd10dc20_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py311hbd1492f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.15-h8561d8f_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py311hc290fe0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.14.2-h492a034_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py311hf1dd2ad_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py311h9a58382_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ukkonen-1.1.0-py311h572238d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda - pypi: . win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyh6dadd2b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.1-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.19-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.6.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-discovery-1.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-21.4.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/black-23.3.0-py311h1ea47a8_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py311h3485c13_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-8_h8455456_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-8_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-8_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.2-hf5d6505_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h692994f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-hbc0d294_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.7-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.0.0-hac47afa_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.4.6-py311h65cb7f3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2026.0.0-h57928b3_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.2-hf411b9b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.11.15-h0159041_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py311h3f79411_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ruff-0.14.2-h3e3edff_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py311hd01f973_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.17.1-py311h9c22a71_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ukkonen-1.1.0-py311h3fd045d_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-h1b7c187_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda - pypi: . optional: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py314h2b28147_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py314ha0b5721_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.5-habeac84_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py314hf09ca88_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py314hf07bd8e_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - pypi: . osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-8_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-8_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-8_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-hf3981d6_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h8f8c405_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.4.6-py314h7b24d9b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pandas-2.3.3-py314hc4308db_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.14.5-h7c6738f_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.9.0-np2py314h67cc4f9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.17.1-py314h5727af0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: . osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py314hb79c6fa_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.3.3-py314ha3d490a_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.5-h4c637c5_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py314h15f0f0f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py314h18e1515_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: . win-64: - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314t.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-8_h8455456_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-8_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-8_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.2-hf5d6505_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h692994f_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-hbc0d294_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.7-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.0.0-hac47afa_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.4.6-py314hffb9209_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2026.0.0-h57928b3_908.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.2-hf411b9b_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-2.3.3-py314h0757d37_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.5-h7c1dbca_0_cp314t.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314hb10d3ac_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.17.1-py314h9c5632b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-h1b7c187_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_38.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - pypi: . - pypi: https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl tests: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.3-h3aafcba_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h8e43964_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.13.1-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h16e98cb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.11.0-hcbcd92d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.26.3-h955231c_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.12.4-h00bea6e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h16e98cb_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h16e98cb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.5.0-py312h90b7ffd_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312hdb49522_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.1-py312h8a5da7c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/flatbuffers-25.9.23-hb7d4c21_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.78.1-py312h39ee1c6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.16.0-nompi_py312ha829cd9_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-2.1.0-nompi_hee75f7e_106.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-ha1258a1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.20.0-hcf29cc6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.78.1-h1d1128b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.33.5-h6eeba95_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h0dc7533_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_cc-2.19.1-cpu_ha812d39_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_framework-2.19.1-cpu_h0a5eddc_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ml_dtypes-0.5.4-np2py312h0f77346_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.19.1-py312hd9148b4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-6.33.5-py312ha7b3241_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.3-hc5a330e_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py312h3226591_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py312h54fa4ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorboard-data-server-0.7.0-py312h4eba8b5_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-2.19.1-cpu_py312h3336143_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-base-2.19.1-cpu_py312h9edca52_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.2.1-py312h4c3975b_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/_x86_64-microarch-level-1-3_x86_64.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/astunparse-1.6.3-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.17-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.8.0-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libml_dtypes-headers-0.5.4-h707e725_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum-3.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-25.9.23-pyh1e1bc0e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.19.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.8-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - pypi: . osx-64: - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/astunparse-1.6.3-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.17-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.8.0-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libml_dtypes-headers-0.5.4-h707e725_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum-3.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-25.9.23-pyh1e1bc0e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.18.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.8-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-auth-0.10.3-h32d7887_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-cal-0.9.14-hf143bd1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-common-0.13.1-ha1e9b39_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-compression-0.3.2-h36b48e2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-http-0.11.0-h2d71af0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-io-0.26.3-h7049b4b_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-s3-0.12.4-h8390798_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-sdkutils-0.2.4-h36b48e2_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-checksums-0.2.10-h36b48e2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/backports.zstd-1.5.0-py312h5f4ecc6_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-python-1.2.0-py312h4b46afd_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/c-ares-1.34.6-hb5e19a0_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.1-py312heb39f77_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/flatbuffers-24.12.23-hd70532e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/giflib-5.2.2-h10d778d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/grpcio-1.67.1-py312h145213c_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.16.0-nompi_py312h82c48cf_102.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-2.1.0-nompi_hf2f48e3_106.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.22.2-h207b36a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20240722.0-cxx17_h0e468a2_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libaec-1.1.5-he7c3a48_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-8_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-8_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.20.0-h8f0b9e4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libedit-3.1.20250104-pl5321ha958ccf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgrpc-1.67.1-h4896ac0_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libjpeg-turbo-3.1.4.1-ha1e9b39_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-8_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.58-he930e7c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libprotobuf-5.28.3-h6401091_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libre2-11-2024.07.02-h0e468a2_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h77d7759_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libssh2-1.11.1-hed3591d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libtensorflow_cc-2.18.0-cpu_hce6ddfb_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libtensorflow_framework-2.18.0-cpu_h38b2b02_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py312heb39f77_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ml_dtypes-0.4.0-py312h98e817e_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.4.6-py312h746d82c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/optree-0.19.1-py312hdb80668_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/protobuf-5.28.3-py312haafddd8_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.12.13-ha9537fe_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/re2-2024.07.02-ha5e900a_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.9.0-np2py312h691318a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.17.1-py312h6309490_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/snappy-1.2.2-h01f5ddf_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorboard-data-server-0.7.0-py312hc363c91_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-2.18.0-cpu_py312hf9ba072_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-base-2.18.0-cpu_py312h7973d44_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-estimator-2.18.0-cpu_py312hd6a94b3_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.2.1-py312h933eb07_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: . osx-arm64: - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/astunparse-1.6.3-pyhd8ed1ab_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.17-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.8.0-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/libml_dtypes-headers-0.5.4-h707e725_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum-3.4.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-25.9.23-pyh1e1bc0e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.19.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.3.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.8-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.3-h1e0905d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-h2370581_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.13.1-h84a0fba_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-h729ab01_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.11.0-h198760e_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.26.3-hee75b50_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.12.4-h45371db_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.4-h729ab01_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-h729ab01_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.5.0-py312h87c4bb7_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py312h0dfefe5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.1-py312h04c11ed_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/flatbuffers-25.9.23-h9e8ef45_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/grpcio-1.78.1-py312h0c23288_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.16.0-nompi_py312h585e8c8_102.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-2.1.0-nompi_h262911d_106.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h385eeb1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.20.0-hd5a2499_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.78.1-h3e3f78d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-6.33.5-h2d4b707_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h4c27e2a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtensorflow_cc-2.19.1-cpu_he004e32_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtensorflow_framework-2.19.1-cpu_h3441331_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h04c11ed_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ml_dtypes-0.5.4-np2py312h60fbb24_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.19.1-py312h766f71e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-6.33.5-py312h857ab9a_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-ha480c28_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py312he5ca3e3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py312h4519d97_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorboard-data-server-0.7.0-py312h9536bd2_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-2.19.1-cpu_py312hc65d1cb_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-base-2.19.1-cpu_py312h66a5cad_5.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.2.1-py312h2bbb03f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: . packages: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda build_number: 20 sha256: 1dd3fffd892081df9726d7eb7e0dea6198962ba775bd88842135a4ddb4deb3c9 md5: a9f577daf3de00bca7c3c76c0ecbd1de depends: - __glibc >=2.17,<3.0.a0 - libgomp >=7.5.0 constrains: - openmp_impl <0.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 28948 timestamp: 1770939786096 - conda: https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.16-hb03c661_0.conda sha256: 64484cb7e7cf65d9c7188498d595125a6e0751604dd7fb483e1bb327eec0f738 md5: 18d273b22e96c97c2017813f099faa82 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: LGPL-2.1-or-later license_family: GPL purls: [] size: 591046 timestamp: 1780398678742 - conda: https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py311h49ec1c0_2.conda sha256: b81f852f13a1d148f6ad7e2a29ab375eb1558b73c9bfa38792d98ea7fb414cff md5: 6e36e9d2b535c3fbe2e093108df26695 depends: - __glibc >=2.17,<3.0.a0 - cffi >=1.0.1 - libgcc >=14 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/argon2-cffi-bindings?source=hash-mapping size: 35831 timestamp: 1762509453632 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-auth-0.10.3-h3aafcba_1.conda sha256: cd8d0c4436c8efb8b0a14832f2454dcacc4cf20519b2131b134f4aef9e4e8e1b md5: 54f62a10a7dcc7e46b149e2d078299b5 depends: - libgcc >=14 - __glibc >=2.17,<3.0.a0 - aws-c-cal >=0.9.14,<0.9.15.0a0 - aws-c-http >=0.11.0,<0.11.1.0a0 - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 - aws-c-io >=0.26.3,<0.26.4.0a0 - aws-c-common >=0.13.1,<0.13.2.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 134414 timestamp: 1779877481302 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-cal-0.9.14-h8e43964_1.conda sha256: 574b5ff4e848e5a03cb4a627ff185b759d26f4cfd8d95db0dc13021c8d5abe34 md5: eb6e8fb306b4025bf9c68b6c16db4e19 depends: - __glibc >=2.17,<3.0.a0 - aws-c-common >=0.13.1,<0.13.2.0a0 - libgcc >=14 - openssl >=3.5.6,<4.0a0 license: Apache-2.0 license_family: Apache purls: [] size: 56948 timestamp: 1779463576189 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-common-0.13.1-hb03c661_0.conda sha256: 7e1a6e799b83c8eeb675bd961202964eb534a671ae7c2b7a0c44ac3d731f07c0 md5: f5f0be3aac62d771c3b0cad1d316d8e9 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: Apache-2.0 license_family: Apache purls: [] size: 242317 timestamp: 1779302509297 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-compression-0.3.2-h16e98cb_1.conda sha256: e0b936833991cea92044b37186de156790b3e945b2b6aefebc49c13423b919fb md5: 673828462eb87b76c178b582b6e19824 depends: - libgcc >=14 - __glibc >=2.17,<3.0.a0 - aws-c-common >=0.13.1,<0.13.2.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 21995 timestamp: 1779462933046 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-http-0.11.0-hcbcd92d_1.conda sha256: abe70bc1b0de021c442631cfb69f321701be8618e7a24d25f718f39253cc6f65 md5: 333fa38d6dfe23cd68c67e20cb1726c9 depends: - libgcc >=14 - __glibc >=2.17,<3.0.a0 - aws-c-io >=0.26.3,<0.26.4.0a0 - aws-c-common >=0.13.1,<0.13.2.0a0 - aws-c-cal >=0.9.14,<0.9.15.0a0 - aws-c-compression >=0.3.2,<0.3.3.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 230314 timestamp: 1779870958760 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-io-0.26.3-h955231c_3.conda sha256: 54815f1c64d5cf1e4616f457c8866424e6a0d4069a00736da241f1abc269c717 md5: a3581835895ca9b7782beb5a49746db7 depends: - libgcc >=14 - __glibc >=2.17,<3.0.a0 - aws-c-cal >=0.9.14,<0.9.15.0a0 - s2n >=1.7.3,<1.7.4.0a0 - aws-c-common >=0.13.1,<0.13.2.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 181606 timestamp: 1779864599229 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-s3-0.12.4-h00bea6e_0.conda sha256: 70f91efac0359b958b6bcd8535f2e831102561965fc8fbb3efe1d1a9810c82ae md5: 3a36b9efa77c11214308630ddb91496a depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - openssl >=3.5.6,<4.0a0 - aws-c-cal >=0.9.14,<0.9.15.0a0 - aws-c-io >=0.26.3,<0.26.4.0a0 - aws-c-http >=0.11.0,<0.11.1.0a0 - aws-c-auth >=0.10.3,<0.10.4.0a0 - aws-checksums >=0.2.10,<0.2.11.0a0 - aws-c-common >=0.13.1,<0.13.2.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 153374 timestamp: 1779887841222 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-c-sdkutils-0.2.4-h16e98cb_5.conda sha256: 315eab8c5b10512b1c4643dd3433c254e6f5bac96043744e900ea5a332b32b4b md5: a81045d3ce07a74751541de2bad6fa49 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - aws-c-common >=0.13.1,<0.13.2.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 59142 timestamp: 1779464526972 - conda: https://conda.anaconda.org/conda-forge/linux-64/aws-checksums-0.2.10-h16e98cb_1.conda sha256: 2200ba495513559be733d877096e678ecdff41ea015bcaf68be297981cfe31f9 md5: 8d963dc4805936cc294348743201d68e depends: - libgcc >=14 - __glibc >=2.17,<3.0.a0 - aws-c-common >=0.13.1,<0.13.2.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 101612 timestamp: 1779464586488 - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.5.0-py310h69bd2ac_0.conda sha256: d4fe868e503056a45ceed694e0263d1300bacbb5f021d402d40f3e3130b15703 md5: 61e26e9f4f132463d161f9311b2ee736 depends: - python - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python_abi 3.10.* *_cp310 - zstd >=1.5.7,<1.6.0a0 license: BSD-3-Clause AND MIT AND EPL-2.0 purls: - pkg:pypi/backports-zstd?source=compressed-mapping size: 192862 timestamp: 1778594060634 - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.5.0-py311h6b1f9c4_0.conda sha256: 79fd407b109c359182fcdd4efef15e122622941690648daacc2d24647f2593b1 md5: 624e015a6784f7b1b8c0071a557e7791 depends: - python - libgcc >=14 - __glibc >=2.17,<3.0.a0 - python_abi 3.11.* *_cp311 - zstd >=1.5.7,<1.6.0a0 license: BSD-3-Clause AND MIT AND EPL-2.0 purls: - pkg:pypi/backports-zstd?source=hash-mapping size: 247051 timestamp: 1778594052903 - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.5.0-py312h90b7ffd_0.conda sha256: a2b08a4e5e549b5f67c38edffd175437e2208547a7e67b5fa5373b67ef419e50 md5: b31dba71fe091e7201826e57e0f7b261 depends: - python - libgcc >=14 - __glibc >=2.17,<3.0.a0 - zstd >=1.5.7,<1.6.0a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause AND MIT AND EPL-2.0 purls: - pkg:pypi/backports-zstd?source=compressed-mapping size: 239928 timestamp: 1778594049826 - conda: https://conda.anaconda.org/conda-forge/linux-64/black-23.3.0-py311h38be061_1.conda sha256: c729ebbdca2ca7286305ad51cc3beea6b85e68cd02794fb9895f3d5e16540b86 md5: b0d621848bfba5aacbdfc43dfdeabfec depends: - click >=8.0.0 - mypy_extensions >=0.4.3 - packaging >=22.0 - pathspec >=0.9 - platformdirs >=2 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/black?source=hash-mapping size: 354716 timestamp: 1682492111062 - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-1.2.0-hed03a55_1.conda sha256: e511644d691f05eb12ebe1e971fd6dc3ae55a4df5c253b4e1788b789bdf2dfa6 md5: 8ccf913aaba749a5496c17629d859ed1 depends: - __glibc >=2.17,<3.0.a0 - brotli-bin 1.2.0 hb03c661_1 - libbrotlidec 1.2.0 hb03c661_1 - libbrotlienc 1.2.0 hb03c661_1 - libgcc >=14 license: MIT license_family: MIT purls: [] size: 20103 timestamp: 1764017231353 - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.2.0-hb03c661_1.conda sha256: 64b137f30b83b1dd61db6c946ae7511657eead59fdf74e84ef0ded219605aa94 md5: af39b9a8711d4a8d437b52c1d78eb6a1 depends: - __glibc >=2.17,<3.0.a0 - libbrotlidec 1.2.0 hb03c661_1 - libbrotlienc 1.2.0 hb03c661_1 - libgcc >=14 license: MIT license_family: MIT purls: [] size: 21021 timestamp: 1764017221344 - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py310hba01987_1.conda sha256: f036fe554d902549f86689a9650a0996901d5c9242b0a1e3fbfe6dbccd2ae011 md5: 393fca4557fbd2c4d995dcb89f569048 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 constrains: - libbrotlicommon 1.2.0 hb03c661_1 license: MIT license_family: MIT purls: - pkg:pypi/brotli?source=hash-mapping size: 367099 timestamp: 1764017439384 - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py311h66f275b_1.conda sha256: c36eb061d9ead85f97644cfb740d485dba9b8823357f35c17851078e95e975c1 md5: 86daecb8e4ed1042d5dc6efbe0152590 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 constrains: - libbrotlicommon 1.2.0 hb03c661_1 license: MIT license_family: MIT purls: - pkg:pypi/brotli?source=hash-mapping size: 367573 timestamp: 1764017405384 - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312hdb49522_1.conda sha256: 49df13a1bb5e388ca0e4e87022260f9501ed4192656d23dc9d9a1b4bf3787918 md5: 64088dffd7413a2dd557ce837b4cbbdb depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 constrains: - libbrotlicommon 1.2.0 hb03c661_1 license: MIT license_family: MIT purls: - pkg:pypi/brotli?source=hash-mapping size: 368300 timestamp: 1764017300621 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda sha256: 0b75d45f0bba3e95dc693336fa51f40ea28c980131fec438afb7ce6118ed05f6 md5: d2ffd7602c02f2b316fd921d39876885 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: bzip2-1.0.6 license_family: BSD purls: [] size: 260182 timestamp: 1771350215188 - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda sha256: cc9accf72fa028d31c2a038460787751127317dcfa991f8d1f1babf216bb454e md5: 920bb03579f15389b9e512095ad995b7 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: MIT license_family: MIT purls: [] size: 207882 timestamp: 1765214722852 - conda: https://conda.anaconda.org/conda-forge/linux-64/cairo-1.18.4-he90730b_1.conda sha256: 06525fa0c4e4f56e771a3b986d0fdf0f0fc5a3270830ee47e127a5105bde1b9a md5: bb6c4808bfa69d6f7f6b07e5846ced37 depends: - __glibc >=2.17,<3.0.a0 - fontconfig >=2.15.0,<3.0a0 - fonts-conda-ecosystem - icu >=78.1,<79.0a0 - libexpat >=2.7.3,<3.0a0 - libfreetype >=2.14.1 - libfreetype6 >=2.14.1 - libgcc >=14 - libglib >=2.86.3,<3.0a0 - libpng >=1.6.53,<1.7.0a0 - libstdcxx >=14 - libxcb >=1.17.0,<2.0a0 - libzlib >=1.3.1,<2.0a0 - pixman >=0.46.4,<1.0a0 - xorg-libice >=1.1.2,<2.0a0 - xorg-libsm >=1.2.6,<2.0a0 - xorg-libx11 >=1.8.12,<2.0a0 - xorg-libxext >=1.3.6,<2.0a0 - xorg-libxrender >=0.9.12,<0.10.0a0 license: LGPL-2.1-only or MPL-1.1 purls: [] size: 989514 timestamp: 1766415934926 - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py311h03d9500_1.conda sha256: 3ad13377356c86d3a945ae30e9b8c8734300925ef81a3cb0a9db0d755afbe7bb md5: 3912e4373de46adafd8f1e97e4bd166b depends: - __glibc >=2.17,<3.0.a0 - libffi >=3.5.2,<3.6.0a0 - libgcc >=14 - pycparser - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/cffi?source=hash-mapping size: 303338 timestamp: 1761202960110 - conda: https://conda.anaconda.org/conda-forge/linux-64/cmarkgfm-2024.11.20-py311h49ec1c0_1.conda sha256: 447b65e534b3104cbd55d98fcf06607d7d136f54be30577fbd654d9647717950 md5: 59ff48e89b1a32ddfbe0d73de2498ec8 depends: - __glibc >=2.17,<3.0.a0 - cffi >=1.0.0 - libgcc >=14 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/cmarkgfm?source=hash-mapping size: 143640 timestamp: 1760363101780 - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py311h724c32c_4.conda sha256: fd7aca059253cff3d8b0aec71f0c1bf2904823b13f1997bf222aea00a76f3cce md5: d04e508f5a03162c8bab4586a65d00bf depends: - numpy >=1.25 - python - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/contourpy?source=hash-mapping size: 320553 timestamp: 1769155975008 - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312h0a2e395_4.conda sha256: 62447faf7e8eb691e407688c0b4b7c230de40d5ecf95bf301111b4d05c5be473 md5: 43c2bc96af3ae5ed9e8a10ded942aa50 depends: - numpy >=1.25 - python - __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - libgcc >=14 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/contourpy?source=hash-mapping size: 320386 timestamp: 1769155979897 - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.1-py310h3406613_0.conda sha256: 179d5e48b5023d26ff5ebc28be01cf6a234aaf866fd8cd9ce1d527caa3b39b8f md5: 0a83dc99ca644004a0ad7de74ca296e3 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - tomli license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/coverage?source=compressed-mapping size: 313575 timestamp: 1779837983139 - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.1-py311h3778330_0.conda sha256: ebefe7c8a41d14e4a50c5b862cf0226b3ac745495415bb6fb0db364b945cfe3a md5: f875c239f662e1b31fbf32282f1da087 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - tomli license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/coverage?source=hash-mapping size: 399156 timestamp: 1779838054673 - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.1-py312h8a5da7c_0.conda sha256: 80b990c6870c721bcde5e14e71d3560bac3dad93b54d027f723dca2bb7ccda03 md5: 6668e2af2de730400bdce9cf2ea132f9 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - tomli license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/coverage?source=hash-mapping size: 389696 timestamp: 1779838017522 - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.14.1-py314h67df5f8_0.conda sha256: 4fb298517c0aff45eb449bf4bd484c0f4d0ab36d4e5c1005b7f0312e68330b57 md5: 2af0e1fec00680b1b6ef3859585ca8fa depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 - tomli license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/coverage?source=hash-mapping size: 413054 timestamp: 1779837945378 - conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-48.0.0-py311h2005dd1_0.conda sha256: 838cde68e70f4e0dc6458613316fa0e50eee5cf86cafb1cee4b7ca1a701a8436 md5: 96f6e551c7ff30c95c1c8b4d2d1c5c9f depends: - __glibc >=2.17,<3.0.a0 - cffi >=2.0 - libgcc >=14 - openssl >=3.5.6,<4.0a0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 constrains: - __glibc >=2.17 license: Apache-2.0 AND BSD-3-Clause AND PSF-2.0 AND MIT license_family: BSD purls: - pkg:pypi/cryptography?source=hash-mapping size: 1913519 timestamp: 1777966158377 - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hac629b4_1.conda sha256: 7684da83306bb69686c0506fb09aa7074e1a55ade50c3a879e4e5df6eebb1009 md5: af491aae930edc096b58466c51c4126c depends: - __glibc >=2.17,<3.0.a0 - krb5 >=1.22.2,<1.23.0a0 - libgcc >=13 - libntlm >=1.8,<2.0a0 - libstdcxx >=13 - libxcrypt >=4.4.36 - openssl >=3.5.5,<4.0a0 license: BSD-3-Clause-Attribution license_family: BSD purls: [] size: 210103 timestamp: 1771943128249 - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda sha256: 8bb557af1b2b7983cf56292336a1a1853f26555d9c6cecf1e5b2b96838c9da87 md5: ce96f2f470d39bd96ce03945af92e280 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - libzlib >=1.3.1,<2.0a0 - libglib >=2.86.2,<3.0a0 - libexpat >=2.7.3,<3.0a0 license: AFL-2.1 OR GPL-2.0-or-later purls: [] size: 447649 timestamp: 1764536047944 - conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.21-py311hc665b79_0.conda sha256: 600beac442edeb098cd93c8d9459904c341dc4c258cee726191a3ead1e877672 md5: f3d3ec5fc6db099c5b24e95a4c55817f depends: - python - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/debugpy?source=compressed-mapping size: 2730876 timestamp: 1780390154098 - conda: https://conda.anaconda.org/conda-forge/linux-64/double-conversion-3.4.0-hecca717_0.conda sha256: 40cdd1b048444d3235069d75f9c8e1f286db567f6278a93b4f024e5642cfaecc md5: dbe3ec0f120af456b3477743ffd99b74 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 license: BSD-3-Clause license_family: BSD purls: [] size: 71809 timestamp: 1765193127016 - conda: https://conda.anaconda.org/conda-forge/linux-64/flatbuffers-24.3.25-h59595ed_0.conda sha256: 0f3b8d6a958d40d5b2ac105ba0ec09f61dd4ce78cafdf99ab2d0fc298dc54d75 md5: 2941a8c4e4871cdfa738c8c1a7611533 depends: - libgcc-ng >=12 - libstdcxx-ng >=12 license: Apache-2.0 license_family: APACHE purls: [] size: 1459911 timestamp: 1711467009850 - conda: https://conda.anaconda.org/conda-forge/linux-64/flatbuffers-25.9.23-hb7d4c21_0.conda sha256: e5f90c2fd61012d6ad332657a5bf5455620f0db8524f0b005d91e1c2737bad69 md5: 10a330bfd5345af730b0fc1349d15eaf depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 license: Apache-2.0 license_family: APACHE purls: [] size: 1584732 timestamp: 1761142459651 - conda: https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.18.1-h27c8c51_0.conda sha256: 2e50bdcebdf70a865b81f2456bbc586386451ec601c60f2b6cd22b8c40a2d384 md5: e0e050cfa9fa85fe39632ab11cb7f3e0 depends: - __glibc >=2.17,<3.0.a0 - libexpat >=2.8.1,<3.0a0 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - libgcc >=14 - libuuid >=2.42.1,<3.0a0 - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT purls: [] size: 281880 timestamp: 1780450077431 - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py311h3778330_0.conda sha256: ce5004df66a6bf4e3d634850ab43471b98700291065281eb1982eb54b7bf7d85 md5: 498ede447c05b203be980ae1dceb06f3 depends: - __glibc >=2.17,<3.0.a0 - brotli - libgcc >=14 - munkres - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - unicodedata2 >=15.1.0 license: MIT license_family: MIT purls: - pkg:pypi/fonttools?source=compressed-mapping size: 3045399 timestamp: 1778770357867 - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.63.0-py312h8a5da7c_0.conda sha256: d235ae7075642044ceb3d922ef2a710a82665755ac9bbb7e8dad7daa72bc6d87 md5: 294fb524171e2a2748cb7fe708aba826 depends: - __glibc >=2.17,<3.0.a0 - brotli - libgcc >=14 - munkres - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - unicodedata2 >=15.1.0 license: MIT license_family: MIT purls: - pkg:pypi/fonttools?source=compressed-mapping size: 3007892 timestamp: 1778770568019 - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.3-ha770c72_0.conda sha256: c934c385889c7836f034039b43b05ccfa98f53c900db03d8411189892ced090b md5: 8462b5322567212beeb025f3519fb3e2 depends: - libfreetype 2.14.3 ha770c72_0 - libfreetype6 2.14.3 h73754d4_0 license: GPL-2.0-only OR FTL purls: [] size: 173839 timestamp: 1774298173462 - conda: https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.2-hd590300_0.conda sha256: aac402a8298f0c0cc528664249170372ef6b37ac39fdc92b40601a6aed1e32ff md5: 3bf7b9fd5a7136126e0234db4b87c8b6 depends: - libgcc-ng >=12 license: MIT license_family: MIT purls: [] size: 77248 timestamp: 1712692454246 - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.15-hecca717_0.conda sha256: 885fa7d1d7e2ad9ed0a700ee0d81ceb49de278253082d517959b22d6336eecce md5: cf09e9fc938518e91d0706572cadf17a depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 license: LGPL-2.0-or-later license_family: LGPL purls: [] size: 100054 timestamp: 1780454302233 - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.62.2-py310h1b8f574_0.conda sha256: e09a98015bc215b02507f5af0499602ea9eaaf104d5cf0135305857eb13cb928 md5: 21caec4bb6765fe65adc364b71a57aa6 depends: - libgcc-ng >=12 - libgrpc 1.62.2 h15f2491_0 - libstdcxx-ng >=12 - libzlib >=1.2.13,<2.0.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/grpcio?source=hash-mapping size: 1003053 timestamp: 1713390837539 - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.78.1-py311h3aa0767_0.conda sha256: 35a45f0a58db104a11a51a485a1b0a9dcde09dafbbaafc8fefeccfb4a5242419 md5: 7435c1c516fb06cd4ac2576ac48cb0c3 depends: - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20260107.1,<20260108.0a0 - libgcc >=14 - libgrpc 1.78.1 h1d1128b_0 - libstdcxx >=14 - libzlib >=1.3.1,<2.0a0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - typing-extensions >=4.12,<5 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/grpcio?source=hash-mapping size: 881750 timestamp: 1774020648884 - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.78.1-py312h39ee1c6_0.conda sha256: 43b09e083e0c3141cb118f84924b498b32d86da5f6a458b60e56e65cd5b97c4e md5: b1995dadc14463d8f914c54cf2061a07 depends: - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20260107.1,<20260108.0a0 - libgcc >=14 - libgrpc 1.78.1 h1d1128b_0 - libstdcxx >=14 - libzlib >=1.3.1,<2.0a0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - typing-extensions >=4.12,<5 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/grpcio?source=hash-mapping size: 875590 timestamp: 1774020588709 - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.16.0-nompi_py310hb9a6f38_102.conda sha256: 5b5a401c2ee28b1f7eb3a060f51a01b9656c9b01a8960132cd5935afc1704dd4 md5: 7f5d5bf2d5d86f6b77682895f44b271e depends: - __glibc >=2.17,<3.0.a0 - cached-property - hdf5 >=2.1.0,<3.0a0 - libgcc >=14 - numpy >=1.21,<3 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/h5py?source=hash-mapping size: 1259075 timestamp: 1775581270235 - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.16.0-nompi_py311hfef529e_102.conda sha256: 9495b6fe2a7b1cf0fded3c53252e964d968fa1fc87cf1ce76c4d9c5e8ab22385 md5: 5737f2130791c91aee8374ee0d4465de depends: - __glibc >=2.17,<3.0.a0 - cached-property - hdf5 >=2.1.0,<3.0a0 - libgcc >=14 - numpy >=1.23,<3 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/h5py?source=hash-mapping size: 1359434 timestamp: 1775581283533 - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.16.0-nompi_py312ha829cd9_102.conda sha256: 578ab0db104435ac003061e103789299720fc49b8b3e8401dabd5130a1ef8663 md5: c6e5cf708b01707fe4cd5e4dc56cf4cc depends: - __glibc >=2.17,<3.0.a0 - cached-property - hdf5 >=2.1.0,<3.0a0 - libgcc >=14 - numpy >=1.23,<3 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/h5py?source=hash-mapping size: 1339689 timestamp: 1775581278758 - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-14.2.1-h6083320_0.conda sha256: da9901aa1e20cbc2369fda212039b294dd02bce95f005539bab840b7310bf7d0 md5: 21ee4640b7c2d94e584349fa12b29b9a depends: - __glibc >=2.17,<3.0.a0 - cairo >=1.18.4,<2.0a0 - graphite2 >=1.3.14,<2.0a0 - icu >=78.3,<79.0a0 - libexpat >=2.8.1,<3.0a0 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - libgcc >=14 - libglib >=2.88.1,<3.0a0 - libstdcxx >=14 - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT purls: [] size: 2362258 timestamp: 1780450503234 - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-2.1.0-nompi_hee75f7e_106.conda sha256: 9a97ad62deba7e832c099b6b4a11f51cab07c0a61b2eb42d249bfddd3fe9f7b4 md5: 16f05278317ac89d2dbd2898c4f8e3d6 depends: - __glibc >=2.17,<3.0.a0 - aws-c-auth >=0.10.3,<0.10.4.0a0 - aws-c-common >=0.13.1,<0.13.2.0a0 - aws-c-http >=0.11.0,<0.11.1.0a0 - aws-c-io >=0.26.3,<0.26.4.0a0 - aws-c-s3 >=0.12.4,<0.12.5.0a0 - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 - libaec >=1.1.5,<2.0a0 - libcurl >=8.20.0,<9.0a0 - libgcc >=14 - libgfortran - libgfortran5 >=14.3.0 - libstdcxx >=14 - libzlib >=1.3.2,<2.0a0 - openssl >=3.5.6,<4.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 4415735 timestamp: 1780577780345 - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-73.2-h59595ed_0.conda sha256: e12fd90ef6601da2875ebc432452590bc82a893041473bc1c13ef29001a73ea8 md5: cc47e1facc155f91abd89b11e48e72ff depends: - libgcc-ng >=12 - libstdcxx-ng >=12 license: MIT license_family: MIT purls: [] size: 12089150 timestamp: 1692900650789 - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.3-h33c6efd_0.conda sha256: fbf86c4a59c2ed05bbffb2ba25c7ed94f6185ec30ecb691615d42342baa1a16a md5: c80d8a3b84358cb967fa81e7075fbc8a depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 license: MIT license_family: MIT purls: [] size: 12723451 timestamp: 1773822285671 - conda: https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda sha256: 0960d06048a7185d3542d850986d807c6e37ca2e644342dd0c72feefcf26c2a4 md5: b38117a3c920364aff79f870c984b4a3 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 license: LGPL-2.1-or-later purls: [] size: 134088 timestamp: 1754905959823 - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.0-py311h724c32c_0.conda sha256: 3ff7e51c88f53f05e22ca5549e935d1ccb398665f6ec080a9c6a5c9e9b186b79 md5: 3d82751e8d682068b58f049edc924ce4 depends: - python - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/kiwisolver?source=hash-mapping size: 77967 timestamp: 1773067041763 - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.5.0-py312h0a2e395_0.conda sha256: eec7654c2d68f06590862c6e845cc70987b6d6559222b6f0e619dea4268f5dd5 md5: cd74a9525dc74bbbf93cf8aa2fa9eb5b depends: - python - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/kiwisolver?source=hash-mapping size: 77120 timestamp: 1773067050308 - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.22.2-ha1258a1_0.conda sha256: 3e307628ca3527448dd1cb14ad7bb9d04d1d28c7d4c5f97ba196ae984571dd25 md5: fb53fb07ce46a575c5d004bbc96032c2 depends: - __glibc >=2.17,<3.0.a0 - keyutils >=1.6.3,<2.0a0 - libedit >=3.1.20250104,<3.2.0a0 - libedit >=3.1.20250104,<4.0a0 - libgcc >=14 - libstdcxx >=14 - openssl >=3.5.5,<4.0a0 license: MIT license_family: MIT purls: [] size: 1386730 timestamp: 1769769569681 - conda: https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.19.1-h0c24ade_1.conda sha256: 112b5b9462572d970f4abd2912f76a25ee7db158b1e7260163d91dd8a630db84 md5: 8b3ce45e929cd8e8e5f4d18586b56d8b depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libjpeg-turbo >=3.1.4.1,<4.0a0 - libtiff >=4.7.1,<4.8.0a0 license: MIT license_family: MIT purls: [] size: 251971 timestamp: 1780211695895 - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45.1-default_hbd61a6d_102.conda sha256: 3d584956604909ff5df353767f3a2a2f60e07d070b328d109f30ac40cd62df6c md5: 18335a698559cdbcd86150a48bf54ba6 depends: - __glibc >=2.17,<3.0.a0 - zstd >=1.5.7,<1.6.0a0 constrains: - binutils_impl_linux-64 2.45.1 license: GPL-3.0-only license_family: GPL purls: [] size: 728002 timestamp: 1774197446916 - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.1.0-hdb68285_0.conda sha256: f84cb54782f7e9cea95e810ea8fef186e0652d0fa73d3009914fa2c1262594e1 md5: a752488c68f2e7c456bcbd8f16eec275 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 license: Apache-2.0 license_family: Apache purls: [] size: 261513 timestamp: 1773113328888 - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20240116.2-cxx17_he02047a_1.conda sha256: 945396726cadae174a661ce006e3f74d71dbd719219faf7cc74696b267f7b0b5 md5: c48fc56ec03229f294176923c3265c05 depends: - __glibc >=2.17,<3.0.a0 - libgcc-ng >=12 - libstdcxx-ng >=12 constrains: - abseil-cpp =20240116.2 - libabseil-static =20240116.2=cxx17* license: Apache-2.0 license_family: Apache purls: [] size: 1264712 timestamp: 1720857377573 - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20260107.1-cxx17_h7b12aa8_0.conda sha256: a7a4481a4d217a3eadea0ec489826a69070fcc3153f00443aa491ed21527d239 md5: 6f7b4302263347698fd24565fbf11310 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 constrains: - libabseil-static =20260107.1=cxx17* - abseil-cpp =20260107.1 license: Apache-2.0 license_family: Apache purls: [] size: 1384817 timestamp: 1770863194876 - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.5-h088129d_0.conda sha256: 822e4ae421a7e9c04e841323526321185f6659222325e1a9aedec811c686e688 md5: 86f7414544ae606282352fa1e116b41f depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 license: BSD-2-Clause license_family: BSD purls: [] size: 36544 timestamp: 1769221884824 - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-8_h4a7cf45_openblas.conda build_number: 8 sha256: b2da6bfd72a1c9cb143ccf64bf5b28790cb4eb58bd1cb978f6537b2322f7d48b md5: 00fc660ab1b2f5ca07e92b4900d10c79 depends: - libopenblas >=0.3.33,<0.3.34.0a0 - libopenblas >=0.3.33,<1.0a0 constrains: - blas 2.308 openblas - mkl <2027 - libcblas 3.11.0 8*_openblas - liblapack 3.11.0 8*_openblas - liblapacke 3.11.0 8*_openblas license: BSD-3-Clause license_family: BSD purls: [] size: 18804 timestamp: 1779859100675 - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-20_linux64_openblas.conda build_number: 20 sha256: 8a0ee1de693a9b3da4a11b95ec81b40dd434bd01fa1f5f38f8268cd2146bf8f0 md5: 2b7bb4f7562c8cf334fc2e20c2d28abc depends: - libopenblas >=0.3.25,<0.3.26.0a0 - libopenblas >=0.3.25,<1.0a0 constrains: - liblapacke 3.9.0 20_linux64_openblas - libcblas 3.9.0 20_linux64_openblas - blas * openblas - liblapack 3.9.0 20_linux64_openblas - mkl <2025 license: BSD-3-Clause license_family: BSD purls: [] size: 14433 timestamp: 1700568383457 - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.2.0-hb03c661_1.conda sha256: 318f36bd49ca8ad85e6478bd8506c88d82454cc008c1ac1c6bf00a3c42fa610e md5: 72c8fd1af66bd67bf580645b426513ed depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: MIT license_family: MIT purls: [] size: 79965 timestamp: 1764017188531 - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.2.0-hb03c661_1.conda sha256: 12fff21d38f98bc446d82baa890e01fd82e3b750378fedc720ff93522ffb752b md5: 366b40a69f0ad6072561c1d09301c886 depends: - __glibc >=2.17,<3.0.a0 - libbrotlicommon 1.2.0 hb03c661_1 - libgcc >=14 license: MIT license_family: MIT purls: [] size: 34632 timestamp: 1764017199083 - conda: https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.2.0-hb03c661_1.conda sha256: a0c15c79997820bbd3fbc8ecf146f4fe0eca36cc60b62b63ac6cf78857f1dd0d md5: 4ffbb341c8b616aa2494b6afb26a0c5f depends: - __glibc >=2.17,<3.0.a0 - libbrotlicommon 1.2.0 hb03c661_1 - libgcc >=14 license: MIT license_family: MIT purls: [] size: 298378 timestamp: 1764017210931 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-8_h0358290_openblas.conda build_number: 8 sha256: 1a2bc77bb26520255904a3d9b1f40e6bf0bf9d8d3405c7709dd162282820915a md5: 33a413f1095f8325e5c30fde3b0d2445 depends: - libblas 3.11.0 8_h4a7cf45_openblas constrains: - blas 2.308 openblas - liblapacke 3.11.0 8*_openblas - liblapack 3.11.0 8*_openblas license: BSD-3-Clause license_family: BSD purls: [] size: 18778 timestamp: 1779859107964 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-20_linux64_openblas.conda build_number: 20 sha256: 0e34fb0f82262f02fcb279ab4a1db8d50875dc98e3019452f8f387e6bf3c0247 md5: 36d486d72ab64ffea932329a1d3729a3 depends: - libblas 3.9.0 20_linux64_openblas constrains: - liblapacke 3.9.0 20_linux64_openblas - blas * openblas - liblapack 3.9.0 20_linux64_openblas - mkl <2025 license: BSD-3-Clause license_family: BSD purls: [] size: 14383 timestamp: 1700568410580 - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-22.1.7-default_h746c552_1.conda sha256: 5100d6571c361a3b4123007b71448a15901ad63ac948f3f02bbc7df4079fe4d1 md5: f5d04d68e7fd19a24f1fe35a74bafabb depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libllvm22 >=22.1.7,<22.2.0a0 - libstdcxx >=14 license: Apache-2.0 WITH LLVM-exception license_family: Apache purls: [] size: 12818349 timestamp: 1780522452233 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h7a8fb5f_6.conda sha256: 205c4f19550f3647832ec44e35e6d93c8c206782bdd620c1d7cf66237580ff9c md5: 49c553b47ff679a6a1e9fc80b9c5a2d4 depends: - __glibc >=2.17,<3.0.a0 - krb5 >=1.22.2,<1.23.0a0 - libgcc >=14 - libstdcxx >=14 - libzlib >=1.3.1,<2.0a0 license: Apache-2.0 license_family: Apache purls: [] size: 4518030 timestamp: 1770902209173 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.20.0-hcf29cc6_0.conda sha256: 75963a5dd913311f59a35dbd307592f4fa754c4808aff9c33edb430c415e38eb md5: c3cc2864f82a944bc90a7beb4d3b0e88 depends: - __glibc >=2.17,<3.0.a0 - krb5 >=1.22.2,<1.23.0a0 - libgcc >=14 - libnghttp2 >=1.68.1,<2.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.2,<2.0a0 - openssl >=3.5.6,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT purls: [] size: 468706 timestamp: 1777461492876 - conda: https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.25-h17f619e_0.conda sha256: aa8e8c4be9a2e81610ddf574e05b64ee131fab5e0e3693210c9d6d2fba32c680 md5: 6c77a605a7a689d17d4819c0f8ac9a00 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: MIT license_family: MIT purls: [] size: 73490 timestamp: 1761979956660 - conda: https://conda.anaconda.org/conda-forge/linux-64/libdrm-2.4.127-hb03c661_0.conda sha256: 7d3187c11b7ae66c5595a8afd5a7ce352a490527fdf6614cab129bc7f2c16ba3 md5: d8d16b9b32a3c5df7e5b3350e2cbe058 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libpciaccess >=0.19,<0.20.0a0 license: MIT license_family: MIT purls: [] size: 311505 timestamp: 1778975798004 - conda: https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20250104-pl5321h7949ede_0.conda sha256: d789471216e7aba3c184cd054ed61ce3f6dac6f87a50ec69291b9297f8c18724 md5: c277e0a4d549b03ac1e9d6cbbe3d017b depends: - ncurses - __glibc >=2.17,<3.0.a0 - libgcc >=13 - ncurses >=6.5,<7.0a0 license: BSD-2-Clause license_family: BSD purls: [] size: 134676 timestamp: 1738479519902 - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-1.7.0-ha4b6fd6_3.conda sha256: 9a25ea93e8272785405a21d30f84e620befb1d545f6dfaae18f06103b5df0443 md5: 75e9f795be506c96dd43cb09c7c8d557 depends: - __glibc >=2.17,<3.0.a0 - libglvnd 1.7.0 ha4b6fd6_3 license: LicenseRef-libglvnd purls: [] size: 46500 timestamp: 1779728188901 - conda: https://conda.anaconda.org/conda-forge/linux-64/libegl-devel-1.7.0-ha4b6fd6_3.conda sha256: e4b46919c9bb65930bce238bd2736110ed7b8c30e5cd5394e4e1edb48de54843 md5: 5bc6d55503483aabe8a90c5e7f49a2a4 depends: - __glibc >=2.17,<3.0.a0 - libegl 1.7.0 ha4b6fd6_3 - libgl-devel 1.7.0 ha4b6fd6_3 - xorg-libx11 license: LicenseRef-libglvnd purls: [] size: 31718 timestamp: 1779728222280 - conda: https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-hd590300_2.conda sha256: 1cd6048169fa0395af74ed5d8f1716e22c19a81a8a36f934c110ca3ad4dd27b4 md5: 172bf1cd1ff8629f2b1179945ed45055 depends: - libgcc-ng >=12 license: BSD-2-Clause license_family: BSD purls: [] size: 112766 timestamp: 1702146165126 - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.8.1-hecca717_0.conda sha256: 363018b25fdb5534c79783d912bd4b685a3547f4fc5996357ad548899b0ee8e7 md5: 93764a5ca80616e9c10106cdaec92f74 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 constrains: - expat 2.8.1.* license: MIT license_family: MIT purls: [] size: 77294 timestamp: 1779278686680 - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h3435931_0.conda sha256: 31f19b6a88ce40ebc0d5a992c131f57d919f73c0b92cd1617a5bec83f6e961e6 md5: a360c33a5abe61c07959e449fa1453eb depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: MIT license_family: MIT purls: [] size: 58592 timestamp: 1769456073053 - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.3-ha770c72_0.conda sha256: 38f014a7129e644636e46064ecd6b1945e729c2140e21d75bb476af39e692db2 md5: e289f3d17880e44b633ba911d57a321b depends: - libfreetype6 >=2.14.3 license: GPL-2.0-only OR FTL purls: [] size: 8049 timestamp: 1774298163029 - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.3-h73754d4_0.conda sha256: 16f020f96da79db1863fcdd8f2b8f4f7d52f177dd4c58601e38e9182e91adf1d md5: fb16b4b69e3f1dcfe79d80db8fd0c55d depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libpng >=1.6.55,<1.7.0a0 - libzlib >=1.3.2,<2.0a0 constrains: - freetype >=2.14.3 license: GPL-2.0-only OR FTL purls: [] size: 384575 timestamp: 1774298162622 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_19.conda sha256: 8e0a3b5e41272e5678499b5dfc4cddb673f9e935de01eb0767ce857001229f46 md5: 57736f29cc2b0ec0b6c2952d3f101b6a depends: - __glibc >=2.17,<3.0.a0 - _openmp_mutex >=4.5 constrains: - libgcc-ng ==15.2.0=*_19 - libgomp 15.2.0 he0feb66_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 1041084 timestamp: 1778269013026 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_19.conda sha256: 9dcf54adfaa5e861123c2da4f2f0451a685464ea7e5a41ad91cf67b31d658d98 md5: 331ee9b72b9dff570d56b1302c5ab37d depends: - libgcc 15.2.0 he0feb66_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 27694 timestamp: 1778269016987 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_19.conda sha256: 561a42758ef25b9ce308c4e2cf56daee4f06138385a17e29a492cd928e00be6f md5: 42bf7eca1a951735fa06c0e3c0d5c8e6 depends: - libgfortran5 15.2.0 h68bc16d_19 constrains: - libgfortran-ng ==15.2.0=*_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 27655 timestamp: 1778269042954 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.2.0-h69a702a_19.conda sha256: 9ca1d254a3e44e608abec6186b18d372cec21e5253e6da9750f4a8f4780ea0bb md5: 35d07243abf828674d273aecd1dd537e depends: - libgfortran 15.2.0 h69a702a_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 27727 timestamp: 1778269220455 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_19.conda sha256: 057978bb69fea29ed715a9b98adf71015c31baecc4aeb2bfc20d4fd5d83579d4 md5: 85072b0ad177c966294f129b7c04a2d5 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=15.2.0 constrains: - libgfortran 15.2.0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 2483673 timestamp: 1778269025089 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_3.conda sha256: ec353b3076ed8e357ed961d0e9ff6997491cade0e603de5bd18a2e301ac78ebd md5: f25206d7322c0e9648e8b83694d143ab depends: - __glibc >=2.17,<3.0.a0 - libglvnd 1.7.0 ha4b6fd6_3 - libglx 1.7.0 ha4b6fd6_3 license: LicenseRef-libglvnd purls: [] size: 133469 timestamp: 1779728207669 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-devel-1.7.0-ha4b6fd6_3.conda sha256: 41d7d864ad1f199bdb06ff6cc3931455c8af62f1d2071a08c6fa08affbcb678f md5: 63e43d278ee5084813fe3c2edf4834ce depends: - __glibc >=2.17,<3.0.a0 - libgl 1.7.0 ha4b6fd6_3 - libglx-devel 1.7.0 ha4b6fd6_3 license: LicenseRef-libglvnd purls: [] size: 115664 timestamp: 1779728218325 - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.88.1-h0d30a3d_2.conda sha256: 33eb5d5310a5c2c0a4707a0afa644801c2e08c8f70c45e1f62f354116dfe0970 md5: 17d484ab9c8179c6a6e5b7dbb5065afc depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libffi >=3.5.2,<3.6.0a0 - pcre2 >=10.47,<10.48.0a0 - libzlib >=1.3.2,<2.0a0 - libiconv >=1.18,<2.0a0 constrains: - glib >2.66 license: LGPL-2.1-or-later purls: [] size: 4754097 timestamp: 1778508800134 - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_3.conda sha256: e019ebe4e3f5cdf23e2f5e58ddf7ade27988c53820115b17b98f218ebcc87748 md5: eb83f3f8cecc3e9bff9e250817fc69b6 depends: - __glibc >=2.17,<3.0.a0 license: LicenseRef-libglvnd purls: [] size: 133586 timestamp: 1779728183422 - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_3.conda sha256: 2f74713c9ca408ea84e88a30a9028153e7b553e8bb42e06139eac9a753c27da9 md5: ec3c4350aa0261bf7f87b8ca15c8e80e depends: - __glibc >=2.17,<3.0.a0 - libglvnd 1.7.0 ha4b6fd6_3 - xorg-libx11 >=1.8.13,<2.0a0 license: LicenseRef-libglvnd purls: [] size: 76586 timestamp: 1779728199059 - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-devel-1.7.0-ha4b6fd6_3.conda sha256: a17ae2d4cb2de04a20882ae14ec3cc1958e868a4dec81e3d7eca30115ee50e94 md5: 16b6330783ce0d1ae8d22782173b32c9 depends: - __glibc >=2.17,<3.0.a0 - libglx 1.7.0 ha4b6fd6_3 - xorg-libx11 >=1.8.13,<2.0a0 - xorg-xorgproto license: LicenseRef-libglvnd purls: [] size: 27363 timestamp: 1779728211402 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_19.conda sha256: 5abe4ab9d93f6c9757d654f1969ae2267d4505315c1f2f8fe705fd60af084f1b md5: faac990cb7aedc7f3a2224f2c9b0c26c depends: - __glibc >=2.17,<3.0.a0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 603817 timestamp: 1778268942614 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.62.2-h15f2491_0.conda sha256: 28241ed89335871db33cb6010e9ccb2d9e9b6bb444ddf6884f02f0857363c06a md5: 8dabe607748cb3d7002ad73cd06f1325 depends: - c-ares >=1.28.1,<2.0a0 - libabseil * cxx17* - libabseil >=20240116.1,<20240117.0a0 - libgcc-ng >=12 - libprotobuf >=4.25.3,<4.25.4.0a0 - libre2-11 >=2023.9.1 - libstdcxx-ng >=12 - libzlib >=1.2.13,<2.0.0a0 - openssl >=3.2.1,<4.0a0 - re2 constrains: - grpc-cpp =1.62.2 license: Apache-2.0 license_family: APACHE purls: [] size: 7316832 timestamp: 1713390645548 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.78.1-h1d1128b_0.conda sha256: 5bb935188999fd70f67996746fd2dca85ec6204289e11695c316772e19451eb8 md5: b5fb6d6c83f63d83ef2721dca6ff7091 depends: - __glibc >=2.17,<3.0.a0 - c-ares >=1.34.6,<2.0a0 - libabseil * cxx17* - libabseil >=20260107.1,<20260108.0a0 - libgcc >=14 - libprotobuf >=6.33.5,<6.33.6.0a0 - libre2-11 >=2025.11.5 - libstdcxx >=14 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.5,<4.0a0 - re2 constrains: - grpc-cpp =1.78.1 license: Apache-2.0 license_family: APACHE purls: [] size: 7021360 timestamp: 1774020290672 - conda: https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.18-h3b78370_2.conda sha256: c467851a7312765447155e071752d7bf9bf44d610a5687e32706f480aad2833f md5: 915f5995e94f60e9a4826e0b0920ee88 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: LGPL-2.1-only purls: [] size: 790176 timestamp: 1754908768807 - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.4.1-hb03c661_0.conda sha256: 10056646c28115b174de81a44e23e3a0a3b95b5347d2e6c45cc6d49d35294256 md5: 6178c6f2fb254558238ef4e6c56fb782 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib purls: [] size: 633831 timestamp: 1775962768273 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-8_h47877c9_openblas.conda build_number: 8 sha256: 168e327d737059553e15cc6ec36d76b9bbb3931c2a7721555fd68b4c9348b247 md5: 809be8ba8712c77bc7d44c2d99390dc4 depends: - libblas 3.11.0 8_h4a7cf45_openblas constrains: - blas 2.308 openblas - libcblas 3.11.0 8*_openblas - liblapacke 3.11.0 8*_openblas license: BSD-3-Clause license_family: BSD purls: [] size: 18790 timestamp: 1779859115086 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-20_linux64_openblas.conda build_number: 20 sha256: ad7745b8d0f2ccb9c3ba7aaa7167d62fc9f02e45eb67172ae5f0dfb5a3b1a2cc md5: 6fabc51f5e647d09cc010c40061557e0 depends: - libblas 3.9.0 20_linux64_openblas constrains: - liblapacke 3.9.0 20_linux64_openblas - libcblas 3.9.0 20_linux64_openblas - blas * openblas - mkl <2025 license: BSD-3-Clause license_family: BSD purls: [] size: 14350 timestamp: 1700568424034 - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm22-22.1.7-hf7376ad_0.conda sha256: 7b2cfedb9a0c4d2329e6b5e527f36e23579c985f00073330c5d739cdd4592218 md5: 963a932cab52c52cb9cda5877d0d8537 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - libxml2 - libxml2-16 >=2.14.6 - libzlib >=1.3.2,<2.0a0 - zstd >=1.5.7,<1.6.0a0 license: Apache-2.0 WITH LLVM-exception license_family: Apache purls: [] size: 44240299 timestamp: 1780445743730 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.3-hb03c661_0.conda sha256: ec30e52a3c1bf7d0425380a189d209a52baa03f22fb66dd3eb587acaa765bd6d md5: b88d90cad08e6bc8ad540cb310a761fb depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 constrains: - xz 5.8.3.* license: 0BSD purls: [] size: 113478 timestamp: 1775825492909 - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb03c661_1.conda sha256: fe171ed5cf5959993d43ff72de7596e8ac2853e9021dec0344e583734f1e0843 md5: 2c21e66f50753a083cbe6b80f38268fa depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: BSD-2-Clause license_family: BSD purls: [] size: 92400 timestamp: 1769482286018 - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.68.1-h877daf1_0.conda sha256: 663444d77a42f2265f54fb8b48c5450bfff4388d9c0f8253dd7855f0d993153f md5: 2a45e7f8af083626f009645a6481f12d depends: - __glibc >=2.17,<3.0.a0 - c-ares >=1.34.6,<2.0a0 - libev >=4.33,<4.34.0a0 - libev >=4.33,<5.0a0 - libgcc >=14 - libstdcxx >=14 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.5,<4.0a0 license: MIT license_family: MIT purls: [] size: 663344 timestamp: 1773854035739 - conda: https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.1-hb9d3cd8_1.conda sha256: 927fe72b054277cde6cb82597d0fcf6baf127dcbce2e0a9d8925a68f1265eef5 md5: d864d34357c3b65a4b731f78c0801dc4 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 license: LGPL-2.1-only license_family: GPL purls: [] size: 33731 timestamp: 1750274110928 - conda: https://conda.anaconda.org/conda-forge/linux-64/libntlm-1.8-hb9d3cd8_0.conda sha256: 3b3f19ced060013c2dd99d9d46403be6d319d4601814c772a3472fe2955612b0 md5: 7c7927b404672409d9917d49bff5f2d6 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 license: LGPL-2.1-or-later purls: [] size: 33418 timestamp: 1734670021371 - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.25-pthreads_h413a1c8_0.conda sha256: 628564517895ee1b09cf72c817548bd80ef1acce6a8214a8520d9f7b44c4cfaf md5: d172b34a443b95f86089e8229ddc9a17 depends: - libgcc-ng >=12 - libgfortran-ng - libgfortran5 >=12.3.0 constrains: - openblas >=0.3.25,<0.3.26.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 5545169 timestamp: 1700536004164 - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.33-pthreads_h94d23a6_0.conda sha256: 3d9aa85648e5e18a6d66db98b8c4317cc426721ad7a220aa86330d1ccedc8903 md5: 2d3278b721e40468295ca755c3b84070 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libgfortran - libgfortran5 >=14.3.0 constrains: - openblas >=0.3.33,<0.3.34.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 5931919 timestamp: 1776993658641 - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_3.conda sha256: 90777039b48529283df5f16383fc399866024257a8bd93de583f4730db1ab30a md5: c2bd8055a2e2dce7a7f32cfd02101fb6 depends: - __glibc >=2.17,<3.0.a0 - libglvnd 1.7.0 ha4b6fd6_3 license: LicenseRef-libglvnd purls: [] size: 51767 timestamp: 1779728204026 - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.19-hb03c661_0.conda sha256: f41721636a7c2e51bc2c642e1127955ab9c81145470714fdaac44d4d09e4af41 md5: 33082e13b4769b48cfeb648e15bfe3fc depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: MIT license_family: MIT purls: [] size: 29147 timestamp: 1773533027610 - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.58-h421ea60_0.conda sha256: 377cfe037f3eeb3b1bf3ad333f724a64d32f315ee1958581fc671891d63d3f89 md5: eba48a68a1a2b9d3c0d9511548db85db depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libzlib >=1.3.2,<2.0a0 license: zlib-acknowledgement purls: [] size: 317729 timestamp: 1776315175087 - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.4-hd5a49e9_0.conda sha256: 076742d4a9fa88711c5fc6726b967e6a03b5060e669aa03288c684a7ae03583b md5: 2772b7ab7bc43f24e9585a714761a255 depends: - __glibc >=2.17,<3.0.a0 - icu >=78.3,<79.0a0 - krb5 >=1.22.2,<1.23.0a0 - libgcc >=14 - openldap >=2.6.13,<2.7.0a0 - openssl >=3.5.6,<4.0a0 license: PostgreSQL purls: [] size: 2754709 timestamp: 1778786234149 - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-4.25.3-hd5b35b9_1.conda sha256: 8b5e4e31ed93bf36fd14e9cf10cd3af78bb9184d0f1f87878b8d28c0374aa4dc md5: 06def97690ef90781a91b786cb48a0a9 depends: - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20240116.2,<20240117.0a0 - libgcc >=13 - libstdcxx >=13 - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 2883090 timestamp: 1727161327039 - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.33.5-h6eeba95_1.conda sha256: a59aa3f076d5710c618ca8fd12d9cd8211d8b738f6b0e0c98517c0162f23a5de md5: 7a4b11f3dd7374f1991a4088390d07c1 depends: - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20260107.1,<20260108.0a0 - libgcc >=14 - libstdcxx >=14 - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 3675765 timestamp: 1780003831209 - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2023.09.01-h5a48ba9_2.conda sha256: 3f3c65fe0e9e328b4c1ebc2b622727cef3e5b81b18228cfa6cf0955bc1ed8eff md5: 41c69fba59d495e8cf5ffda48a607e35 depends: - libabseil * cxx17* - libabseil >=20240116.1,<20240117.0a0 - libgcc-ng >=12 - libstdcxx-ng >=12 constrains: - re2 2023.09.01.* license: BSD-3-Clause license_family: BSD purls: [] size: 232603 timestamp: 1708946763521 - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h0dc7533_1.conda sha256: 138fc85321a8c0731c1715688b38e2be4fb71db349c9ab25f685315095ae70ff md5: ced7f10b6cfb4389385556f47c0ad949 depends: - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20260107.0,<20260108.0a0 - libgcc >=14 - libstdcxx >=14 constrains: - re2 2025.11.05.* license: BSD-3-Clause license_family: BSD purls: [] size: 213122 timestamp: 1768190028309 - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.22-h280c20c_1.conda sha256: b677bbf1c339d894757c3dcfbb2f88649e499e4991d70ae09a1466da9a6c92d6 md5: 965e4d531b588b2e42f66fd8e48b056c depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: ISC purls: [] size: 269272 timestamp: 1779163468406 - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.53.2-h0c1763c_0.conda sha256: 1ab603b6ec93933e76027e1f23b21b22b858ba1b56f1e1695ef6fe5e80cb7358 md5: 062b0ac602fb0adf250e3dfa86f221c4 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libzlib >=1.3.2,<2.0a0 license: blessing purls: [] size: 957849 timestamp: 1780574429573 - conda: https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.11.1-hcf80075_0.conda sha256: fa39bfd69228a13e553bd24601332b7cfeb30ca11a3ca50bb028108fe90a7661 md5: eecce068c7e4eddeb169591baac20ac4 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.0,<4.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 304790 timestamp: 1745608545575 - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_19.conda sha256: dff1058c76ec6b8759e41cefa2508162d00e4a5e6721aa68ec3fd10094e702dc md5: 5794b3bdc38177caf969dabd3af08549 depends: - __glibc >=2.17,<3.0.a0 - libgcc 15.2.0 he0feb66_19 constrains: - libstdcxx-ng ==15.2.0=*_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 5852044 timestamp: 1778269036376 - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_19.conda sha256: 0672b6b6e1791c92e8eccad58081a99d614fcf82bca5841f9dfa3c3e658f83b9 md5: e5ce228e579726c07255dbf90dc62101 depends: - libstdcxx 15.2.0 h934c35e_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 27776 timestamp: 1778269074600 - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_cc-2.19.1-cpu_ha812d39_5.conda sha256: 324ca9745318b6ea5d4b208bfea2ac81005289cd339ddb0dcce850ceca61acbb md5: 05ebb4d47388230ef273ce047083c991 depends: - libml_dtypes-headers >=0.5.1,<0.6 - libtensorflow_framework ==2.19.1 cpu_h0a5eddc_5 - libstdcxx >=14 - libgcc >=14 - _x86_64-microarch-level >=1 - __glibc >=2.17,<3.0.a0 - openssl >=3.5.6,<4.0a0 - libjpeg-turbo >=3.1.4.1,<4.0a0 - flatbuffers >=25.9.23,<25.9.24.0a0 - libabseil >=20260107.1,<20260108.0a0 - libabseil * cxx17* - giflib >=5.2.2,<5.3.0a0 - libzlib >=1.3.2,<2.0a0 - snappy >=1.2.2,<1.3.0a0 - libpng >=1.6.58,<1.7.0a0 - libsqlite >=3.53.1,<4.0a0 - libcurl >=8.20.0,<9.0a0 - libprotobuf >=6.33.5,<6.33.6.0a0 - libgrpc >=1.78.1,<1.79.0a0 - icu >=78.3,<79.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 159026496 timestamp: 1778107988940 - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_framework-2.19.1-cpu_h0a5eddc_5.conda sha256: 83cf93ea6c2ce1eb11e1bffd5ac84e4120add4c4005300f07f869a980f7537a8 md5: cfc4b8f6b8e2df761155fbf67280832c depends: - libstdcxx >=14 - libgcc >=14 - _x86_64-microarch-level >=1 - __glibc >=2.17,<3.0.a0 - openssl >=3.5.6,<4.0a0 - libjpeg-turbo >=3.1.4.1,<4.0a0 - flatbuffers >=25.9.23,<25.9.24.0a0 - libabseil >=20260107.1,<20260108.0a0 - libabseil * cxx17* - giflib >=5.2.2,<5.3.0a0 - libzlib >=1.3.2,<2.0a0 - snappy >=1.2.2,<1.3.0a0 - libpng >=1.6.58,<1.7.0a0 - libsqlite >=3.53.1,<4.0a0 - libcurl >=8.20.0,<9.0a0 - libprotobuf >=6.33.5,<6.33.6.0a0 - libgrpc >=1.78.1,<1.79.0a0 - icu >=78.3,<79.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 10327786 timestamp: 1778107988940 - conda: https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.7.1-h9d88235_1.conda sha256: e5f8c38625aa6d567809733ae04bb71c161a42e44a9fa8227abe61fa5c60ebe0 md5: cd5a90476766d53e901500df9215e927 depends: - __glibc >=2.17,<3.0.a0 - lerc >=4.0.0,<5.0a0 - libdeflate >=1.25,<1.26.0a0 - libgcc >=14 - libjpeg-turbo >=3.1.0,<4.0a0 - liblzma >=5.8.1,<6.0a0 - libstdcxx >=14 - libwebp-base >=1.6.0,<2.0a0 - libzlib >=1.3.1,<2.0a0 - zstd >=1.5.7,<1.6.0a0 license: HPND purls: [] size: 435273 timestamp: 1762022005702 - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.42.1-h5347b49_0.conda sha256: 3f0edf1280e2f6684a986f821eaa3e123d2694a00b31b96ca0d4a4c12c129231 md5: 7d0a66598195ef00b6efc55aefc7453b depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: BSD-3-Clause license_family: BSD purls: [] size: 40163 timestamp: 1779118517630 - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.341.0-h5279c79_0.conda sha256: a68280d57dfd29e3d53400409a39d67c4b9515097eba733aa6fe00c880620e2b md5: 31ad065eda3c2d88f8215b1289df9c89 depends: - __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - libgcc >=14 - xorg-libx11 >=1.8.12,<2.0a0 - xorg-libxrandr >=1.5.5,<2.0a0 constrains: - libvulkan-headers 1.4.341.0.* license: Apache-2.0 license_family: APACHE purls: [] size: 199795 timestamp: 1770077125520 - conda: https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.6.0-hd42ef1d_0.conda sha256: 3aed21ab28eddffdaf7f804f49be7a7d701e8f0e46c856d801270b470820a37b md5: aea31d2e5b1091feca96fcfe945c3cf9 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 constrains: - libwebp 1.6.0 license: BSD-3-Clause license_family: BSD purls: [] size: 429011 timestamp: 1752159441324 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.17.0-h8a09558_0.conda sha256: 666c0c431b23c6cec6e492840b176dde533d48b7e6fb8883f5071223433776aa md5: 92ed62436b625154323d40d5f2f11dd7 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - pthread-stubs - xorg-libxau >=1.0.11,<2.0a0 - xorg-libxdmcp license: MIT license_family: MIT purls: [] size: 395888 timestamp: 1727278577118 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda sha256: 6ae68e0b86423ef188196fff6207ed0c8195dd84273cb5623b85aa08033a410c md5: 5aa797f8787fe7a17d1b0821485b5adc depends: - libgcc-ng >=12 license: LGPL-2.1-or-later purls: [] size: 100393 timestamp: 1702724383534 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.13.2-hca5e8e5_0.conda sha256: 046f2ff4acebd8729fac03e99c8c307dfb48b6a32894ba8c11576e78f6e76e43 md5: dc8b067e22b414172bedd8e3f03f3c95 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - libxcb >=1.17.0,<2.0a0 - libxml2 - libxml2-16 >=2.14.6 - xkeyboard-config - xorg-libxau >=1.0.12,<2.0a0 license: MIT/X11 Derivative license_family: MIT purls: [] size: 851166 timestamp: 1780213397575 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.3-hca6bf5a_0.conda sha256: 3d44f737c5ae52d5af32682cc1530df433f401f8e58a7533926536244127572a md5: e79d2c2f24b027aa8d5ab1b1ba3061e7 depends: - __glibc >=2.17,<3.0.a0 - icu >=78.3,<79.0a0 - libgcc >=14 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - libzlib >=1.3.2,<2.0a0 constrains: - libxml2 2.15.3 license: MIT license_family: MIT purls: [] size: 559775 timestamp: 1776376739004 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.3-h49c6c72_0.conda sha256: 3bc5551720c58591f6ea1146f7d1539c734ed1c40e7b9f5cb8cb7e900c509aba md5: 995d8c8bad2a3cc8db14675a153dec2b depends: - __glibc >=2.17,<3.0.a0 - icu >=78.3,<79.0a0 - libgcc >=14 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - libxml2-16 2.15.3 hca6bf5a_0 - libzlib >=1.3.2,<2.0a0 license: MIT license_family: MIT purls: [] size: 46810 timestamp: 1776376751152 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxslt-1.1.43-h711ed8c_1.conda sha256: 0694760a3e62bdc659d90a14ae9c6e132b525a7900e59785b18a08bb52a5d7e5 md5: 87e6096ec6d542d1c1f8b33245fe8300 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libxml2 - libxml2-16 >=2.14.6 license: MIT license_family: MIT purls: [] size: 245434 timestamp: 1757963724977 - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda sha256: 55044c403570f0dc26e6364de4dc5368e5f3fc7ff103e867c487e2b5ab2bcda9 md5: d87ff7921124eccd67248aa483c23fec depends: - __glibc >=2.17,<3.0.a0 constrains: - zlib 1.3.2 *_2 license: Zlib license_family: Other purls: [] size: 63629 timestamp: 1774072609062 - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py310h3406613_1.conda sha256: 9f3c34f8a7a8dcfed64221a2e19bbe0094ab2c6df7c029b7df713e52c9c9f229 md5: 671afe636d2a97759804723f5afc22e0 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 constrains: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/markupsafe?source=hash-mapping size: 23899 timestamp: 1772445369460 - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py311h3778330_1.conda sha256: 710e207b2e91308a34bcfe547c60ad86c1fa294827266ba18548c1fe1a9d8333 md5: f9efdf9b0f3d0cc309d56af6edf2a6b0 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 constrains: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/markupsafe?source=hash-mapping size: 26756 timestamp: 1772445078834 - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda sha256: 5f3aad1f3a685ed0b591faad335957dbdb1b73abfd6fc731a0d42718e0653b33 md5: 93a4752d42b12943a355b682ee43285b depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 constrains: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/markupsafe?source=hash-mapping size: 26057 timestamp: 1772445297924 - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.9-py311h38be061_0.conda sha256: 6b953d01e930b387114f50c29ca53e4e2c205d5a6a592a0d42f1b16c3740673f md5: a6c99cb32e954b7f06e293058b82a15a depends: - matplotlib-base >=3.10.9,<3.10.10.0a0 - pyside6 >=6.7.2 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - tornado >=5 license: PSF-2.0 license_family: PSF purls: [] size: 17808 timestamp: 1777000594884 - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.9-py312h7900ff3_0.conda sha256: cdd59bb1a52b09979f11c68909d53120b2e749edd1992853a74e1604db19c8b0 md5: 579c6a324b197594fabc9240bddf2d8b depends: - matplotlib-base >=3.10.9,<3.10.10.0a0 - pyside6 >=6.7.2 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - tornado >=5 license: PSF-2.0 license_family: PSF purls: [] size: 17831 timestamp: 1777000588302 - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.9-py311h0f3be63_0.conda sha256: 3e122e4aacab6f07e046d87ddd2ad5896fb3bf344748ca784be3274891a4db8d md5: cc259645be458c9222ffcf321ff5fc1e depends: - __glibc >=2.17,<3.0.a0 - contourpy >=1.0.1 - cycler >=0.10 - fonttools >=4.22.0 - freetype - kiwisolver >=1.3.1 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - libgcc >=14 - libstdcxx >=14 - numpy >=1.23 - numpy >=1.23,<3 - packaging >=20.0 - pillow >=8 - pyparsing >=2.3.1 - python >=3.11,<3.12.0a0 - python-dateutil >=2.7 - python_abi 3.11.* *_cp311 - qhull >=2020.2,<2020.3.0a0 - tk >=8.6.13,<8.7.0a0 license: PSF-2.0 license_family: PSF purls: - pkg:pypi/matplotlib?source=hash-mapping size: 8372143 timestamp: 1777000579013 - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.9-py312he3d6523_0.conda sha256: c7e133837376e53e6a52719c205a3067c42f05769bc3e8307417f8d817dfc63e md5: 7d499b5b6d150f133800dc3a582771c7 depends: - __glibc >=2.17,<3.0.a0 - contourpy >=1.0.1 - cycler >=0.10 - fonttools >=4.22.0 - freetype - kiwisolver >=1.3.1 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - libgcc >=14 - libstdcxx >=14 - numpy >=1.23 - numpy >=1.23,<3 - packaging >=20.0 - pillow >=8 - pyparsing >=2.3.1 - python >=3.12,<3.13.0a0 - python-dateutil >=2.7 - python_abi 3.12.* *_cp312 - qhull >=2020.2,<2020.3.0a0 - tk >=8.6.13,<8.7.0a0 license: PSF-2.0 license_family: PSF purls: - pkg:pypi/matplotlib?source=hash-mapping size: 8336056 timestamp: 1777000573501 - conda: https://conda.anaconda.org/conda-forge/linux-64/ml_dtypes-0.3.2-py310hcc13569_0.conda sha256: b8d490515e31049fb3408c60cb4cdf6dc445f4bc2377033a827dbf68463d2085 md5: 1ad84020b1ab816d7ab3f3d0859faf40 depends: - libgcc-ng >=12 - libstdcxx-ng >=12 - numpy >=1.22.4,<2.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 license: MPL-2.0 AND Apache-2.0 purls: - pkg:pypi/ml-dtypes?source=hash-mapping size: 164312 timestamp: 1704727828371 - conda: https://conda.anaconda.org/conda-forge/linux-64/ml_dtypes-0.5.4-np2py310h3d4ba91_1.conda sha256: 3dacf9d226b632546e901b81059ebca5b10da9dc323ae5b388db472c5206402a md5: 9d02fb99445f6266cb9b05dace5d5ba8 depends: - python - libgcc >=14 - __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - python_abi 3.10.* *_cp310 - numpy >=1.21,<3 license: MPL-2.0 AND Apache-2.0 purls: - pkg:pypi/ml-dtypes?source=hash-mapping size: 342598 timestamp: 1771362428824 - conda: https://conda.anaconda.org/conda-forge/linux-64/ml_dtypes-0.5.4-np2py311h912ec1f_1.conda sha256: 0b9b24d1d6da203729d75c74c531a632e965d0a5a973da964b666637948f5a0b md5: 81be6dcbed9f37c8afb78c2e24de3fe4 depends: - python - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - numpy >=1.23,<3 - python_abi 3.11.* *_cp311 license: MPL-2.0 AND Apache-2.0 purls: - pkg:pypi/ml-dtypes?source=hash-mapping size: 345364 timestamp: 1771362487210 - conda: https://conda.anaconda.org/conda-forge/linux-64/ml_dtypes-0.5.4-np2py312h0f77346_1.conda sha256: e4d06cb57b23319ecee6c2a488c43c6e91c215cdc47a47eb08316beaf3412951 md5: 657a00c06d327012fb3002e5600b2976 depends: - python - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - python_abi 3.12.* *_cp312 - numpy >=1.23,<3 license: MPL-2.0 AND Apache-2.0 purls: - pkg:pypi/ml-dtypes?source=hash-mapping size: 344784 timestamp: 1771362527782 - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.6-hdb14827_0.conda sha256: fc89f74bbe362fb29fa3c037697a89bec140b346a2469a90f7936d1d7ea4d8a3 md5: fc21868a1a5aacc937e7a18747acb8a5 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: X11 AND BSD-3-Clause purls: [] size: 918956 timestamp: 1777422145199 - conda: https://conda.anaconda.org/conda-forge/linux-64/nh3-0.3.5-py310hd8a072f_1.conda noarch: python sha256: 3bb2aa2bf8758f4f1269fa36d67020e4f25199366773e2b73164456ce9286dc3 md5: 8719dd226e9a128bd10033746e2418c6 depends: - python - libgcc >=14 - __glibc >=2.17,<3.0.a0 - _python_abi3_support 1.* - cpython >=3.10 constrains: - __glibc >=2.17 license: MIT license_family: MIT purls: - pkg:pypi/nh3?source=hash-mapping size: 678701 timestamp: 1777219093168 - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.25.2-py310ha4c1d20_0.conda sha256: 81bba557f0f6109f7a1cb8f4d739e5c9ef310a49f8a2842f1fc67bd3545067b0 md5: 188e72aa313da668464e35309e9a32b0 depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libgcc-ng >=12 - liblapack >=3.9.0,<4.0a0 - libstdcxx-ng >=12 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping size: 6853721 timestamp: 1691056650655 - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-1.26.4-py310hb13e2d6_0.conda sha256: 028fe2ea8e915a0a032b75165f11747770326f3d767e642880540c60a3256425 md5: 6593de64c935768b6bad3e19b3e978be depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libgcc-ng >=12 - liblapack >=3.9.0,<4.0a0 - libstdcxx-ng >=12 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping size: 7009070 timestamp: 1707225917496 - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py311h2e04523_0.conda sha256: 8e8fb64c1a51282e8940d57d116aec54a4d66da59594973ae9c0b35d419b9a81 md5: 5d4e35d7097b88c8b1455ef9f6ddf511 depends: - python - libgcc >=14 - __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - libblas >=3.9.0,<4.0a0 - python_abi 3.11.* *_cp311 - liblapack >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=compressed-mapping size: 9389525 timestamp: 1779169198155 - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py312h33ff503_0.conda sha256: dfcbeadb3e7ad0da7a55a0525884ca34c19584154e13cc4159396b305d1bd445 md5: 6e31d55ee1110fda83b4f4045f4d73ff depends: - python - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - liblapack >=3.9.0,<4.0a0 - libblas >=3.9.0,<4.0a0 - python_abi 3.12.* *_cp312 - libcblas >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=compressed-mapping size: 8759520 timestamp: 1779169200325 - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.4.6-py314h2b28147_0.conda sha256: bc61ae892973751a6b0e6ecea57ed6d7053224bddcb007165d6ceb1d7344ad47 md5: f49b5f950379e0b97c35ca97682f7c6a depends: - python - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - liblapack >=3.9.0,<4.0a0 - python_abi 3.14.* *_cp314 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping size: 8928909 timestamp: 1779169198391 - conda: https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.4-h55fea9a_0.conda sha256: 3900f9f2dbbf4129cf3ad6acf4e4b6f7101390b53843591c53b00f034343bc4d md5: 11b3379b191f63139e29c0d19dee24cd depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libpng >=1.6.50,<1.7.0a0 - libstdcxx >=14 - libtiff >=4.7.1,<4.8.0a0 - libzlib >=1.3.1,<2.0a0 license: BSD-2-Clause license_family: BSD purls: [] size: 355400 timestamp: 1758489294972 - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.13-hbde042b_0.conda sha256: 21c4f6c7f41dc9bec2ea2f9c80440d9a4d45a6f2ac13243e658f10dcf1044146 md5: 680608784722880fbfe1745067570b00 depends: - __glibc >=2.17,<3.0.a0 - cyrus-sasl >=2.1.28,<3.0a0 - krb5 >=1.22.2,<1.23.0a0 - libgcc >=14 - libstdcxx >=14 - openssl >=3.5.6,<4.0a0 license: OLDAP-2.8 license_family: BSD purls: [] size: 786149 timestamp: 1775741359582 - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.2-h35e630c_0.conda sha256: c0ef482280e38c71a08ad6d71448194b719630345b0c9c60744a2010e8a8e0cb md5: da1b85b6a87e141f5140bb9924cecab0 depends: - __glibc >=2.17,<3.0.a0 - ca-certificates - libgcc >=14 license: Apache-2.0 license_family: Apache purls: [] size: 3167099 timestamp: 1775587756857 - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.19.1-py310h03d9f68_0.conda sha256: a4e508e003df3a82b23b70c36f7f56e690e7e937c4b3102bdbfccd17134ed50b md5: 782c7dc3146971aa31e623479ca6ece5 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - typing-extensions >=4.6 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 446737 timestamp: 1778047694777 - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.19.1-py311hdf67eae_0.conda sha256: 35dac95d20a7f63f2a613a4830cd0f7e7d1ff323d3101db686eef6cdc2ddf5d9 md5: c81c6109e593265c80d6b18ff4ba5150 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - typing-extensions >=4.6 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 487687 timestamp: 1778047683874 - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.19.1-py312hd9148b4_0.conda sha256: ff6a3f9124d112541f2557e8b40c00dbca9aaf5e254cd16fb485e8ad925c48d6 md5: 5a9273e06750ca36e478c142813e59a8 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - typing-extensions >=4.6 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 492574 timestamp: 1778047684091 - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.0.3-py310h7cbd5c2_1.conda sha256: e8937c160b6eb469c5d80971046b25ed305fd97a8b1d6880de7c4a660cd245c3 md5: 11e0099d4571b4974c04386e4ce679ed depends: - libgcc-ng >=12 - libstdcxx-ng >=12 - numpy >=1.21.6,<2.0a0 - python >=3.10,<3.11.0a0 - python-dateutil >=2.8.1 - python-tzdata >=2022a - python_abi 3.10.* *_cp310 - pytz >=2020.1 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pandas?source=hash-mapping size: 12296643 timestamp: 1688741475871 - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py311hed34c8f_2.conda sha256: a2af9dbc4827db418a73127d4001bb3c2ee19adcd2d4387d6bc049c3780d2a62 md5: 2366b5470cf61614c131e356efe9f74c depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - numpy >=1.22.4 - numpy >=1.23,<3 - python >=3.11,<3.12.0a0 - python-dateutil >=2.8.2 - python-tzdata >=2022.7 - python_abi 3.11.* *_cp311 - pytz >=2020.1 constrains: - matplotlib >=3.6.3 - fsspec >=2022.11.0 - zstandard >=0.19.0 - xarray >=2022.12.0 - lxml >=4.9.2 - pyqt5 >=5.15.9 - sqlalchemy >=2.0.0 - pandas-gbq >=0.19.0 - psycopg2 >=2.9.6 - odfpy >=1.4.1 - gcsfs >=2022.11.0 - pyxlsb >=1.0.10 - qtpy >=2.3.0 - openpyxl >=3.1.0 - fastparquet >=2022.12.0 - beautifulsoup4 >=4.11.2 - html5lib >=1.1 - pytables >=3.8.0 - tabulate >=0.9.0 - pyarrow >=10.0.1 - blosc >=1.21.3 - pyreadstat >=1.2.0 - xlrd >=2.0.1 - numexpr >=2.8.4 - bottleneck >=1.3.6 - scipy >=1.10.0 - tzdata >=2022.7 - s3fs >=2022.11.0 - python-calamine >=0.1.7 - xlsxwriter >=3.0.5 - numba >=0.56.4 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pandas?source=hash-mapping size: 15180047 timestamp: 1764615050121 - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py312hf79963d_1.conda sha256: f633d5f9b28e4a8f66a6ec9c89ef1b6743b880b0511330184b4ab9b7e2dda247 md5: e597b3e812d9613f659b7d87ad252d18 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - numpy >=1.22.4 - numpy >=1.23,<3 - python >=3.12,<3.13.0a0 - python-dateutil >=2.8.2 - python-tzdata >=2022.7 - python_abi 3.12.* *_cp312 - pytz >=2020.1 constrains: - xarray >=2022.12.0 - qtpy >=2.3.0 - html5lib >=1.1 - pandas-gbq >=0.19.0 - tzdata >=2022.7 - fsspec >=2022.11.0 - fastparquet >=2022.12.0 - odfpy >=1.4.1 - pyxlsb >=1.0.10 - scipy >=1.10.0 - sqlalchemy >=2.0.0 - pytables >=3.8.0 - bottleneck >=1.3.6 - pyarrow >=10.0.1 - numexpr >=2.8.4 - pyqt5 >=5.15.9 - xlsxwriter >=3.0.5 - openpyxl >=3.1.0 - blosc >=1.21.3 - matplotlib >=3.6.3 - lxml >=4.9.2 - numba >=0.56.4 - s3fs >=2022.11.0 - tabulate >=0.9.0 - xlrd >=2.0.1 - gcsfs >=2022.11.0 - pyreadstat >=1.2.0 - python-calamine >=0.1.7 - zstandard >=0.19.0 - psycopg2 >=2.9.6 - beautifulsoup4 >=4.11.2 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pandas?source=hash-mapping size: 15099922 timestamp: 1759266031115 - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py314ha0b5721_2.conda sha256: 0a86a582b906d9cfd4d2c59180898fe9d714b55eea7ced71630a1fedae206c62 md5: fe3a5c8be07a7b82058bdeb39d33d93b depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - numpy >=1.22.4 - numpy >=1.23,<3 - python >=3.14,<3.15.0a0 - python-dateutil >=2.8.2 - python-tzdata >=2022.7 - python_abi 3.14.* *_cp314 - pytz >=2020.1 constrains: - pyarrow >=10.0.1 - numba >=0.56.4 - odfpy >=1.4.1 - xlsxwriter >=3.0.5 - tabulate >=0.9.0 - html5lib >=1.1 - lxml >=4.9.2 - blosc >=1.21.3 - s3fs >=2022.11.0 - fsspec >=2022.11.0 - psycopg2 >=2.9.6 - pandas-gbq >=0.19.0 - openpyxl >=3.1.0 - qtpy >=2.3.0 - python-calamine >=0.1.7 - sqlalchemy >=2.0.0 - pyqt5 >=5.15.9 - bottleneck >=1.3.6 - zstandard >=0.19.0 - numexpr >=2.8.4 - tzdata >=2022.7 - scipy >=1.10.0 - gcsfs >=2022.11.0 - pyxlsb >=1.0.10 - matplotlib >=3.6.3 - pytables >=3.8.0 - beautifulsoup4 >=4.11.2 - pyreadstat >=1.2.0 - fastparquet >=2022.12.0 - xlrd >=2.0.1 - xarray >=2022.12.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pandas?source=hash-mapping size: 15178918 timestamp: 1764615084415 - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda sha256: 5e6f7d161356fefd981948bea5139c5aa0436767751a6930cb1ca801ebb113ff md5: 7a3bff861a6583f1889021facefc08b1 depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - libgcc >=14 - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 1222481 timestamp: 1763655398280 - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.2.0-py311hf88fc01_0.conda sha256: 5b182a7588874e497514b52e2ef278b66fa4089e94379d249897df28b917a659 md5: b4e4b0fc807b68aa1706457f2e31279d depends: - python - __glibc >=2.17,<3.0.a0 - libgcc >=14 - lcms2 >=2.18,<3.0a0 - libxcb >=1.17.0,<2.0a0 - python_abi 3.11.* *_cp311 - tk >=8.6.13,<8.7.0a0 - zlib-ng >=2.3.3,<2.4.0a0 - libwebp-base >=1.6.0,<2.0a0 - libtiff >=4.7.1,<4.8.0a0 - openjpeg >=2.5.4,<3.0a0 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - libjpeg-turbo >=3.1.2,<4.0a0 license: HPND purls: - pkg:pypi/pillow?source=hash-mapping size: 1056849 timestamp: 1775060059882 - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.2.0-py312h50c33e8_0.conda sha256: fa291f8915114733dc1df9f1627b8c63c517217c1eee1a6ede2ceb5e368cf27a md5: 9e5609720e31213d4f39afe377f6217e depends: - python - libgcc >=14 - __glibc >=2.17,<3.0.a0 - lcms2 >=2.18,<3.0a0 - libxcb >=1.17.0,<2.0a0 - libjpeg-turbo >=3.1.2,<4.0a0 - libtiff >=4.7.1,<4.8.0a0 - libwebp-base >=1.6.0,<2.0a0 - openjpeg >=2.5.4,<3.0a0 - python_abi 3.12.* *_cp312 - tk >=8.6.13,<8.7.0a0 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - zlib-ng >=2.3.3,<2.4.0a0 license: HPND purls: - pkg:pypi/pillow?source=hash-mapping size: 1039561 timestamp: 1775060059882 - conda: https://conda.anaconda.org/conda-forge/linux-64/pixman-0.46.4-h54a6638_1.conda sha256: 43d37bc9ca3b257c5dd7bf76a8426addbdec381f6786ff441dc90b1a49143b6a md5: c01af13bdc553d1a8fbfff6e8db075f0 depends: - libgcc >=14 - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 license: MIT license_family: MIT purls: [] size: 450960 timestamp: 1754665235234 - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-4.25.3-py310h0e2eeba_1.conda sha256: 03361d8328089e3f0b762e887fefb74bb74f3d81325f8c448054a90adb0bc6c8 md5: 3e274aa9ac708a9f34ee2a0009c01e5b depends: - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20240116.2,<20240117.0a0 - libgcc >=13 - libprotobuf >=4.25.3,<4.25.4.0a0 - libstdcxx >=13 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - setuptools license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/protobuf?source=hash-mapping size: 331722 timestamp: 1725018565633 - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-6.33.5-py311h3f0a9aa_2.conda sha256: f6184d8b4c2c39ef107b579f3712a38947ad52d5bd1c9415c919057ea8aed785 md5: 3b8a5188f2b1365af1105c40a7ec32e9 depends: - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20260107.1,<20260108.0a0 - libgcc >=14 - libstdcxx >=14 - libzlib >=1.3.1,<2.0a0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 constrains: - libprotobuf 6.33.5 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/protobuf?source=hash-mapping size: 488615 timestamp: 1773265956304 - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-6.33.5-py312ha7b3241_2.conda sha256: 53997629b27a2465989f23e3a6212cfcc19f51c474d8f89905bb99859140ee88 md5: 0aee4f9ff95fc4bc78264a93e2a74adf depends: - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20260107.1,<20260108.0a0 - libgcc >=14 - libstdcxx >=14 - libzlib >=1.3.1,<2.0a0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 constrains: - libprotobuf 6.33.5 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/protobuf?source=hash-mapping size: 481654 timestamp: 1773265949321 - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py311haee01d2_0.conda sha256: 8d9325af538a8f56013e42bbb91a4dc6935aece34476e20bafacf6007b571e86 md5: 2ed8f6fe8b51d8e19f7621941f7bb95f depends: - python - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/psutil?source=hash-mapping size: 231786 timestamp: 1769678156460 - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h5253ce2_0.conda sha256: d834fd656133c9e4eaf63ffe9a117c7d0917d86d89f7d64073f4e3a0020bd8a7 md5: dd94c506b119130aef5a9382aed648e7 depends: - python - libgcc >=14 - __glibc >=2.17,<3.0.a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/psutil?source=hash-mapping size: 225545 timestamp: 1769678155334 - conda: https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-hb9d3cd8_1002.conda sha256: 9c88f8c64590e9567c6c80823f0328e58d3b1efb0e1c539c0315ceca764e0973 md5: b3c17d95b5a10c6e64a21fa17573e70e depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 license: MIT license_family: MIT purls: [] size: 8252 timestamp: 1726802366959 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.1-py311hf27b23e_1.conda sha256: 3cd4963051cffa6d96972cd8e42e6b224bbf385353e9a743940b4434fba176e6 md5: dfd3d0af46ab4c53740abe6d6dbdd403 depends: - python - qt6-main 6.11.1.* - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - libegl >=1.7.0,<2.0a0 - libopengl >=1.7.0,<2.0a0 - libvulkan-loader >=1.4.341.0,<2.0a0 - libgl >=1.7.0,<2.0a0 - python_abi 3.11.* *_cp311 - libxslt >=1.1.43,<2.0a0 - libxml2 - libxml2-16 >=2.14.6 - libclang13 >=22.1.5 - qt6-main >=6.11.1,<6.12.0a0 license: LGPL-3.0-only license_family: LGPL purls: - pkg:pypi/pyside6?source=hash-mapping - pkg:pypi/shiboken6?source=hash-mapping size: 13815486 timestamp: 1778933870587 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.11.1-py312h50ac2ff_1.conda sha256: 6f9e4fd9f6aa1d82a524384399c956c0c79c6c5df5ae42e241eb59f42c11ffbf md5: 90f891bc96f673acbff89f6f405aef10 depends: - python - qt6-main 6.11.1.* - libgcc >=14 - libstdcxx >=14 - __glibc >=2.17,<3.0.a0 - libopengl >=1.7.0,<2.0a0 - libclang13 >=22.1.5 - libxslt >=1.1.43,<2.0a0 - qt6-main >=6.11.1,<6.12.0a0 - libvulkan-loader >=1.4.341.0,<2.0a0 - libegl >=1.7.0,<2.0a0 - libxml2 - libxml2-16 >=2.14.6 - libgl >=1.7.0,<2.0a0 - python_abi 3.12.* *_cp312 license: LGPL-3.0-only license_family: LGPL purls: - pkg:pypi/pyside6?source=compressed-mapping size: 13797566 timestamp: 1778933891067 - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.10.20-h3c07f61_0_cpython.conda sha256: 8ff2ce308faf2588b69c65b120293f59a8f2577b772b34df4e817d220b09e081 md5: 5d4e2b00d99feacd026859b7fa239dc0 depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - ld_impl_linux-64 >=2.36.1 - libexpat >=2.7.4,<3.0a0 - libffi >=3.4,<4.0a0 - libgcc >=14 - liblzma >=5.8.2,<6.0a0 - libnsl >=2.0.1,<2.1.0a0 - libsqlite >=3.51.2,<4.0a0 - libuuid >=2.41.3,<3.0a0 - libxcrypt >=4.4.36 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.5,<4.0a0 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.10.* *_cp310 license: Python-2.0 purls: [] size: 25455342 timestamp: 1772729810280 - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.15-hd63d673_0_cpython.conda sha256: bf6a32c69889d38482436a786bea32276756cedf0e9805cc856ffd088e8d00f0 md5: a5ebcefec0c12a333bcd6d7bf3bddc1f depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - ld_impl_linux-64 >=2.36.1 - libexpat >=2.7.4,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - libgcc >=14 - liblzma >=5.8.2,<6.0a0 - libnsl >=2.0.1,<2.1.0a0 - libsqlite >=3.51.2,<4.0a0 - libuuid >=2.41.3,<3.0a0 - libxcrypt >=4.4.36 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.5,<4.0a0 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.11.* *_cp311 license: Python-2.0 purls: [] size: 30949404 timestamp: 1772730362552 - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda sha256: a44655c1c3e1d43ed8704890a91e12afd68130414ea2c0872e154e5633a13d7e md5: 7eccb41177e15cc672e1babe9056018e depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - ld_impl_linux-64 >=2.36.1 - libexpat >=2.7.4,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - libgcc >=14 - liblzma >=5.8.2,<6.0a0 - libnsl >=2.0.1,<2.1.0a0 - libsqlite >=3.51.2,<4.0a0 - libuuid >=2.41.3,<3.0a0 - libxcrypt >=4.4.36 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.5,<4.0a0 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.12.* *_cp312 license: Python-2.0 purls: [] size: 31608571 timestamp: 1772730708989 - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.5-habeac84_100_cp314.conda build_number: 100 sha256: 55eed9bf2a3f6e90311276f0834737fe7c2d9ec3e5e2e557507858df4c7521e6 md5: da92e59ff92f2d5ede4f612af20f583f depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - ld_impl_linux-64 >=2.36.1 - libexpat >=2.8.0,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - libgcc >=14 - liblzma >=5.8.3,<6.0a0 - libmpdec >=4.0.0,<5.0a0 - libsqlite >=3.53.1,<4.0a0 - libuuid >=2.42.1,<3.0a0 - libzlib >=1.3.2,<2.0a0 - ncurses >=6.6,<7.0a0 - openssl >=3.5.6,<4.0a0 - python_abi 3.14.* *_cp314 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata - zstd >=1.5.7,<1.6.0a0 license: Python-2.0 purls: [] size: 36745188 timestamp: 1779236923603 python_site_packages_path: lib/python3.14/site-packages - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py311h3778330_1.conda sha256: c9a6cd2c290d7c3d2b30ea34a0ccda30f770e8ddb2937871f2c404faf60d0050 md5: a24add9a3bababee946f3bc1c829acfe depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT purls: - pkg:pypi/pyyaml?source=hash-mapping size: 206190 timestamp: 1770223702917 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda sha256: cb142bfd92f6e55749365ddc244294fa7b64db6d08c45b018ff1c658907bfcbf md5: 15878599a87992e44c059731771591cb depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT purls: - pkg:pypi/pyyaml?source=hash-mapping size: 198293 timestamp: 1770223620706 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py311hca8cfc1_3.conda sha256: 2c15f4c797f3f0c2ee8f4446294b9cea60a20ca724e0ab3212d2930926f08757 md5: efdc6d747c064d0c4cd6fcbbc9c57c58 depends: - python - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - zeromq >=4.3.5,<4.4.0a0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pyzmq?source=hash-mapping size: 383048 timestamp: 1779483879092 - conda: https://conda.anaconda.org/conda-forge/linux-64/qhull-2020.2-h434a139_5.conda sha256: 776363493bad83308ba30bcb88c2552632581b143e8ee25b1982c8c743e73abc md5: 353823361b1d27eb3960efb076dfcaf6 depends: - __glibc >=2.17,<3.0.a0 - libgcc-ng >=12 - libstdcxx-ng >=12 license: LicenseRef-Qhull purls: [] size: 552937 timestamp: 1720813982144 - conda: https://conda.anaconda.org/conda-forge/linux-64/qt6-main-6.11.1-pl5321h16c4a6b_1.conda sha256: aefbc43bde188ff4027d480da99c7fa9e8e6341e9762e065190239cb9b99bb1c md5: 331d660aef48fec733a878dd1f8f4206 depends: - libxcb - xcb-util - xcb-util-wm - xcb-util-keysyms - xcb-util-image - xcb-util-renderutil - xcb-util-cursor - libgl-devel - libegl-devel - libgcc >=14 - __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - xcb-util >=0.4.1,<0.5.0a0 - xorg-libx11 >=1.8.13,<2.0a0 - pcre2 >=10.47,<10.48.0a0 - libbrotlicommon >=1.2.0,<1.3.0a0 - libbrotlienc >=1.2.0,<1.3.0a0 - libbrotlidec >=1.2.0,<1.3.0a0 - fontconfig >=2.18.1,<3.0a0 - fonts-conda-ecosystem - xorg-libxxf86vm >=1.1.7,<2.0a0 - xorg-libxrandr >=1.5.5,<2.0a0 - libsqlite >=3.53.2,<4.0a0 - libpq >=18.4,<19.0a0 - xorg-libice >=1.1.2,<2.0a0 - libtiff >=4.7.1,<4.8.0a0 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - wayland >=1.25.0,<2.0a0 - libzlib >=1.3.2,<2.0a0 - libvulkan-loader >=1.4.341.0,<2.0a0 - xorg-libxext >=1.3.7,<2.0a0 - xcb-util-keysyms >=0.4.1,<0.5.0a0 - libpng >=1.6.58,<1.7.0a0 - harfbuzz >=14.2.1 - xcb-util-cursor >=0.1.6,<0.2.0a0 - xorg-libxcursor >=1.2.3,<2.0a0 - libcups >=2.3.3,<2.4.0a0 - libxcb >=1.17.0,<2.0a0 - libjpeg-turbo >=3.1.4.1,<4.0a0 - libdrm >=2.4.127,<2.5.0a0 - xorg-libxcomposite >=0.4.7,<1.0a0 - xcb-util-image >=0.4.0,<0.5.0a0 - xcb-util-wm >=0.4.2,<0.5.0a0 - zstd >=1.5.7,<1.6.0a0 - krb5 >=1.22.2,<1.23.0a0 - xcb-util-renderutil >=0.3.10,<0.4.0a0 - icu >=78.3,<79.0a0 - xorg-libxdamage >=1.1.6,<2.0a0 - xorg-libsm >=1.2.6,<2.0a0 - alsa-lib >=1.2.16,<1.3.0a0 - openssl >=3.5.6,<4.0a0 - libglib >=2.88.1,<3.0a0 - libgl >=1.7.0,<2.0a0 - libxkbcommon >=1.13.2,<2.0a0 - libwebp-base >=1.6.0,<2.0a0 - double-conversion >=3.4.0,<3.5.0a0 - dbus >=1.16.2,<2.0a0 - xorg-libxtst >=1.2.5,<2.0a0 - libegl >=1.7.0,<2.0a0 - libxml2 - libxml2-16 >=2.14.6 constrains: - qt ==6.11.1 license: LGPL-3.0-only license_family: LGPL purls: [] size: 60185421 timestamp: 1780593127053 - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2023.09.01-h7f4b329_2.conda sha256: f0f520f57e6b58313e8c41abc7dfa48742a05f1681f05654558127b667c769a8 md5: 8f70e36268dea8eb666ef14c29bd3cda depends: - libre2-11 2023.09.01 h5a48ba9_2 license: BSD-3-Clause license_family: BSD purls: [] size: 26617 timestamp: 1708946796423 - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_1.conda sha256: 3fc684b81631348540e9a42f6768b871dfeab532d3f47d5c341f1f83e2a2b2b2 md5: 66a715bc01c77d43aca1f9fcb13dde3c depends: - libre2-11 2025.11.05 h0dc7533_1 license: BSD-3-Clause license_family: BSD purls: [] size: 27469 timestamp: 1768190052132 - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda sha256: 12ffde5a6f958e285aa22c191ca01bbd3d6e710aa852e00618fa6ddc59149002 md5: d7d95fc8287ea7bf33e0e7116d2b95ec depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - ncurses >=6.5,<7.0a0 license: GPL-3.0-only license_family: GPL purls: [] size: 345073 timestamp: 1765813471974 - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-2026.5.1-py311h1baac5b_0.conda sha256: a2a40c189cf925a0c6dcffba5b02011d4be0e555ffe40d122a88a15ae328dd35 md5: d95df38d5ea0f1134ef39e16ed1c28e9 depends: - python - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python_abi 3.11.* *_cp311 constrains: - __glibc >=2.17 license: MIT license_family: MIT purls: - pkg:pypi/rpds-py?source=compressed-mapping size: 308612 timestamp: 1779976991365 - conda: https://conda.anaconda.org/conda-forge/linux-64/ruff-0.14.2-ha3a3aed_0.conda noarch: python sha256: d6c3ee6381ce275107a3fb3cfe82499f3cb74da1d95e134a93ed5e4851b4ec01 md5: ba27344e81c9c460c9502fcbf7bd1e5c depends: - python - __glibc >=2.17,<3.0.a0 - libgcc >=14 constrains: - __glibc >=2.17 license: MIT license_family: MIT purls: - pkg:pypi/ruff?source=hash-mapping size: 11071649 timestamp: 1761253375744 - conda: https://conda.anaconda.org/conda-forge/linux-64/s2n-1.7.3-hc5a330e_0.conda sha256: 150a0a5254e8b15ad737549721c7d13406cd96432f3f446e07073dbd98bb2491 md5: f2bd09e21c5844a12e2f5eefcd075555 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - openssl >=3.5.6,<4.0a0 license: Apache-2.0 license_family: Apache purls: [] size: 388111 timestamp: 1778113913631 - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.4.2-py310h981052a_1.conda sha256: b3718226723c94f5a93f417acb29ad82b0520acf945a06ae90e0b7ed076191a7 md5: 672f0238a945f1c98fe97b147c8a040a depends: - _openmp_mutex >=4.5 - joblib >=1.2.0 - libgcc-ng >=12 - libstdcxx-ng >=12 - numpy >=1.19,<3 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - scipy - threadpoolctl >=2.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9132101 timestamp: 1715869775101 - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.4.2-py311he08f58d_1.conda sha256: b818f7df6ae949012a38b41b6577ac2319569971b1a063c0386447ec2c6c09ed md5: fd4a80e35c05513590b33c83fc81dcc7 depends: - _openmp_mutex >=4.5 - joblib >=1.2.0 - libgcc-ng >=12 - libstdcxx-ng >=12 - numpy >=1.19,<3 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - scipy - threadpoolctl >=2.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 10331975 timestamp: 1715869752060 - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.5.2-py311h57cc02b_1.conda sha256: b6489f65911847d1f9807e254e9af0815548454b911df4d0b5019f9ab16fe530 md5: d1b6d7a73364d9fe20d2863bd2c43e3a depends: - __glibc >=2.17,<3.0.a0 - _openmp_mutex >=4.5 - joblib >=1.2.0 - libgcc >=13 - libstdcxx >=13 - numpy >=1.19,<3 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - scipy - threadpoolctl >=3.1.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 10596895 timestamp: 1726083362968 - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.6.1-py312h7a48858_0.conda sha256: 7c869c73c95ef09edef839448ae3d153c4e3a208fb110c4260225f342d23e08e md5: 102727f71df02a51e9e173f2e6f87d57 depends: - __glibc >=2.17,<3.0.a0 - _openmp_mutex >=4.5 - joblib >=1.2.0 - libgcc >=13 - libstdcxx >=13 - numpy >=1.19,<3 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - scipy - threadpoolctl >=3.1.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 10628698 timestamp: 1736497249999 - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.7.2-py310h228f341_0.conda sha256: 8af2a49b75b9697653a0bf55717c8153732c4fc53f58d4aa0ed692ae348c49f6 md5: 0f3e3324506bd3e67934eda9895f37a7 depends: - __glibc >=2.17,<3.0.a0 - _openmp_mutex >=4.5 - joblib >=1.2.0 - libgcc >=14 - libstdcxx >=14 - numpy >=1.21,<3 - numpy >=1.22.0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - scipy >=1.8.0 - threadpoolctl >=3.1.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 8415134 timestamp: 1757406407327 - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py311ha15b03d_0.conda sha256: 8d9c2c1d676091fcbc04c8419d8d0b474c5019df07531e7fb4860c94466c4c1d md5: 7f2415bac058bf107a28457fcc2989e7 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.4.0 - threadpoolctl >=3.5.0 - narwhals >=2.0.1 - _openmp_mutex >=4.5 - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - python_abi 3.11.* *_cp311 - numpy >=1.23,<3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=compressed-mapping size: 10240664 timestamp: 1780401051398 - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py312h3226591_0.conda sha256: 8c0cd0326b5a17ddcf189fc4f119bf6871b7853595c088075847c484a3ed567e md5: e6e9b5795bb495325c3b4ebd451519aa depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.4.0 - threadpoolctl >=3.5.0 - narwhals >=2.0.1 - libgcc >=14 - _openmp_mutex >=4.5 - __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - numpy >=1.23,<3 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=compressed-mapping size: 10038167 timestamp: 1780401052981 - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.9.0-np2py314hf09ca88_0.conda sha256: 6a01f4403db746acd676e34e80e3a14d041f2261d658402ca13dae6407c35d44 md5: 30883954413aad9e3ac42134bef91ffe depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.4.0 - threadpoolctl >=3.5.0 - narwhals >=2.0.1 - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - _openmp_mutex >=4.5 - numpy >=1.23,<3 - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=compressed-mapping size: 10311253 timestamp: 1780401051520 - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.11.4-py310hb13e2d6_0.conda sha256: d465ffa50c22c0ce7472831abbb01f10411bb62d2ee3b493ff3fe3dc214765cb md5: f0063b2885bfae11324a00a693f88781 depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libgcc-ng >=12 - libgfortran-ng - libgfortran5 >=12.3.0 - liblapack >=3.9.0,<4.0a0 - libstdcxx-ng >=12 - numpy >=1.22.4,<1.28 - numpy >=1.22.4,<2.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 constrains: - libopenblas <0.3.26 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 15254417 timestamp: 1700813355821 - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.15.2-py310h1d65ade_0.conda sha256: 4cb98641f870666d365594013701d5691205a0fe81ac3ba7778a23b1cc2caa8e md5: 8c29cd33b64b2eb78597fa28b5595c8d depends: - __glibc >=2.17,<3.0.a0 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libgcc >=13 - libgfortran - libgfortran5 >=13.3.0 - liblapack >=3.9.0,<4.0a0 - libstdcxx >=13 - numpy <2.5 - numpy >=1.19,<3 - numpy >=1.23.5 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 16417101 timestamp: 1739791865060 - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.15.2-py311h8f841c2_0.conda sha256: 6d0902775e3ff96dd1d36ac627e03fe6c0b3d2159bb71e115dd16a1f31693b25 md5: 5ec0a1732a05376241e1e4c6d50e0e91 depends: - __glibc >=2.17,<3.0.a0 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libgcc >=13 - libgfortran - libgfortran5 >=13.3.0 - liblapack >=3.9.0,<4.0a0 - libstdcxx >=13 - numpy <2.5 - numpy >=1.19,<3 - numpy >=1.23.5 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 17193126 timestamp: 1739791897768 - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.15.2-py312ha707e6e_0.conda sha256: b9faaa024b77a3678a988c5a490f02c4029c0d5903998b585100e05bc7d4ff36 md5: 00b999c5f9d01fb633db819d79186bd4 depends: - __glibc >=2.17,<3.0.a0 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libgcc >=13 - libgfortran - libgfortran5 >=13.3.0 - liblapack >=3.9.0,<4.0a0 - libstdcxx >=13 - numpy <2.5 - numpy >=1.19,<3 - numpy >=1.23.5 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 17064784 timestamp: 1739791925628 - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py311hbe70eeb_1.conda sha256: 3ae2ff1d1cc5930de2ca6ac03216118bdf13b2af6098e28e827f1ba25bfcbc4e md5: 089de2ee37e4e19885c985a4fe4aaf14 depends: - __glibc >=2.17,<3.0.a0 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libgcc >=14 - libgfortran - libgfortran5 >=14.3.0 - liblapack >=3.9.0,<4.0a0 - libstdcxx >=14 - numpy <2.7 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=compressed-mapping size: 17303931 timestamp: 1779874783665 - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py312h54fa4ab_1.conda sha256: d5ac05ad45c0d48731eb189c2cbb2bb99f0e3cb7e1acaad373cb2f1f2597fc75 md5: 15995ecb2ef890778ba9a3750190f09d depends: - __glibc >=2.17,<3.0.a0 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libgcc >=14 - libgfortran - libgfortran5 >=14.3.0 - liblapack >=3.9.0,<4.0a0 - libstdcxx >=14 - numpy <2.7 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=compressed-mapping size: 16828243 timestamp: 1779874781187 - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.17.1-py314hf07bd8e_1.conda sha256: 505e3466e97c16d125a9adb61a80bdfc2fefe62bc9f0bfe798eda88706e4b0ed md5: 718437171257e579e7d1f3b51c62536f depends: - __glibc >=2.17,<3.0.a0 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libgcc >=14 - libgfortran - libgfortran5 >=14.3.0 - liblapack >=3.9.0,<4.0a0 - libstdcxx >=14 - numpy <2.7 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=compressed-mapping size: 16995364 timestamp: 1779874760991 - conda: https://conda.anaconda.org/conda-forge/linux-64/secretstorage-3.5.0-py311h38be061_0.conda sha256: 2a9e51caca89cad9e7079fbe05ca6bcfc29503297a312dd4b00e30f080e3fa3e md5: 7aca89ddc1696400582cf6b4de8cf75e depends: - cryptography >=2.0 - dbus - jeepney >=0.6 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/secretstorage?source=hash-mapping size: 33898 timestamp: 1780858514693 - conda: https://conda.anaconda.org/conda-forge/linux-64/snappy-1.2.2-h03e3b7b_1.conda sha256: 48f3f6a76c34b2cfe80de9ce7f2283ecb55d5ed47367ba91e8bb8104e12b8f11 md5: 98b6c9dc80eb87b2519b97bcf7e578dd depends: - libgcc >=14 - __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - libgcc >=14 license: BSD-3-Clause license_family: BSD purls: [] size: 45829 timestamp: 1762948049098 - conda: https://conda.anaconda.org/conda-forge/linux-64/statsmodels-0.14.6-py311h0372a8f_0.conda sha256: b5a0d724e8490b887ddf65fd6feccb5ba535d4e6276bf389e52eee6d747115d6 md5: dd92402db25b74b98489a4c144f14b62 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - numpy <3,>=1.22.3 - numpy >=1.23,<3 - packaging >=21.3 - pandas !=2.1.0,>=1.4 - patsy >=0.5.6 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - scipy !=1.9.2,>=1.8 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/statsmodels?source=hash-mapping size: 12109735 timestamp: 1764983382505 - conda: https://conda.anaconda.org/conda-forge/linux-64/statsmodels-0.14.6-py312h4f23490_0.conda sha256: 0c61eccf3f71b9812da8ced747b1f22bafd6f66f9a64abe06bbe147a03b7322e md5: 423b8676bd6eed60e97097b33f13ea3f depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - numpy <3,>=1.22.3 - numpy >=1.23,<3 - packaging >=21.3 - pandas !=2.1.0,>=1.4 - patsy >=0.5.6 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - scipy !=1.9.2,>=1.8 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/statsmodels?source=hash-mapping size: 11903737 timestamp: 1764983555676 - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorboard-data-server-0.7.0-py310hf0f1799_4.conda sha256: 8fd652115ca94768a3760ef3ca15da9c7f040538cff936bf04429658af32d419 md5: 7440bff77da787eb26e7b58c13a9a6a3 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - openssl >=3.5.4,<4.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 constrains: - __glibc >=2.17 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tensorboard-data-server?source=hash-mapping size: 3490698 timestamp: 1764930051334 - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorboard-data-server-0.7.0-py311h97c413e_4.conda sha256: 61a6d78685c4edd1562dcf433536ee307fff98d7ca67946c31c660b5ef27ccc1 md5: e2d333d85b459ca7fce89351e2ca3c9e depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - openssl >=3.5.4,<4.0a0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 constrains: - __glibc >=2.17 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tensorboard-data-server?source=hash-mapping size: 3494880 timestamp: 1764929829640 - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorboard-data-server-0.7.0-py312h4eba8b5_4.conda sha256: 9a3faf2dd703667683feee3cb0ecb9ed035b0a5193b95da3a633ff9da029fea9 md5: 7d6b10ed9058f6a97cd8007605b61c33 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - openssl >=3.5.4,<4.0a0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 constrains: - __glibc >=2.17 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tensorboard-data-server?source=hash-mapping size: 3491485 timestamp: 1764929907168 - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-2.16.1-cpu_py310h49b650b_0.conda sha256: e51f41cf58f445687520283042ce782d1d4cc6561048efce93d84909548330d9 md5: 0642b314cfbc3e2be4dbd5c3097aee2d depends: - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - tensorflow-base 2.16.1 cpu_py310h224022f_0 - tensorflow-estimator 2.16.1 cpu_py310hc6dcfef_0 track_features: - tensorflow-cpu license: Apache-2.0 license_family: Apache purls: [] size: 42219 timestamp: 1716523409933 - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-2.19.1-cpu_py311hae6871b_5.conda sha256: 30b4ca8ceb2a622610b96cc04130f06e328588cb67b8ea7f719a661d68285129 md5: 927fdf8ac15af3ad2758dcf109cc5912 depends: - python - tensorflow-base ==2.19.1 cpu_py311hca13484_5 - python_abi 3.11.* *_cp311 license: Apache-2.0 license_family: APACHE purls: [] size: 48716 timestamp: 1778107988940 - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-2.19.1-cpu_py312h3336143_5.conda sha256: 5c8d7fd25ab2dcbd93615aa2050471082c5280e10b3ed23198979cbc822553fe md5: 50c6712df349db8c7ed5b7ad4fbd202f depends: - python - tensorflow-base ==2.19.1 cpu_py312h9edca52_5 - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: APACHE purls: [] size: 48712 timestamp: 1778107988940 - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-base-2.16.1-cpu_py310h224022f_0.conda sha256: fca1f4fb4e82d34e3974d1f6f5489c521c2b60f36307480ed140002f2d0e3b62 md5: 43e6673f4fcabef68fd60434a49b6b70 depends: - absl-py >=1.0.0 - astunparse >=1.6.0 - flatbuffers >=24.3.25,<24.3.26.0a0 - gast >=0.2.1,!=0.5.0,!=0.5.1,!=0.5.2 - giflib >=5.2.2,<5.3.0a0 - google-pasta >=0.1.1 - grpcio 1.62.* - h5py >=3.10 - icu >=73.2,<74.0a0 - keras >=3.0 - libabseil * cxx17* - libabseil >=20240116.2,<20240117.0a0 - libcurl >=8.8.0,<9.0a0 - libgcc-ng >=12 - libgrpc >=1.62.2,<1.63.0a0 - libjpeg-turbo >=3.0.0,<4.0a0 - libpng >=1.6.43,<1.7.0a0 - libprotobuf >=4.25.3,<4.25.4.0a0 - libsqlite >=3.45.3,<4.0a0 - libstdcxx-ng >=12 - libzlib >=1.2.13,<2.0.0a0 - ml_dtypes >=0.3.1,<0.4 - numpy >=1.22,<2.0a0 - numpy >=1.22.4,<2.0a0 - openssl >=3.3.0,<4.0a0 - opt_einsum >=2.3.2 - packaging - protobuf >=3.20.3,<5,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5 - python >=3.10,<3.11.0a0 - python-flatbuffers >=23.5.26 - python_abi 3.10.* *_cp310 - requests >=2.21.0,<3 - six >=1.12 - snappy >=1.2.0,<1.3.0a0 - tensorboard >=2.16,<2.17 - termcolor >=1.1.0 - typing_extensions >=3.6.6 - wrapt >=1.11.0 track_features: - tensorflow-cpu license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tensorflow?source=hash-mapping size: 154505441 timestamp: 1716522833206 - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-base-2.19.1-cpu_py311hca13484_5.conda sha256: 61a4f4acce44448eade2f5237acadd6599d86d040e4e499727dc8df5a519a5ab md5: d347cce833c2022cfdf35fbf9809a2f3 depends: - python - packaging - absl-py >=1.0.0 - astunparse >=1.6.0 - gast >=0.2.1,!=0.5.0,!=0.5.1,!=0.5.2 - google-pasta >=0.1.1 - grpcio 1.78.* - h5py >=3.11 - ml_dtypes >=0.5.1,<1.0 - opt_einsum >=2.3.2 - protobuf >=5.26 - python-flatbuffers >=24.3.25 - requests >=2.21.0,<3 - six >=1.12 - termcolor >=1.1.0 - typing_extensions >=3.6.6 - wrapt >=1.11.0 - tensorboard >=2.19,<2.20 - keras >=3.5 - libtensorflow_framework ==2.19.1 cpu_h0a5eddc_5 - libtensorflow_cc ==2.19.1 cpu_ha812d39_5 - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - _x86_64-microarch-level >=1 - python_abi 3.11.* *_cp311 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tensorflow?source=hash-mapping size: 325591976 timestamp: 1778107988940 - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-base-2.19.1-cpu_py312h9edca52_5.conda sha256: 91a713f7af98269e1d23324d7f376de54dc33f75f71b7acc2c7ca1ec5e1e275e md5: a412cf6cf13a017db097c88aa89cf94a depends: - python - packaging - absl-py >=1.0.0 - astunparse >=1.6.0 - gast >=0.2.1,!=0.5.0,!=0.5.1,!=0.5.2 - google-pasta >=0.1.1 - grpcio 1.78.* - h5py >=3.11 - ml_dtypes >=0.5.1,<1.0 - opt_einsum >=2.3.2 - protobuf >=5.26 - python-flatbuffers >=24.3.25 - requests >=2.21.0,<3 - six >=1.12 - termcolor >=1.1.0 - typing_extensions >=3.6.6 - wrapt >=1.11.0 - tensorboard >=2.19,<2.20 - keras >=3.5 - libtensorflow_framework ==2.19.1 cpu_h0a5eddc_5 - libtensorflow_cc ==2.19.1 cpu_ha812d39_5 - _x86_64-microarch-level >=1 - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tensorflow?source=hash-mapping size: 325310955 timestamp: 1778107988940 - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-estimator-2.16.1-cpu_py310hc6dcfef_0.conda sha256: b5fc38e8836b400f16d00582c9ff768b4228aeaeacbbeede3757b37c843119d6 md5: 2885387bbefa00f77062b7ddb75e1f5d depends: - libgcc-ng >=12 - libstdcxx-ng >=12 - openssl >=3.3.0,<4.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - tensorflow-base 2.16.1 cpu_py310h224022f_0 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tensorflow-estimator?source=hash-mapping size: 552895 timestamp: 1716523398292 - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda sha256: cafeec44494f842ffeca27e9c8b0c27ed714f93ac77ddadc6aaf726b5554ebac md5: cffd3bdd58090148f4cfcd831f4b26ab depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libzlib >=1.3.1,<2.0a0 constrains: - xorg-libx11 >=1.8.12,<2.0a0 license: TCL license_family: BSD purls: [] size: 3301196 timestamp: 1769460227866 - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.6-py311h49ec1c0_0.conda sha256: a1991a1be748a44282d0351bf73710e7f13bb5e4db48ddf5243a4f5170bbec76 md5: 4b1da0e55da3e79266be96fdd32ef407 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tornado?source=hash-mapping size: 879860 timestamp: 1779915940451 - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.6-py312h4c3975b_0.conda sha256: 1a5f2eee536c5ea97dd9674b1b883d8d676ee346f10c8d4ae30626e20aa6d289 md5: dcbe46475eff6fb9d1adad473c984f39 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tornado?source=compressed-mapping size: 860785 timestamp: 1779915943143 - conda: https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.1.0-py311hdf67eae_0.conda sha256: 31ad759e2d7668f1615021bca0ae2358a284939e3ca3fcb971b3f7aa83c2a6d6 md5: 5a715cf5e3dc6cd68717c50e47dd7b6b depends: - __glibc >=2.17,<3.0.a0 - cffi - libgcc >=14 - libstdcxx >=14 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/ukkonen?source=hash-mapping size: 14898 timestamp: 1769438724694 - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py311h49ec1c0_0.conda sha256: 2bd8ee058dc98e614003591eb221a8b08449768b13aebe76dad8528bf0f5f88b md5: 2889f0c0b6a6d7a37bd64ec60f4cc210 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/unicodedata2?source=hash-mapping size: 409682 timestamp: 1770909108616 - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.1-py312h4c3975b_0.conda sha256: 895bbfe9ee25c98c922799de901387d842d7c01cae45c346879865c6a907f229 md5: 0b6c506ec1f272b685240e70a29261b8 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/unicodedata2?source=hash-mapping size: 410641 timestamp: 1770909099497 - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.25.0-hd6090a7_0.conda sha256: ea374d57a8fcda281a0a89af0ee49a2c2e99cc4ac97cf2e2db7064e74e764bdb md5: 996583ea9c796e5b915f7d7580b51ea6 depends: - __glibc >=2.17,<3.0.a0 - libexpat >=2.7.4,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - libgcc >=14 - libstdcxx >=14 license: MIT license_family: MIT purls: [] size: 334139 timestamp: 1773959575393 - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.2.1-py310h7c4b9e2_0.conda sha256: 50f580af14d83030eceb4a5d439b9c33b05946692693df2fdcdb9bb9fda9ee90 md5: 551230d62e51575f9c2b2c387dfc8f1d depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/wrapt?source=hash-mapping size: 101965 timestamp: 1779477455278 - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.2.1-py311h49ec1c0_0.conda sha256: 4461bb07b3aed91fcfb437891a53a0fa0c2591910bc5da85abce481694effced md5: e6cc7808fc5ac070f75c682ca9d00cb5 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/wrapt?source=compressed-mapping size: 117311 timestamp: 1779477448367 - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.2.1-py312h4c3975b_0.conda sha256: 3d36b297c5f0c1ab0e598760f0033377334a3bfb5c12f4129c2abcb884e2d632 md5: a4d41bb00f252b99b4b903b3a8fa3ecc depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/wrapt?source=hash-mapping size: 114800 timestamp: 1779477451511 - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.1-h4f16b4b_2.conda sha256: ad8cab7e07e2af268449c2ce855cbb51f43f4664936eff679b1f3862e6e4b01d md5: fdc27cb255a7a2cc73b7919a968b48f0 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - libxcb >=1.17.0,<2.0a0 license: MIT license_family: MIT purls: [] size: 20772 timestamp: 1750436796633 - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-cursor-0.1.6-hb03c661_0.conda sha256: c2be9cae786fdb2df7c2387d2db31b285cf90ab3bfabda8fa75a596c3d20fc67 md5: 4d1fc190b99912ed557a8236e958c559 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libxcb >=1.13 - libxcb >=1.17.0,<2.0a0 - xcb-util-image >=0.4.0,<0.5.0a0 - xcb-util-renderutil >=0.3.10,<0.4.0a0 license: MIT license_family: MIT purls: [] size: 20829 timestamp: 1763366954390 - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-hb711507_2.conda sha256: 94b12ff8b30260d9de4fd7a28cca12e028e572cbc504fd42aa2646ec4a5bded7 md5: a0901183f08b6c7107aab109733a3c91 depends: - libgcc-ng >=12 - libxcb >=1.16,<2.0.0a0 - xcb-util >=0.4.1,<0.5.0a0 license: MIT license_family: MIT purls: [] size: 24551 timestamp: 1718880534789 - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.1-hb711507_0.conda sha256: 546e3ee01e95a4c884b6401284bb22da449a2f4daf508d038fdfa0712fe4cc69 md5: ad748ccca349aec3e91743e08b5e2b50 depends: - libgcc-ng >=12 - libxcb >=1.16,<2.0.0a0 license: MIT license_family: MIT purls: [] size: 14314 timestamp: 1718846569232 - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.10-hb711507_0.conda sha256: 2d401dadc43855971ce008344a4b5bd804aca9487d8ebd83328592217daca3df md5: 0e0cbe0564d03a99afd5fd7b362feecd depends: - libgcc-ng >=12 - libxcb >=1.16,<2.0.0a0 license: MIT license_family: MIT purls: [] size: 16978 timestamp: 1718848865819 - conda: https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.2-hb711507_0.conda sha256: 31d44f297ad87a1e6510895740325a635dd204556aa7e079194a0034cdd7e66a md5: 608e0ef8256b81d04456e8d211eee3e8 depends: - libgcc-ng >=12 - libxcb >=1.16,<2.0.0a0 license: MIT license_family: MIT purls: [] size: 51689 timestamp: 1718844051451 - conda: https://conda.anaconda.org/conda-forge/linux-64/xkeyboard-config-2.47-hb03c661_0.conda sha256: 19c2bb14bec84b0e995b56b752369775c75f1589314b43733948bb5f471a6915 md5: b56e0c8432b56decafae7e78c5f29ba5 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - xorg-libx11 >=1.8.13,<2.0a0 license: MIT license_family: MIT purls: [] size: 399291 timestamp: 1772021302485 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.1.2-hb9d3cd8_0.conda sha256: c12396aabb21244c212e488bbdc4abcdef0b7404b15761d9329f5a4a39113c4b md5: fb901ff28063514abb6046c9ec2c4a45 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 license: MIT license_family: MIT purls: [] size: 58628 timestamp: 1734227592886 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.6-he73a12e_0.conda sha256: 277841c43a39f738927145930ff963c5ce4c4dacf66637a3d95d802a64173250 md5: 1c74ff8c35dcadf952a16f752ca5aa49 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - libuuid >=2.38.1,<3.0a0 - xorg-libice >=1.1.2,<2.0a0 license: MIT license_family: MIT purls: [] size: 27590 timestamp: 1741896361728 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.8.13-he1eb515_0.conda sha256: 516d4060139dbb4de49a4dcdc6317a9353fb39ebd47789c14e6fe52de0deee42 md5: 861fb6ccbc677bb9a9fb2468430b9c6a depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libxcb >=1.17.0,<2.0a0 license: MIT license_family: MIT purls: [] size: 839652 timestamp: 1770819209719 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.12-hb03c661_1.conda sha256: 6bc6ab7a90a5d8ac94c7e300cc10beb0500eeba4b99822768ca2f2ef356f731b md5: b2895afaf55bf96a8c8282a2e47a5de0 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: MIT license_family: MIT purls: [] size: 15321 timestamp: 1762976464266 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcomposite-0.4.7-hb03c661_0.conda sha256: 048c103000af9541c919deef03ae7c5e9c570ffb4024b42ecb58dbde402e373a md5: f2ba4192d38b6cef2bb2c25029071d90 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - xorg-libx11 >=1.8.12,<2.0a0 - xorg-libxfixes >=6.0.2,<7.0a0 license: MIT license_family: MIT purls: [] size: 14415 timestamp: 1770044404696 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxcursor-1.2.3-hb9d3cd8_0.conda sha256: 832f538ade441b1eee863c8c91af9e69b356cd3e9e1350fff4fe36cc573fc91a md5: 2ccd714aa2242315acaf0a67faea780b depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - xorg-libx11 >=1.8.10,<2.0a0 - xorg-libxfixes >=6.0.1,<7.0a0 - xorg-libxrender >=0.9.11,<0.10.0a0 license: MIT license_family: MIT purls: [] size: 32533 timestamp: 1730908305254 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdamage-1.1.6-hb9d3cd8_0.conda sha256: 43b9772fd6582bf401846642c4635c47a9b0e36ca08116b3ec3df36ab96e0ec0 md5: b5fcc7172d22516e1f965490e65e33a4 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - xorg-libx11 >=1.8.10,<2.0a0 - xorg-libxext >=1.3.6,<2.0a0 - xorg-libxfixes >=6.0.1,<7.0a0 license: MIT license_family: MIT purls: [] size: 13217 timestamp: 1727891438799 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.5-hb03c661_1.conda sha256: 25d255fb2eef929d21ff660a0c687d38a6d2ccfbcbf0cc6aa738b12af6e9d142 md5: 1dafce8548e38671bea82e3f5c6ce22f depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: MIT license_family: MIT purls: [] size: 20591 timestamp: 1762976546182 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.7-hb03c661_0.conda sha256: 79c60fc6acfd3d713d6340d3b4e296836a0f8c51602327b32794625826bd052f md5: 34e54f03dfea3e7a2dcf1453a85f1085 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - xorg-libx11 >=1.8.12,<2.0a0 license: MIT license_family: MIT purls: [] size: 50326 timestamp: 1769445253162 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxfixes-6.0.2-hb03c661_0.conda sha256: 83c4c99d60b8784a611351220452a0a85b080668188dce5dfa394b723d7b64f4 md5: ba231da7fccf9ea1e768caf5c7099b84 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - xorg-libx11 >=1.8.12,<2.0a0 license: MIT license_family: MIT purls: [] size: 20071 timestamp: 1759282564045 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxi-1.8.3-hb03c661_0.conda sha256: 495f99c8eacfa4ae2d8fed2a7f2105777af89acdc204df145d2bbbc380ac631b md5: adba2e334082bb218db806d4c12277c9 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - xorg-libx11 >=1.8.13,<2.0a0 - xorg-libxext >=1.3.7,<2.0a0 - xorg-libxfixes >=6.0.2,<7.0a0 license: MIT license_family: MIT purls: [] size: 47717 timestamp: 1779111857071 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.5-hb03c661_0.conda sha256: 80ed047a5cb30632c3dc5804c7716131d767089f65877813d4ae855ee5c9d343 md5: e192019153591938acf7322b6459d36e depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - xorg-libx11 >=1.8.12,<2.0a0 - xorg-libxext >=1.3.6,<2.0a0 - xorg-libxrender >=0.9.12,<0.10.0a0 license: MIT license_family: MIT purls: [] size: 30456 timestamp: 1769445263457 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.12-hb9d3cd8_0.conda sha256: 044c7b3153c224c6cedd4484dd91b389d2d7fd9c776ad0f4a34f099b3389f4a1 md5: 96d57aba173e878a2089d5638016dc5e depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - xorg-libx11 >=1.8.10,<2.0a0 license: MIT license_family: MIT purls: [] size: 33005 timestamp: 1734229037766 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxtst-1.2.5-hb9d3cd8_3.conda sha256: 752fdaac5d58ed863bbf685bb6f98092fe1a488ea8ebb7ed7b606ccfce08637a md5: 7bbe9a0cc0df0ac5f5a8ad6d6a11af2f depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - xorg-libx11 >=1.8.10,<2.0a0 - xorg-libxext >=1.3.6,<2.0a0 - xorg-libxi >=1.7.10,<2.0a0 license: MIT license_family: MIT purls: [] size: 32808 timestamp: 1727964811275 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxxf86vm-1.1.7-hb03c661_0.conda sha256: 64db17baaf36fa03ed8fae105e2e671a7383e22df4077486646f7dbf12842c9f md5: 665d152b9c6e78da404086088077c844 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - xorg-libx11 >=1.8.12,<2.0a0 - xorg-libxext >=1.3.6,<2.0a0 license: MIT license_family: MIT purls: [] size: 18701 timestamp: 1769434732453 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-xorgproto-2025.1-hb03c661_0.conda sha256: 7a8c64938428c2bfd016359f9cb3c44f94acc256c6167dbdade9f2a1f5ca7a36 md5: aa8d21be4b461ce612d8f5fb791decae depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: MIT license_family: MIT purls: [] size: 570010 timestamp: 1766154256151 - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda sha256: 6d9ea2f731e284e9316d95fa61869fe7bbba33df7929f82693c121022810f4ad md5: a77f85f77be52ff59391544bfe73390a depends: - libgcc >=14 - __glibc >=2.17,<3.0.a0 license: MIT license_family: MIT purls: [] size: 85189 timestamp: 1753484064210 - conda: https://conda.anaconda.org/conda-forge/linux-64/zeromq-4.3.5-h09e67af_11.conda sha256: dc9f28dedcb5f35a127fad2d847674d2833369dd616d294e423b8997df31d8a8 md5: 96b08867e21d4694fa5c2c226e6581b0 depends: - libgcc >=14 - __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - krb5 >=1.22.2,<1.23.0a0 - libsodium >=1.0.22,<1.0.23.0a0 license: MPL-2.0 license_family: MOZILLA purls: [] size: 311184 timestamp: 1779123989774 - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.3-hceb46e0_1.conda sha256: ea4e50c465d70236408cb0bfe0115609fd14db1adcd8bd30d8918e0291f8a75f md5: 2aadb0d17215603a82a2a6b0afd9a4cb depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 license: Zlib license_family: Other purls: [] size: 122618 timestamp: 1770167931827 - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda sha256: 68f0206ca6e98fea941e5717cec780ed2873ffabc0e1ed34428c061e2c6268c7 md5: 4a13eeac0b5c8e5b8ab496e6c4ddd829 depends: - __glibc >=2.17,<3.0.a0 - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 601375 timestamp: 1764777111296 - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda sha256: a3967b937b9abf0f2a99f3173fa4630293979bd1644709d89580e7c62a544661 md5: aaa2a381ccc56eac91d63b6c1240312f depends: - cpython - python-gil license: MIT license_family: MIT purls: [] size: 8191 timestamp: 1744137672556 - conda: https://conda.anaconda.org/conda-forge/noarch/_x86_64-microarch-level-1-3_x86_64.conda build_number: 3 sha256: 5f9029eaa78eb13a5499b7a2b012a47a18136b2d41bad99bb7b1796d1fc2b179 md5: 225cb2e9b9512730a92f83696b8fbab8 depends: - __archspec 1.* x86_64 license: BSD-3-Clause license_family: BSD purls: [] size: 9818 timestamp: 1764034326319 - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.4.0-pyhd8ed1ab_0.conda sha256: 0e5e34179a52e0f3aa3c92904bd326de1d1cd74c6fe3bd98f8b8b6889491c7e4 md5: a46362fa67f5138d526715107be0ee32 depends: - python >=3.10 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/absl-py?source=hash-mapping size: 109955 timestamp: 1769637168641 - conda: https://conda.anaconda.org/conda-forge/noarch/accessible-pygments-0.0.5-pyhd8ed1ab_1.conda sha256: 1307719f0d8ee694fc923579a39c0621c23fdaa14ccdf9278a5aac5665ac58e9 md5: 74ac5069774cdbc53910ec4d631a3999 depends: - pygments - python >=3.9 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/accessible-pygments?source=hash-mapping size: 1326096 timestamp: 1734956217254 - conda: https://conda.anaconda.org/conda-forge/noarch/alabaster-1.0.0-pyhd8ed1ab_1.conda sha256: 6c4456a138919dae9edd3ac1a74b6fbe5fd66c05675f54df2f8ab8c8d0cc6cea md5: 1fd9696649f65fd6611fcdb4ffec738a depends: - python >=3.10 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/alabaster?source=hash-mapping size: 18684 timestamp: 1733750512696 - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.13.0-pyhcf101f3_0.conda sha256: f09aed24661cd45ba54a43772504f05c0698248734f9ae8cd289d314ac89707e md5: af2df4b9108808da3dc76710fe50eae2 depends: - exceptiongroup >=1.0.2 - idna >=2.8 - python >=3.10 - typing_extensions >=4.5 - python constrains: - trio >=0.32.0 - uvloop >=0.22.1 - winloop >=0.2.3 license: MIT license_family: MIT purls: - pkg:pypi/anyio?source=hash-mapping size: 146764 timestamp: 1774359453364 - conda: https://conda.anaconda.org/conda-forge/noarch/appnope-0.1.4-pyhd8ed1ab_1.conda sha256: 8f032b140ea4159806e4969a68b4a3c0a7cab1ad936eb958a2b5ffe5335e19bf md5: 54898d0f524c9dee622d44bbb081a8ab depends: - python >=3.9 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/appnope?source=hash-mapping size: 10076 timestamp: 1733332433806 - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda sha256: bea62005badcb98b1ae1796ec5d70ea0fc9539e7d59708ac4e7d41e2f4bb0bad md5: 8ac12aff0860280ee0cff7fa2cf63f3b depends: - argon2-cffi-bindings - python >=3.9 - typing-extensions constrains: - argon2_cffi ==999 license: MIT license_family: MIT purls: - pkg:pypi/argon2-cffi?source=hash-mapping size: 18715 timestamp: 1749017288144 - conda: https://conda.anaconda.org/conda-forge/noarch/arrow-1.4.0-pyhcf101f3_0.conda sha256: 792da8131b1b53ff667bd6fc617ea9087b570305ccb9913deb36b8e12b3b5141 md5: 85c4f19f377424eafc4ed7911b291642 depends: - python >=3.10 - python-dateutil >=2.7.0 - python-tzdata - python license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/arrow?source=hash-mapping size: 113854 timestamp: 1760831179410 - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.1-pyhd8ed1ab_0.conda sha256: ee4da0f3fe9d59439798ee399ef3e482791e48784873d546e706d0935f9ff010 md5: 9673a61a297b00016442e022d689faa6 depends: - python >=3.10 constrains: - astroid >=2,<5 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/asttokens?source=hash-mapping size: 28797 timestamp: 1763410017955 - conda: https://conda.anaconda.org/conda-forge/noarch/astunparse-1.6.3-pyhd8ed1ab_3.conda sha256: 7304f265f146235c34e24db310a94648aa306ca0b2a4a12042bf96da1881f99c md5: d3f195dfdbbf736e4ec178bbec2a975c depends: - python >=3.9 - six >=1.6.1,<2.0 license: BSD-3-Clause AND PSF-2.0 purls: - pkg:pypi/astunparse?source=hash-mapping size: 18143 timestamp: 1736248194225 - conda: https://conda.anaconda.org/conda-forge/noarch/async-lru-2.3.0-pyhcf101f3_0.conda sha256: ea8486637cfb89dc26dc9559921640cd1d5fd37e5e02c33d85c94572139f2efe md5: b85e84cb64c762569cc1a760c2327e0a depends: - python >=3.10 - typing_extensions >=4.0.0 - python license: MIT license_family: MIT purls: - pkg:pypi/async-lru?source=hash-mapping size: 22949 timestamp: 1773926359134 - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda sha256: 1b6124230bb4e571b1b9401537ecff575b7b109cc3a21ee019f65e083b8399ab md5: c6b0543676ecb1fb2d7643941fe375f2 depends: - python >=3.10 - python license: MIT license_family: MIT purls: - pkg:pypi/attrs?source=hash-mapping size: 64927 timestamp: 1773935801332 - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.18.0-pyhcf101f3_1.conda sha256: a14a9ad02101aab25570543a59c5193043b73dc311a25650134ed9e6cb691770 md5: f1976ce927373500cc19d3c0b2c85177 depends: - python >=3.10 - python constrains: - pytz >=2015.7 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/babel?source=hash-mapping size: 7684321 timestamp: 1772555330347 - conda: https://conda.anaconda.org/conda-forge/noarch/backports-1.0-pyhd8ed1ab_5.conda sha256: e1c3dc8b5aa6e12145423fed262b4754d70fec601339896b9ccf483178f690a6 md5: 767d508c1a67e02ae8f50e44cacfadb2 depends: - python >=3.9 license: BSD-3-Clause license_family: BSD purls: [] size: 7069 timestamp: 1733218168786 - conda: https://conda.anaconda.org/conda-forge/noarch/backports.tarfile-1.2.0-pyhcf101f3_2.conda sha256: 25abdb37e186f0d6ac3b774a63c81c5bc4bf554b5096b51343fa5e7c381193b1 md5: bea46844deb274b2cc2a3a941745fa73 depends: - python >=3.10 - backports - python license: MIT license_family: MIT purls: - pkg:pypi/backports-tarfile?source=hash-mapping size: 35739 timestamp: 1767290467820 - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.15.0-pyha770c72_0.conda sha256: aed4b9dcf68ec2a75e5645fed14d77fd884d38d2e52bfa6ef4b278d90cd88781 md5: 3b261da3fe9b4168738712832410b022 depends: - python >=3.10 - soupsieve >=1.2 - typing-extensions license: MIT license_family: MIT purls: - pkg:pypi/beautifulsoup4?source=compressed-mapping size: 92704 timestamp: 1780853175566 - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-6.4.0-pyhcf101f3_0.conda sha256: 0c786f3e571bd58ac73d730d06314716663884d848ae320de0b438fae5e0bea9 md5: 93009c29cdd6f2500468f2502fff9209 depends: - python >=3.10 - webencodings - python constrains: - tinycss2 >=1.1.0,<1.5 license: Apache-2.0 AND MIT purls: - pkg:pypi/bleach?source=compressed-mapping size: 142246 timestamp: 1780675823953 - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.4.0-hac0b51c_0.conda sha256: ede77e412304cd080e23967352a7904932207d0167ecdccd6a9e210530942be6 md5: 5f710eab1f3c4e773c75686f5e8e6481 depends: - bleach ==6.4.0 pyhcf101f3_0 - tinycss2 license: Apache-2.0 AND MIT purls: [] size: 4406 timestamp: 1780675823953 - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-h4c7d964_0.conda sha256: 86981d764e4ea1883409d30447ff9da46127426d31a63df08315aaded768e652 md5: c9b86eece2f944541b86441c94117ab3 depends: - __win license: ISC purls: [] size: 130182 timestamp: 1779289939595 - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.5.20-hbd8a1cb_0.conda sha256: 9812a303a1395e1dafbd92e5bc8a1ff6013bcbba0a09c7f03a8d23e43560aa9b md5: 489b8e97e666c93f68fdb35c3c9b957f depends: - __unix license: ISC purls: [] size: 129868 timestamp: 1779289852439 - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 noarch: python sha256: 561e6660f26c35d137ee150187d89767c988413c978e1b712d53f27ddf70ea17 md5: 9b347a7ec10940d3f7941ff6c460b551 depends: - cached_property >=1.5.2,<1.5.3.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 4134 timestamp: 1615209571450 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 sha256: 6dbf7a5070cc43d90a1e4c2ec0c541c69d8e30a0e25f50ce9f6e4a432e42c5d7 md5: 576d629e47797577ab0f1b351297ef4a depends: - python >=3.6 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/cached-property?source=hash-mapping size: 11065 timestamp: 1615209567874 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.5.20-pyhd8ed1ab_0.conda sha256: 645655a3510e38e625da136595f3f16f2130c3263630cc3bc8f60f619ddbe490 md5: 9fefff2f745ea1cc2ef15211a20c054a depends: - python >=3.10 license: ISC purls: - pkg:pypi/certifi?source=compressed-mapping size: 134201 timestamp: 1779285131141 - conda: https://conda.anaconda.org/conda-forge/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda sha256: aa589352e61bb221351a79e5946d56916e3c595783994884accdb3b97fe9d449 md5: 381bd45fb7aa032691f3063aff47e3a1 depends: - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/cfgv?source=hash-mapping size: 13589 timestamp: 1763607964133 - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda sha256: 3f9483d62ce24ecd063f8a5a714448445dc8d9e201147c46699fc0033e824457 md5: a9167b9571f3baa9d448faa2139d1089 depends: - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/charset-normalizer?source=hash-mapping size: 58872 timestamp: 1775127203018 - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyh6dadd2b_0.conda sha256: 5b8e8d8876ace41735f51ca43c43cdc9e1b4fbbae0f415d6b8441fec826d8c47 md5: f73f35eedcd8e89d6c4407df15101233 depends: - __win - colorama - python >=3.10 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/click?source=compressed-mapping size: 104080 timestamp: 1779900586237 - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.4.1-pyhc90fa1f_0.conda sha256: c253a41cdf898b651a0786cbb76c6d5fc101d0dbbe719f93a124bc4fde5cdd6a md5: 554304a07e581a85891b15e39ea9f268 depends: - __unix - python - python >=3.10 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/click?source=compressed-mapping size: 104999 timestamp: 1779900548735 - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 md5: 962b9857ee8e7018c22f2776ffa0b2d7 depends: - python >=3.9 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/colorama?source=hash-mapping size: 27011 timestamp: 1733218222191 - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda sha256: 576a44729314ad9e4e5ebe055fbf48beb8116b60e58f9070278985b2b634f212 md5: 2da13f2b299d8e1995bafbbe9689a2f7 depends: - python >=3.9 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/comm?source=hash-mapping size: 14690 timestamp: 1753453984907 - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.11.15-py311hd8ed1ab_0.conda noarch: generic sha256: 4811072ccc369c80cb94156fb96fa234db1b90120c0859dc173bea42d49a57b5 md5: bf0d09d5c2b61aaf7b11c551c8545cfa depends: - python >=3.11,<3.12.0a0 - python_abi * *_cp311 license: Python-2.0 purls: [] size: 48036 timestamp: 1772730257686 - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda sha256: bb47aec5338695ff8efbddbc669064a3b10fe34ad881fb8ad5d64fbfa6910ed1 md5: 4c2a8fef270f6c69591889b93f9f55c1 depends: - python >=3.10 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/cycler?source=hash-mapping size: 14778 timestamp: 1764466758386 - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.3.1-pyhd8ed1ab_0.conda sha256: 430bd9d731b265f0bedb3183ac3ecfaa1656390c092b6e864ff8cc1229843c8c md5: 61dcf784d59ef0bd62c57d982b154ace depends: - python >=3.10 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/decorator?source=compressed-mapping size: 16102 timestamp: 1779115228886 - conda: https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2 sha256: 9717a059677553562a8f38ff07f3b9f61727bd614f505658b0a5ecbcf8df89be md5: 961b3a227b437d82ad7054484cfa71b2 depends: - python >=3.6 license: PSF-2.0 license_family: PSF purls: - pkg:pypi/defusedxml?source=hash-mapping size: 24062 timestamp: 1615232388757 - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.1-pyhcf101f3_1.conda sha256: c691221f821fad3a0fcdedb7b18605edb7117cdaa50b76885619c6a0f66ed761 md5: f8638ae693ee89e9069ed4f3b77a6be9 depends: - python >=3.10 - python license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/distlib?source=compressed-mapping size: 302890 timestamp: 1780422960389 - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_1.conda sha256: fa5966bb1718bbf6967a85075e30e4547901410cc7cb7b16daf68942e9a94823 md5: 24c1ca34138ee57de72a943237cde4cc depends: - python >=3.9 license: CC-PDDC AND BSD-3-Clause AND BSD-2-Clause AND ZPL-2.1 purls: - pkg:pypi/docutils?source=hash-mapping size: 402700 timestamp: 1733217860944 - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda sha256: ee6cf346d017d954255bbcbdb424cddea4d14e4ed7e9813e429db1d795d01144 md5: 8e662bd460bda79b1ea39194e3c4c9ab depends: - python >=3.10 - typing_extensions >=4.6.0 license: MIT and PSF-2.0 purls: - pkg:pypi/exceptiongroup?source=hash-mapping size: 21333 timestamp: 1763918099466 - conda: https://conda.anaconda.org/conda-forge/noarch/execnet-2.1.2-pyhd8ed1ab_0.conda sha256: 1acc6a420efc5b64c384c1f35f49129966f8a12c93b4bb2bdc30079e5dc9d8a8 md5: a57b4be42619213a94f31d2c69c5dda7 depends: - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/execnet?source=hash-mapping size: 39499 timestamp: 1762974150770 - conda: https://conda.anaconda.org/conda-forge/noarch/executing-2.2.1-pyhd8ed1ab_0.conda sha256: 210c8165a58fdbf16e626aac93cc4c14dbd551a01d1516be5ecad795d2422cad md5: ff9efb7f7469aed3c4a8106ffa29593c depends: - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/executing?source=hash-mapping size: 30753 timestamp: 1756729456476 - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.29.1-pyhd8ed1ab_0.conda sha256: ab7c43874d451c36a11b1516a3fbdf23803c05fcb01063a3877549dfb3e34ec4 md5: 917880ebad7632e8a52eada085b98ce9 depends: - python >=3.10 license: Unlicense purls: - pkg:pypi/filelock?source=compressed-mapping size: 34899 timestamp: 1780521975987 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2 sha256: 58d7f40d2940dd0a8aa28651239adbf5613254df0f75789919c4e6762054403b md5: 0c96522c6bdaed4b1566d11387caaf45 license: BSD-3-Clause license_family: BSD purls: [] size: 397370 timestamp: 1566932522327 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2 sha256: c52a29fdac682c20d252facc50f01e7c2e7ceac52aa9817aaf0bb83f7559ec5c md5: 34893075a5c9e55cdafac56607368fc6 license: OFL-1.1 license_family: Other purls: [] size: 96530 timestamp: 1620479909603 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2 sha256: 00925c8c055a2275614b4d983e1df637245e19058d79fc7dd1a93b8d9fb4b139 md5: 4d59c254e01d9cde7957100457e2d5fb license: OFL-1.1 license_family: Other purls: [] size: 700814 timestamp: 1620479612257 - conda: https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-h77eed37_3.conda sha256: 2821ec1dc454bd8b9a31d0ed22a7ce22422c0aef163c59f49dfdf915d0f0ca14 md5: 49023d73832ef61042f6a237cb2687e7 license: LicenseRef-Ubuntu-Font-Licence-Version-1.0 license_family: Other purls: [] size: 1620504 timestamp: 1727511233259 - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2 sha256: a997f2f1921bb9c9d76e6fa2f6b408b7fa549edd349a77639c9fe7a23ea93e61 md5: fee5683a3f04bd15cbd8318b096a27ab depends: - fonts-conda-forge license: BSD-3-Clause license_family: BSD purls: [] size: 3667 timestamp: 1566974674465 - conda: https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-hc364b38_1.conda sha256: 54eea8469786bc2291cc40bca5f46438d3e062a399e8f53f013b6a9f50e98333 md5: a7970cd949a077b7cb9696379d338681 depends: - font-ttf-ubuntu - font-ttf-inconsolata - font-ttf-dejavu-sans-mono - font-ttf-source-code-pro license: BSD-3-Clause license_family: BSD purls: [] size: 4059 timestamp: 1762351264405 - conda: https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda sha256: 2509992ec2fd38ab27c7cdb42cf6cadc566a1cc0d1021a2673475d9fa87c6276 md5: d3549fd50d450b6d9e7dddff25dd2110 depends: - cached-property >=1.3.0 - python >=3.9,<4 license: MPL-2.0 license_family: MOZILLA purls: - pkg:pypi/fqdn?source=hash-mapping size: 16705 timestamp: 1733327494780 - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_0.conda sha256: 587a0e2606026716760af58e5d7c15ea02a848bd076a324946798be29d0229db md5: 377a825d91b5d6fcc0e6cdb98bbe9799 depends: - python >=3.10 constrains: - pythran >=0.12.2 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/gast?source=hash-mapping size: 27047 timestamp: 1764507196904 - conda: https://conda.anaconda.org/conda-forge/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda sha256: 9f668fe562a9cf71a5d1f348645ac041af3f2e4bc634b18d6374e838e1c55dd8 md5: 005b9749218cb8c9e94ac2a77ca3c8c0 depends: - python >=3.9 - six license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/google-pasta?source=hash-mapping size: 49210 timestamp: 1733852592869 - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhcf101f3_1.conda sha256: 96cac6573fd35ae151f4d6979bab6fbc90cb6b1fb99054ba19eb075da9822fcb md5: b8993c19b0c32a2f7b66cbb58ca27069 depends: - python >=3.10 - typing_extensions - python license: MIT license_family: MIT purls: - pkg:pypi/h11?source=hash-mapping size: 39069 timestamp: 1767729720872 - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda sha256: 84c64443368f84b600bfecc529a1194a3b14c3656ee2e832d15a20e0329b6da3 md5: 164fc43f0b53b6e3a7bc7dce5e4f1dc9 depends: - python >=3.10 - hyperframe >=6.1,<7 - hpack >=4.1,<5 - python license: MIT license_family: MIT purls: - pkg:pypi/h2?source=hash-mapping size: 95967 timestamp: 1756364871835 - conda: https://conda.anaconda.org/conda-forge/noarch/hpack-4.1.0-pyhd8ed1ab_0.conda sha256: 6ad78a180576c706aabeb5b4c8ceb97c0cb25f1e112d76495bff23e3779948ba md5: 0a802cb9888dd14eeefc611f05c40b6e depends: - python >=3.9 license: MIT license_family: MIT purls: - pkg:pypi/hpack?source=hash-mapping size: 30731 timestamp: 1737618390337 - conda: https://conda.anaconda.org/conda-forge/noarch/httpcore-1.0.9-pyh29332c3_0.conda sha256: 04d49cb3c42714ce533a8553986e1642d0549a05dc5cc48e0d43ff5be6679a5b md5: 4f14640d58e2cc0aa0819d9d8ba125bb depends: - python >=3.9 - h11 >=0.16 - h2 >=3,<5 - sniffio 1.* - anyio >=4.0,<5.0 - certifi - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/httpcore?source=hash-mapping size: 49483 timestamp: 1745602916758 - conda: https://conda.anaconda.org/conda-forge/noarch/httpx-0.28.1-pyhd8ed1ab_0.conda sha256: cd0f1de3697b252df95f98383e9edb1d00386bfdd03fdf607fa42fe5fcb09950 md5: d6989ead454181f4f9bc987d3dc4e285 depends: - anyio - certifi - httpcore 1.* - idna - python >=3.9 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/httpx?source=hash-mapping size: 63082 timestamp: 1733663449209 - conda: https://conda.anaconda.org/conda-forge/noarch/hyperframe-6.1.0-pyhd8ed1ab_0.conda sha256: 77af6f5fe8b62ca07d09ac60127a30d9069fdc3c68d6b256754d0ffb1f7779f8 md5: 8e6923fc12f1fe8f8c4e5c9f343256ac depends: - python >=3.9 license: MIT license_family: MIT purls: - pkg:pypi/hyperframe?source=hash-mapping size: 17397 timestamp: 1737618427549 - conda: https://conda.anaconda.org/conda-forge/noarch/id-1.6.1-pyhcf101f3_0.conda sha256: 54c80a4ca6e6a19b4bb89c829f757d0de00362a3bfa4647517d2ebd519717f0f md5: 563a022fc58cf7a200c35cb3fee07a6b depends: - python >=3.10 - urllib3 >=2,<3 - python license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/id?source=hash-mapping size: 27972 timestamp: 1770237711404 - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.19-pyhd8ed1ab_0.conda sha256: 381cedccf0866babfc135d65ee40b778bd20e927d2a5ec810f750c5860a7c5b8 md5: 84a3233b709a289a4ddd7a2fd27dd988 depends: - python >=3.10 - ukkonen license: MIT license_family: MIT purls: - pkg:pypi/identify?source=hash-mapping size: 79757 timestamp: 1776455344188 - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.17-pyhcf101f3_0.conda sha256: f9fe1f9e539c544405ccb7ba632d4ba79edf243c05554d76ace073158a80b691 md5: c75e517ebd7a5c5272fe111e8b162228 depends: - python >=3.10 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/idna?source=compressed-mapping size: 56858 timestamp: 1779999227630 - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-2.0.0-pyhd8ed1ab_0.conda sha256: 5a047f9eac290e679b4e6f6f4cbfcc5acdfbf031a4f06824d4ddb590cdbb850b md5: 92617c2ba2847cca7a6ed813b6f4ab79 depends: - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/imagesize?source=hash-mapping size: 15729 timestamp: 1773752188889 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-9.0.0-pyhcf101f3_0.conda sha256: 43e2a5497cad1598ff88a3e69f69bc88b7b8f141fa63c60eab5db296317318b8 md5: ffc17e785d64e12fc311af9184221839 depends: - python >=3.10 - zipp >=3.20 - python license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/importlib-metadata?source=compressed-mapping size: 34766 timestamp: 1779714582554 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-7.1.0-pyhd8ed1ab_0.conda sha256: a563a51aa522998172838e867e6dedcf630bc45796e8612f5a1f6d73e9c8125a md5: 0ba6225c279baf7ea9473a62ea0ec9ae depends: - python >=3.10 - zipp >=3.1.0 constrains: - importlib-resources >=7.1.0,<7.1.1.0a0 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/importlib-resources?source=hash-mapping size: 34809 timestamp: 1776068839274 - conda: https://conda.anaconda.org/conda-forge/noarch/iniconfig-2.3.0-pyhd8ed1ab_0.conda sha256: e1a9e3b1c8fe62dc3932a616c284b5d8cbe3124bbfbedcf4ce5c828cb166ee19 md5: 9614359868482abba1bd15ce465e3c42 depends: - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/iniconfig?source=hash-mapping size: 13387 timestamp: 1760831448842 - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.2.0-pyh5552912_1.conda sha256: 5c1f3e874adaf603449f2b135d48f168c5d510088c78c229bda0431268b43b27 md5: 4b53d436f3fbc02ce3eeaf8ae9bebe01 depends: - appnope - __osx - comm >=0.1.1 - debugpy >=1.6.5 - ipython >=7.23.1 - jupyter_client >=8.8.0 - jupyter_core >=5.1,!=6.0.* - matplotlib-inline >=0.1 - nest-asyncio >=1.4 - packaging >=22 - psutil >=5.7 - python >=3.10 - pyzmq >=25 - tornado >=6.4.1 - traitlets >=5.4.0 - python constrains: - appnope >=0.1.2 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/ipykernel?source=hash-mapping size: 132260 timestamp: 1770566135697 - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.2.0-pyha191276_1.conda sha256: b77ed58eb235e5ad80e742b03caeed4bbc2a2ef064cb9a2deee3b75dfae91b2a md5: 8b267f517b81c13594ed68d646fd5dcb depends: - __linux - comm >=0.1.1 - debugpy >=1.6.5 - ipython >=7.23.1 - jupyter_client >=8.8.0 - jupyter_core >=5.1,!=6.0.* - matplotlib-inline >=0.1 - nest-asyncio >=1.4 - packaging >=22 - psutil >=5.7 - python >=3.10 - pyzmq >=25 - tornado >=6.4.1 - traitlets >=5.4.0 - python constrains: - appnope >=0.1.2 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/ipykernel?source=hash-mapping size: 133644 timestamp: 1770566133040 - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.14.1-pyh53cf698_0.conda sha256: a3f76e06c31bcf1bda0f633d5c9f1c834286b4f6decc6626067a6cffee283318 md5: fbd58549b374103c1a80577f09a328ef depends: - __unix - decorator >=5.1.0 - ipython_pygments_lexers >=1.0.0 - jedi >=0.18.2 - matplotlib-inline >=0.1.6 - prompt-toolkit >=3.0.41,<3.1.0 - psutil >=7 - pygments >=2.14.0 - python >=3.11 - stack_data >=0.6.0 - traitlets >=5.13.0 - typing_extensions >=4.6 - pexpect >4.6 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/ipython?source=compressed-mapping size: 652893 timestamp: 1780654403616 - conda: https://conda.anaconda.org/conda-forge/noarch/ipython_pygments_lexers-1.1.1-pyhd8ed1ab_0.conda sha256: 894682a42a7d659ae12878dbcb274516a7031bbea9104e92f8e88c1f2765a104 md5: bd80ba060603cc228d9d81c257093119 depends: - pygments - python >=3.9 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/ipython-pygments-lexers?source=hash-mapping size: 13993 timestamp: 1737123723464 - conda: https://conda.anaconda.org/conda-forge/noarch/isoduration-20.11.0-pyhd8ed1ab_1.conda sha256: 08e838d29c134a7684bca0468401d26840f41c92267c4126d7b43a6b533b0aed md5: 0b0154421989637d424ccf0f104be51a depends: - arrow >=0.15.0 - python >=3.9 license: MIT license_family: MIT purls: - pkg:pypi/isoduration?source=hash-mapping size: 19832 timestamp: 1733493720346 - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.classes-3.4.0-pyhcf101f3_3.conda sha256: 3cc991f0f09dfd00d2626e745ba68da03e4f1dcbb7b36dd20f7a7373643cd5d5 md5: d59568bad316413c89831456e691de29 depends: - python >=3.10 - more-itertools - python license: MIT license_family: MIT purls: - pkg:pypi/jaraco-classes?source=hash-mapping size: 14831 timestamp: 1767294269456 - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.context-6.1.2-pyhcf101f3_0.conda sha256: 108b3919db8ef81b5585b38a3fedbe9eeccdf8fa576c250a13559a1188f597cc md5: b9d2b1ffa307961f6bb7d83c8ba8a8be depends: - python >=3.10 - backports.tarfile - python license: MIT license_family: MIT purls: - pkg:pypi/jaraco-context?source=hash-mapping size: 16222 timestamp: 1776862415793 - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.functools-4.5.0-pyhcf101f3_0.conda sha256: 8bc317fe1e93dec7350b460f7b4e82ea9c3d157c278219bfc6a95b7a51007fdd md5: 8e33f6f90e5de5efd971840c7634d954 depends: - python >=3.10 - more-itertools - python license: MIT license_family: MIT purls: - pkg:pypi/jaraco-functools?source=hash-mapping size: 18461 timestamp: 1778889146059 - conda: https://conda.anaconda.org/conda-forge/noarch/jedi-0.19.2-pyhd8ed1ab_1.conda sha256: 92c4d217e2dc68983f724aa983cca5464dcb929c566627b26a2511159667dba8 md5: a4f4c5dc9b80bc50e0d3dc4e6e8f1bd9 depends: - parso >=0.8.3,<0.9.0 - python >=3.9 license: Apache-2.0 AND MIT purls: - pkg:pypi/jedi?source=hash-mapping size: 843646 timestamp: 1733300981994 - conda: https://conda.anaconda.org/conda-forge/noarch/jeepney-0.9.0-pyhd8ed1ab_0.conda sha256: 00d37d85ca856431c67c8f6e890251e7cc9e5ef3724a0302b8d4a101f22aa27f md5: b4b91eb14fbe2f850dd2c5fc20676c0d depends: - python >=3.9 license: MIT license_family: MIT purls: - pkg:pypi/jeepney?source=hash-mapping size: 40015 timestamp: 1740828380668 - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda sha256: fc9ca7348a4f25fed2079f2153ecdcf5f9cf2a0bc36c4172420ca09e1849df7b md5: 04558c96691bed63104678757beb4f8d depends: - markupsafe >=2.0 - python >=3.10 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/jinja2?source=hash-mapping size: 120685 timestamp: 1764517220861 - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.2.0-pyhd8ed1ab_0.tar.bz2 sha256: 0c21351871df2c0a53168575597dd9c881e2a9fa4c42fe89a9bcd7fab37f462c md5: 7583652522d71ad78ba536bba06940eb depends: - python >=3.6 - setuptools license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/joblib?source=hash-mapping size: 210022 timestamp: 1663332186018 - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda sha256: 301539229d7be6420c084490b8145583291123f0ce6b92f56be5948a2c83a379 md5: 615de2a4d97af50c350e5cf160149e77 depends: - python >=3.10 - setuptools license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/joblib?source=hash-mapping size: 226448 timestamp: 1765794135253 - conda: https://conda.anaconda.org/conda-forge/noarch/json5-0.14.0-pyhd8ed1ab_0.conda sha256: 9daa95bd164c8fa23b3ab196e906ef806141d749eddce2a08baa064f722d25fa md5: 1269891272187518a0a75c286f7d0bbf depends: - python >=3.10 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/json5?source=hash-mapping size: 34731 timestamp: 1774655440045 - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.1.1-pyhcf101f3_0.conda sha256: a3d10301b6ff399ba1f3d39e443664804a3d28315a4fb81e745b6817845f70ae md5: 89bf346df77603055d3c8fe5811691e6 depends: - python >=3.10 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/jsonpointer?source=hash-mapping size: 14190 timestamp: 1774311356147 - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.26.0-pyhcf101f3_0.conda sha256: db973a37d75db8e19b5f44bbbdaead0c68dde745407f281e2a7fe4db74ec51d7 md5: ada41c863af263cc4c5fcbaff7c3e4dc depends: - attrs >=22.2.0 - jsonschema-specifications >=2023.3.6 - python >=3.10 - referencing >=0.28.4 - rpds-py >=0.25.0 - python license: MIT license_family: MIT purls: - pkg:pypi/jsonschema?source=hash-mapping size: 82356 timestamp: 1767839954256 - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-specifications-2025.9.1-pyhcf101f3_0.conda sha256: 0a4f3b132f0faca10c89fdf3b60e15abb62ded6fa80aebfc007d05965192aa04 md5: 439cd0f567d697b20a8f45cb70a1005a depends: - python >=3.10 - referencing >=0.31.0 - python license: MIT license_family: MIT purls: - pkg:pypi/jsonschema-specifications?source=hash-mapping size: 19236 timestamp: 1757335715225 - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-with-format-nongpl-4.26.0-hcf101f3_0.conda sha256: 6886fc61e4e4edd38fd38729976b134e8bd2143f7fce56cc80d7ac7bac99bce1 md5: 8368d58342d0825f0843dc6acdd0c483 depends: - jsonschema >=4.26.0,<4.26.1.0a0 - fqdn - idna - isoduration - jsonpointer >1.13 - rfc3339-validator - rfc3986-validator >0.1.0 - rfc3987-syntax >=1.1.0 - uri-template - webcolors >=24.6.0 license: MIT license_family: MIT purls: [] size: 4740 timestamp: 1767839954258 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.1-pyhcf101f3_0.conda sha256: 3766e2ae59641c172cec8a821528bfa6bf9543ffaaeb8b358bfd5259dcf18e4e md5: 0c3b465ceee138b9c39279cc02e5c4a0 depends: - importlib-metadata >=4.8.3 - jupyter_server >=1.1.2 - python >=3.10 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/jupyter-lsp?source=hash-mapping size: 61633 timestamp: 1775136333147 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.9.0-pyhcf101f3_0.conda sha256: 5341cc66792399cfdf9191dec437b09f59f199d39ce8dbd032a292dcb84a0943 md5: 3461dad033b573b208d9401f305452e0 depends: - jupyter_core >=5.1 - python >=3.10 - python-dateutil >=2.8.2 - pyzmq >=25.0 - tornado >=6.4.1 - traitlets >=5.3 - typing_extensions >=4.13.0 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/jupyter-client?source=compressed-mapping size: 117631 timestamp: 1780678109744 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_core-5.9.1-pyhc90fa1f_0.conda sha256: 1d34b80e5bfcd5323f104dbf99a2aafc0e5d823019d626d0dce5d3d356a2a52a md5: b38fe4e78ee75def7e599843ef4c1ab0 depends: - __unix - python - platformdirs >=2.5 - python >=3.10 - traitlets >=5.3 - python constrains: - pywin32 >=300 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/jupyter-core?source=hash-mapping size: 65503 timestamp: 1760643864586 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_events-0.12.1-pyhcf101f3_0.conda sha256: c7edb5682c6316a95ad781dccb1b6589cd2ec0bf94f23c21152974eb0363b5d7 md5: bf42ee94c750c0b2e7e998b79ac299ea depends: - jsonschema-with-format-nongpl >=4.18.0 - packaging - python >=3.10 - python-json-logger >=2.0.4 - pyyaml >=5.3 - referencing - rfc3339-validator - rfc3986-validator >=0.1.1 - traitlets >=5.3 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/jupyter-events?source=hash-mapping size: 24002 timestamp: 1776861872237 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.19.0-pyhcf101f3_0.conda sha256: 896a350a026db8fff26a7884ed841d53cb84f57f914064fbead0628ab23d1da0 md5: 82525f37e0976e83bbb69bc4d4011665 depends: - anyio >=3.1.0 - argon2-cffi >=21.1 - jinja2 >=3.0.3 - jupyter_client >=7.4.4 - jupyter_core >=4.12,!=5.0.* - jupyter_events >=0.11.0 - jupyter_server_terminals >=0.4.4 - nbconvert-core >=6.4.4 - nbformat >=5.3.0 - overrides >=5.0 - packaging >=22.0 - prometheus_client >=0.9 - python >=3.10 - pyzmq >=24 - send2trash >=1.8.2 - terminado >=0.8.3 - tornado >=6.2.0 - traitlets >=5.6.0 - websocket-client >=1.7 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/jupyter-server?source=compressed-mapping size: 361523 timestamp: 1780151480958 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.4-pyhcf101f3_0.conda sha256: 5eda79ed9f53f590031d29346abd183051263227dd9ee667b5ca1133ce297654 md5: 7b8bace4943e0dc345fc45938826f2b8 depends: - python >=3.10 - terminado >=0.8.3 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/jupyter-server-terminals?source=hash-mapping size: 22052 timestamp: 1768574057200 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.5.8-pyhd8ed1ab_0.conda sha256: 46565306e181df07cd5aed855fa7ef3522658e11b3a840ebbf047ea675c51d30 md5: 8e3f969b0c5d9c22191f3c3306c0f1fb depends: - async-lru >=1.0.0 - httpx >=0.25.0,<1 - ipykernel >=6.5.0,!=6.30.0 - jinja2 >=3.0.3 - jupyter-lsp >=2.0.0 - jupyter_core - jupyter_server >=2.4.0,<3 - jupyterlab_server >=2.28.0,<3 - notebook-shim >=0.2 - packaging >=23.2 - python >=3.10 - setuptools >=41.1.0 - tomli >=1.2.2 - tornado >=6.2.0 - traitlets license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/jupyterlab?source=compressed-mapping size: 8579063 timestamp: 1780577426236 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab_pygments-0.3.0-pyhd8ed1ab_2.conda sha256: dc24b900742fdaf1e077d9a3458fd865711de80bca95fe3c6d46610c532c6ef0 md5: fd312693df06da3578383232528c468d depends: - pygments >=2.4.1,<3 - python >=3.9 constrains: - jupyterlab >=4.0.8,<5.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/jupyterlab-pygments?source=hash-mapping size: 18711 timestamp: 1733328194037 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab_server-2.28.0-pyhcf101f3_0.conda sha256: 381d2d6a259a3be5f38a69463e0f6c5dcf1844ae113058007b51c3bef13a7cee md5: a63877cb23de826b1620d3adfccc4014 depends: - babel >=2.10 - jinja2 >=3.0.3 - json5 >=0.9.0 - jsonschema >=4.18 - jupyter_server >=1.21,<3 - packaging >=21.3 - python >=3.10 - requests >=2.31 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/jupyterlab-server?source=hash-mapping size: 51621 timestamp: 1761145478692 - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.14.1-pyh753f3f9_0.conda sha256: 13141e166f73e8688c441f548a743175d0bfb0590c450e7406493eca7121958a md5: b3d345d389695c26f9f8e06607c7491f depends: - absl-py - h5py - ml_dtypes - namex - numpy - optree - packaging - python >=3.11 - rich constrains: - pytorch >=2.6.0 - jax >=0.5.0 - tensorflow >=2.18.0 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/keras?source=hash-mapping size: 938226 timestamp: 1778246365721 - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.3.3-pyhd8ed1ab_0.conda sha256: a8030a626960f522bc88cf966162fe20d47b4626db5a6afd4b69b9bb7543bf5d md5: 0038c23b52e79facde5d5ecf6745cf64 depends: - absl-py - h5py - ml_dtypes - namex - numpy <2.0a0 - optree - packaging - python >=3.9 - rich constrains: - pytorch >=2.1,<2.3 - jax 0.4.* - tensorflow >=2.15.0,<2.17.0a license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/keras?source=hash-mapping size: 860960 timestamp: 1714206432046 - conda: https://conda.anaconda.org/conda-forge/noarch/keras-3.8.0-pyh753f3f9_0.conda sha256: 71be487fcaa14b57a29654de223a333d9ca373dd9ea988b6312a77ecb65fb3ff md5: fdb4c844d7bd8baed40a01225d837fca depends: - absl-py - h5py - ml_dtypes - namex - numpy - optree - packaging - python >=3.9 - rich constrains: - jax >=0.4.0 - pytorch >=2.1.0 - tensorflow >=2.15.0 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/keras?source=hash-mapping size: 733634 timestamp: 1736538851193 - conda: https://conda.anaconda.org/conda-forge/noarch/keyring-25.7.0-pyh534df25_0.conda sha256: 9def5c6fb3b3b4952a4f6b55a019b5c7065b592682b84710229de5a0b73f6364 md5: c88f9579d08eb4031159f03640714ce3 depends: - __osx - importlib-metadata >=4.11.4 - importlib_resources - jaraco.classes - jaraco.context - jaraco.functools - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/keyring?source=hash-mapping size: 37924 timestamp: 1763320995459 - conda: https://conda.anaconda.org/conda-forge/noarch/keyring-25.7.0-pyha804496_0.conda sha256: 010718b1b1a35ce72782d38e6d6b9495d8d7d0dbea9a3e42901d030ff2189545 md5: 9eeb0eaf04fa934808d3e070eebbe630 depends: - __linux - importlib-metadata >=4.11.4 - importlib_resources - jaraco.classes - jaraco.context - jaraco.functools - jeepney >=0.4.2 - python >=3.10 - secretstorage >=3.2 license: MIT license_family: MIT purls: - pkg:pypi/keyring?source=hash-mapping size: 37717 timestamp: 1763320674488 - conda: https://conda.anaconda.org/conda-forge/noarch/lark-1.3.1-pyhd8ed1ab_0.conda sha256: 49570840fb15f5df5d4b4464db8ee43a6d643031a2bc70ef52120a52e3809699 md5: 9b965c999135d43a3d0f7bd7d024e26a depends: - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/lark?source=hash-mapping size: 94312 timestamp: 1761596921009 - conda: https://conda.anaconda.org/conda-forge/noarch/latexcodec-2.0.1-pyh9f0ad1d_0.tar.bz2 sha256: 5210d31c8f2402dd1ad1b3edcf7a53292b9da5de20cd14d9c243dbf9278b1c4f md5: 8d67904973263afd2985ba56aa2d6bb4 depends: - python - six license: MIT license_family: MIT purls: - pkg:pypi/latexcodec?source=hash-mapping size: 18212 timestamp: 1592937373647 - conda: https://conda.anaconda.org/conda-forge/noarch/libml_dtypes-headers-0.5.4-h707e725_0.conda sha256: 37e3fe66ec86b8fff9db741a1e6fcc19874646702aaa74c97bf26852ffbd0276 md5: 597a39bc0946262b29644366059cd109 depends: - __unix license: Apache-2.0 license_family: APACHE purls: [] size: 23946 timestamp: 1764082185215 - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10.2-pyhcf101f3_0.conda sha256: 20e0892592a3e7c683e3d66df704a9425d731486a97c34fc56af4da1106b2b6b md5: ba0a9221ce1063f31692c07370d062f3 depends: - importlib-metadata >=4.4 - python >=3.10 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/markdown?source=hash-mapping size: 85893 timestamp: 1770694658918 - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.2.0-pyhd8ed1ab_0.conda sha256: 0c4c35376fe920714390d46e4b8d31c876d65f18e1655899e0763ec25f2a902f md5: 6d03368f2b2b0a5fb6839df53b2eb5e0 depends: - mdurl >=0.1,<1 - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/markdown-it-py?source=hash-mapping size: 69017 timestamp: 1778169663339 - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.2-pyhd8ed1ab_0.conda sha256: 35b43d7343f74452307fd018a1cca92b8f68961ff8e2ab6a81ce0a703c9a3764 md5: 9acc1c385be401d533ff70ef5b50dae6 depends: - python >=3.10 - traitlets license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/matplotlib-inline?source=compressed-mapping size: 15725 timestamp: 1778264403247 - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda sha256: 78c1bbe1723449c52b7a9df1af2ee5f005209f67e40b6e1d3c7619127c43b1c7 md5: 592132998493b3ff25fd7479396e8351 depends: - python >=3.9 license: MIT license_family: MIT purls: - pkg:pypi/mdurl?source=hash-mapping size: 14465 timestamp: 1733255681319 - conda: https://conda.anaconda.org/conda-forge/noarch/memory_profiler-0.61.0-pyhcf101f3_1.conda sha256: 737616a517a15c9d8a56602f54eff7aeb81491711c2f5634bc2b6873af1b4037 md5: e1bccffd88819e75729412799824e270 depends: - python >=3.10 - psutil - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/memory-profiler?source=hash-mapping size: 36168 timestamp: 1764885507963 - conda: https://conda.anaconda.org/conda-forge/noarch/mistune-3.2.1-pyhcf101f3_0.conda sha256: b52dc6c78fbbe7a3008535cb8bfd87d70d8053e9250bbe16e387470a9df07070 md5: b97e84d1553b4a1c765b87fff83453ad depends: - python >=3.10 - typing_extensions - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/mistune?source=hash-mapping size: 74567 timestamp: 1777824616382 - conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-11.1.0-pyhcf101f3_0.conda sha256: 6725c1c8db9d025db763e1bba1acdcff2eb2ae20a7766a71512ea84081033288 md5: 0e694257dbf916540b749c3ffc808efe depends: - python >=3.10 - python license: MIT license_family: MIT purls: - pkg:pypi/more-itertools?source=hash-mapping size: 71270 timestamp: 1779478190496 - conda: https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyhd8ed1ab_1.conda sha256: d09c47c2cf456de5c09fa66d2c3c5035aa1fa228a1983a433c47b876aa16ce90 md5: 37293a85a0f4f77bbd9cf7aaefc62609 depends: - python >=3.9 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/munkres?source=hash-mapping size: 15851 timestamp: 1749895533014 - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda sha256: 6ed158e4e5dd8f6a10ad9e525631e35cee8557718f83de7a4e3966b1f772c4b1 md5: e9c622e0d00fa24a6292279af3ab6d06 depends: - python >=3.9 license: MIT license_family: MIT purls: - pkg:pypi/mypy-extensions?source=hash-mapping size: 11766 timestamp: 1745776666688 - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda sha256: 295e0ef6aae0185b55752c981eca5c11443ba9ea4c236d45112128799fd99f51 md5: 3eb854547a0183b994431957fa0e05d2 depends: - python >=3.9 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/namex?source=hash-mapping size: 11936 timestamp: 1748346473739 - conda: https://conda.anaconda.org/conda-forge/noarch/narwhals-2.22.1-pyhcf101f3_0.conda sha256: dd2744a501f2db0aef084566bf3d0c2b312661dc91beb5a4cc97d27cdda0a959 md5: 9450fb40fb1e147d0bcbdf07cd02ca96 depends: - python >=3.10 - python license: MIT license_family: MIT purls: - pkg:pypi/narwhals?source=compressed-mapping size: 285532 timestamp: 1780672242196 - conda: https://conda.anaconda.org/conda-forge/noarch/nbclient-0.11.0-pyhd8ed1ab_0.conda sha256: eceb424236fbbb9b337a857fe5448307b57a2a3fb2db389ae37e7a8b8cdca2ab md5: cf01a81d7960ad9c829bf2e794fcee9a depends: - jupyter_client >=7.0.0 - jupyter_core >=5.4 - nbformat >=5.2.0 - python >=3.10 - traitlets >=5.13 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/nbclient?source=compressed-mapping size: 29138 timestamp: 1780661039538 - conda: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.17.1-pyhcf101f3_0.conda sha256: ab2ac79c5892c5434d50b3542d96645bdaa06d025b6e03734be29200de248ac2 md5: 2bce0d047658a91b99441390b9b27045 depends: - beautifulsoup4 - bleach-with-css !=5.0.0 - defusedxml - importlib-metadata >=3.6 - jinja2 >=3.0 - jupyter_core >=4.7 - jupyterlab_pygments - markupsafe >=2.0 - mistune >=2.0.3,<4 - nbclient >=0.5.0 - nbformat >=5.7 - packaging - pandocfilters >=1.4.1 - pygments >=2.4.1 - python >=3.10 - traitlets >=5.1 - python constrains: - pandoc >=2.9.2,<4.0.0 - nbconvert ==7.17.1 *_0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/nbconvert?source=hash-mapping size: 202229 timestamp: 1775615493260 - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda sha256: 7a5bd30a2e7ddd7b85031a5e2e14f290898098dc85bea5b3a5bf147c25122838 md5: bbe1963f1e47f594070ffe87cdf612ea depends: - jsonschema >=2.6 - jupyter_core >=4.12,!=5.0.* - python >=3.9 - python-fastjsonschema >=2.15 - traitlets >=5.1 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/nbformat?source=hash-mapping size: 100945 timestamp: 1733402844974 - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda sha256: bb7b21d7fd0445ddc0631f64e66d91a179de4ba920b8381f29b9d006a42788c0 md5: 598fd7d4d0de2455fb74f56063969a97 depends: - python >=3.9 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/nest-asyncio?source=hash-mapping size: 11543 timestamp: 1733325673691 - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.10.0-pyhd8ed1ab_0.conda sha256: 4fa40e3e13fc6ea0a93f67dfc76c96190afd7ea4ffc1bac2612d954b42cdc3ee md5: eb52d14a901e23c39e9e7b4a1a5c015f depends: - python >=3.10 - setuptools license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/nodeenv?source=hash-mapping size: 40866 timestamp: 1766261270149 - conda: https://conda.anaconda.org/conda-forge/noarch/notebook-shim-0.2.4-pyhd8ed1ab_1.conda sha256: 7b920e46b9f7a2d2aa6434222e5c8d739021dbc5cc75f32d124a8191d86f9056 md5: e7f89ea5f7ea9401642758ff50a2d9c1 depends: - jupyter_server >=1.8,<3 - python >=3.9 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/notebook-shim?source=hash-mapping size: 16817 timestamp: 1733408419340 - conda: https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.10.0-pyhcf101f3_0.conda sha256: 482d94fce136c4352b18c6397b9faf0a3149bfb12499ab1ffebad8db0cb6678f md5: 3aa4b625f20f55cf68e92df5e5bf3c39 depends: - python >=3.10 - sphinx >=6 - tomli >=1.1.0 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpydoc?source=hash-mapping size: 65801 timestamp: 1764715638266 - conda: https://conda.anaconda.org/conda-forge/noarch/opt_einsum-3.4.0-pyhd8ed1ab_1.conda sha256: af71aabb2bfa4b2c89b7b06403e5cec23b418452cae9f9772bd7ac3f9ea1ff44 md5: 52919815cd35c4e1a0298af658ccda04 depends: - python >=3.9 license: MIT license_family: MIT purls: - pkg:pypi/opt-einsum?source=hash-mapping size: 62479 timestamp: 1733688053334 - conda: https://conda.anaconda.org/conda-forge/noarch/overrides-7.7.0-pyhd8ed1ab_1.conda sha256: 1840bd90d25d4930d60f57b4f38d4e0ae3f5b8db2819638709c36098c6ba770c md5: e51f1e4089cad105b6cac64bd8166587 depends: - python >=3.9 - typing_utils license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/overrides?source=hash-mapping size: 30139 timestamp: 1734587755455 - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda sha256: da157b19bcd398b9804c5c52fc000fcb8ab0525bdb9c70f95beaa0bb42f85af1 md5: 3bfed7e6228ebf2f7b9eaa47f1b4e2aa depends: - python >=3.8 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/packaging?source=hash-mapping size: 60164 timestamp: 1733203368787 - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda sha256: 3906abfb6511a3bb309e39b9b1b7bc38f50a723971de2395489fd1f379255890 md5: 4c06a92e74452cfa53623a81592e8934 depends: - python >=3.8 - python license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/packaging?source=hash-mapping size: 91574 timestamp: 1777103621679 - conda: https://conda.anaconda.org/conda-forge/noarch/pandocfilters-1.5.0-pyhd8ed1ab_0.tar.bz2 sha256: 2bb9ba9857f4774b85900c2562f7e711d08dd48e2add9bee4e1612fbee27e16f md5: 457c2c8c08e54905d6954e79cb5b5db9 depends: - python !=3.0,!=3.1,!=3.2,!=3.3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pandocfilters?source=hash-mapping size: 11627 timestamp: 1631603397334 - conda: https://conda.anaconda.org/conda-forge/noarch/parso-0.8.7-pyhcf101f3_0.conda sha256: 611882f7944b467281c46644ffde6c5145d1a7730388bcde26e7e86819b0998e md5: 39894c952938276405a1bd30e4ce2caf depends: - python >=3.10 - python license: MIT license_family: MIT purls: - pkg:pypi/parso?source=hash-mapping size: 82472 timestamp: 1777722955579 - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda sha256: 6eaee417d33f298db79bc7185ab1208604c0e6cf51dade34cd513c6f9db9c6f3 md5: 11adc78451c998c0fd162584abfa3559 depends: - python >=3.10 license: MPL-2.0 license_family: MOZILLA purls: - pkg:pypi/pathspec?source=hash-mapping size: 56559 timestamp: 1777271601895 - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.2-pyhcf101f3_0.conda sha256: 9678f4745e6b82b36fab9657a19665081862268cb079cf9acf878ab2c4fadee9 md5: 8678577a52161cc4e1c93fcc18e8a646 depends: - numpy >=1.4.0 - python >=3.10 - python license: BSD-2-Clause AND PSF-2.0 purls: - pkg:pypi/patsy?source=hash-mapping size: 193450 timestamp: 1760998269054 - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda sha256: 202af1de83b585d36445dc1fda94266697341994d1a3328fabde4989e1b3d07a md5: d0d408b1f18883a944376da5cf8101ea depends: - ptyprocess >=0.5 - python >=3.9 license: ISC purls: - pkg:pypi/pexpect?source=hash-mapping size: 53561 timestamp: 1733302019362 - conda: https://conda.anaconda.org/conda-forge/noarch/pip-26.1.2-pyh8b19718_0.conda sha256: 29b7d75bf81ad11645a8e320b369abdc90a92b93f2a9178e853d9dddf82e5106 md5: 511fbc2c63d2c73650ad1755e4d357ba depends: - python >=3.10,<3.13.0a0 - setuptools - wheel license: MIT license_family: MIT purls: - pkg:pypi/pip?source=compressed-mapping size: 1203173 timestamp: 1780262795392 - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.10.0-pyhcf101f3_0.conda sha256: 9e5e1fd3506ccfc4d444fc4d2d39b0ed097d5d0e3bd3d4bdf6bcc81aaf66860d md5: 2c5ef45db85d34799771629bd5860fd7 depends: - python >=3.10 - python license: MIT license_family: MIT purls: - pkg:pypi/platformdirs?source=compressed-mapping size: 26308 timestamp: 1779972894916 - conda: https://conda.anaconda.org/conda-forge/noarch/pluggy-1.6.0-pyhf9edf01_1.conda sha256: e14aafa63efa0528ca99ba568eaf506eb55a0371d12e6250aaaa61718d2eb62e md5: d7585b6550ad04c8c5e21097ada2888e depends: - python >=3.9 - python license: MIT license_family: MIT purls: - pkg:pypi/pluggy?source=hash-mapping size: 25877 timestamp: 1764896838868 - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.6.0-pyha770c72_0.conda sha256: 716960bf0a9eb334458a26b3bdcb17b8d0786062138a4f48c7f335c8418c5d0b md5: 7859736b4f8ebe6c8481bf48d91c9a1e depends: - cfgv >=2.0.0 - identify >=1.0.0 - nodeenv >=0.11.1 - python >=3.10 - pyyaml >=5.1 - virtualenv >=20.10.0 license: MIT license_family: MIT purls: - pkg:pypi/pre-commit?source=hash-mapping size: 201606 timestamp: 1776858157327 - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.25.0-pyhd8ed1ab_0.conda sha256: 4d7ec90d4f9c1f3b4a50623fefe4ebba69f651b102b373f7c0e9dbbfa43d495c md5: a11ab1f31af799dd93c3a39881528884 depends: - python >=3.10 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/prometheus-client?source=hash-mapping size: 57113 timestamp: 1775771465170 - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda sha256: 4817651a276016f3838957bfdf963386438c70761e9faec7749d411635979bae md5: edb16f14d920fb3faf17f5ce582942d6 depends: - python >=3.10 - wcwidth constrains: - prompt_toolkit 3.0.52 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/prompt-toolkit?source=hash-mapping size: 273927 timestamp: 1756321848365 - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda sha256: a7713dfe30faf17508ec359e0bc7e0983f5d94682492469bd462cdaae9c64d83 md5: 7d9daffbb8d8e0af0f769dbbcd173a54 depends: - python >=3.9 license: ISC purls: - pkg:pypi/ptyprocess?source=hash-mapping size: 19457 timestamp: 1733302371990 - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda sha256: 71bd24600d14bb171a6321d523486f6a06f855e75e547fa0cb2a0953b02047f0 md5: 3bfdfb8dbcdc4af1ae3f9a8eb3948f04 depends: - python >=3.9 license: MIT license_family: MIT purls: - pkg:pypi/pure-eval?source=hash-mapping size: 16668 timestamp: 1733569518868 - conda: https://conda.anaconda.org/conda-forge/noarch/pybtex-0.26.1-pyhcf101f3_0.conda sha256: fd9b0a389b21dc2ccb11925db83a5e9e32652e87971f93c23358d51c3632d96d md5: b0969b1326b5b9d6094dd1831eb48919 depends: - python >=3.10 - pyyaml >=3.1 - latexcodec >=1.0.4 - importlib-metadata - python license: MIT license_family: MIT purls: - pkg:pypi/pybtex?source=hash-mapping size: 78091 timestamp: 1775231969598 - conda: https://conda.anaconda.org/conda-forge/noarch/pybtex-docutils-1.0.3-pyhcf101f3_4.conda sha256: a0397b8fc65eabd773fe33affb726fe9d16c8f0a8ab7c3493d80c412ef2539a6 md5: 75f19dd4b0b95ce928286e18c561cb13 depends: - python >=3.10 - setuptools - docutils >=0.14 - pybtex >=0.16 - python license: MIT license_family: MIT purls: - pkg:pypi/pybtex-docutils?source=hash-mapping size: 14980 timestamp: 1765317730499 - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-3.0-pyhcf101f3_0.conda sha256: e27e0473fc6723311a0bd48b89b616fa1b996a2f7a2b555338cbbcfb9c640568 md5: 9c5491066224083c41b6d5635ed7107b depends: - python >=3.10 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pycparser?source=compressed-mapping size: 55886 timestamp: 1779293633166 - conda: https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.18.0-pyhcf101f3_0.conda sha256: 5af387503b34fddaf6192acbdd45a90350574a29a33cc513b058cc6cf00cdf4f md5: 1f88d3881f8d26c1357db69ad65b0df5 depends: - accessible-pygments - babel - beautifulsoup4 - docutils !=0.17.0 - pygments >=2.7 - python >=3.10 - sphinx >=8.0 - typing_extensions - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pydata-sphinx-theme?source=hash-mapping size: 1312246 timestamp: 1779282171610 - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda sha256: cf70b2f5ad9ae472b71235e5c8a736c9316df3705746de419b59d442e8348e86 md5: 16c18772b340887160c79a6acc022db0 depends: - python >=3.10 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/pygments?source=hash-mapping size: 893031 timestamp: 1774796815820 - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.3.2-pyhcf101f3_0.conda sha256: 417fba4783e528ee732afa82999300859b065dc59927344b4859c64aae7182de md5: 3687cc0b82a8b4c17e1f0eb7e47163d5 depends: - python >=3.10 - python license: MIT license_family: MIT purls: - pkg:pypi/pyparsing?source=hash-mapping size: 110893 timestamp: 1769003998136 - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda sha256: ba3b032fa52709ce0d9fd388f63d330a026754587a2f461117cac9ab73d8d0d8 md5: 461219d1a5bd61342293efa2c0c90eac depends: - __unix - python >=3.9 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pysocks?source=hash-mapping size: 21085 timestamp: 1733217331982 - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-8.4.2-pyhcf101f3_1.conda sha256: 39f41a52eb6f927caf5cd42a2ff98a09bb850ce9758b432869374b6253826962 md5: da0c42269086f5170e2b296878ec13a6 depends: - pygments >=2.7.2 - python >=3.10 - iniconfig >=1 - packaging >=20 - pluggy >=1.5,<2 - tomli >=1 - colorama >=0.4 - exceptiongroup >=1 - python constrains: - pytest-faulthandler >=2 license: MIT license_family: MIT purls: - pkg:pypi/pytest?source=hash-mapping size: 294852 timestamp: 1762354779909 - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-cov-5.0.0-pyhd8ed1ab_0.conda sha256: 218306243faf3c36347131c2b36bb189daa948ac2e92c7ab52bb26cc8c157b3c md5: c54c0107057d67ddf077751339ec2c63 depends: - coverage >=5.2.1 - pytest >=4.6 - python >=3.8 - toml license: MIT license_family: MIT purls: - pkg:pypi/pytest-cov?source=hash-mapping size: 25507 timestamp: 1711411153367 - conda: https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.8.0-pyhd8ed1ab_0.conda sha256: b7b58a5be090883198411337b99afb6404127809c3d1c9f96e99b59f36177a96 md5: 8375cfbda7c57fbceeda18229be10417 depends: - execnet >=2.1 - pytest >=7.0.0 - python >=3.9 constrains: - psutil >=3.0 license: MIT license_family: MIT purls: - pkg:pypi/pytest-xdist?source=hash-mapping size: 39300 timestamp: 1751452761594 - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda sha256: d6a17ece93bbd5139e02d2bd7dbfa80bee1a4261dced63f65f679121686bf664 md5: 5b8d21249ff20967101ffa321cab24e8 depends: - python >=3.9 - six >=1.5 - python license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/python-dateutil?source=hash-mapping size: 233310 timestamp: 1751104122689 - conda: https://conda.anaconda.org/conda-forge/noarch/python-discovery-1.4.0-pyhcf101f3_0.conda sha256: dd709df1ef4e44ea9b6dd48b6e53c062efcc12850b3116b2884cfbef1aa4bd92 md5: fb1e5c138e2d933e59b3fa0462acc5e6 depends: - python >=3.10 - filelock >=3.15.4 - platformdirs <5,>=4.3.6 - python license: MIT license_family: MIT purls: - pkg:pypi/python-discovery?source=compressed-mapping size: 34924 timestamp: 1779967197357 - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda sha256: df9aa74e9e28e8d1309274648aac08ec447a92512c33f61a8de0afa9ce32ebe8 md5: 23029aae904a2ba587daba708208012f depends: - python >=3.9 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/fastjsonschema?source=hash-mapping size: 244628 timestamp: 1755304154927 - conda: https://conda.anaconda.org/conda-forge/noarch/python-flatbuffers-25.9.23-pyh1e1bc0e_0.conda sha256: 1933243d9f839bb7bc6c9b75481b6445f50f8b727eb926086e8a2a347fb0467d md5: 611207751a2c0b5b999b07b78985d7a0 depends: - python >=3.10 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/flatbuffers?source=hash-mapping size: 34827 timestamp: 1758880404905 - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.11.15-hd8ed1ab_0.conda sha256: d4431c088320de51f61df0c0aa5aa85921037fdcfcff528b9d7e614ed5d7c6b4 md5: 8793751936480e56ce4047b4f54b997f depends: - cpython 3.11.15.* - python_abi * *_cp311 license: Python-2.0 purls: [] size: 47985 timestamp: 1772730345561 - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-3.2.1-pyh332efcf_0.conda sha256: 1c55116c22512cef7b01d55ae49697707f2c1fd829407183c19817e2d300fd8d md5: 1cd2f3e885162ee1366312bd1b1677fd depends: - python >=3.10 - typing_extensions license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/python-json-logger?source=hash-mapping size: 18969 timestamp: 1777318679482 - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda sha256: e943f9c15a6bdba2e1b9f423ab913b3f6b02197b0ef9f8e6b7464d78b59965b9 md5: f6ad7450fc21e00ecc23812baed6d2e4 depends: - python >=3.10 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tzdata?source=hash-mapping size: 146639 timestamp: 1777068997932 - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda build_number: 8 sha256: 7ad76fa396e4bde336872350124c0819032a9e8a0a40590744ff9527b54351c1 md5: 05e00f3b21e88bb3d658ac700b2ce58c constrains: - python 3.10.* *_cpython license: BSD-3-Clause license_family: BSD purls: [] size: 6999 timestamp: 1752805924192 - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda build_number: 8 sha256: fddf123692aa4b1fc48f0471e346400d9852d96eeed77dbfdd746fa50a8ff894 md5: 8fcb6b0e2161850556231336dae58358 constrains: - python 3.11.* *_cpython license: BSD-3-Clause license_family: BSD purls: [] size: 7003 timestamp: 1752805919375 - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda build_number: 8 sha256: 80677180dd3c22deb7426ca89d6203f1c7f1f256f2d5a94dc210f6e758229809 md5: c3efd25ac4d74b1584d2f7a57195ddf1 constrains: - python 3.12.* *_cpython license: BSD-3-Clause license_family: BSD purls: [] size: 6958 timestamp: 1752805918820 - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda build_number: 8 sha256: ad6d2e9ac39751cc0529dd1566a26751a0bf2542adb0c232533d32e176e21db5 md5: 0539938c55b6b1a59b560e843ad864a4 constrains: - python 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD purls: [] size: 6989 timestamp: 1752805904792 - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314t.conda build_number: 8 sha256: d9ed2538fba61265a330ee1b1afe99a4bb23ace706172b9464546c7e01259d63 md5: 3251796e09870c978e0f69fa05e38fb6 constrains: - python 3.14.* *_cp314t license: BSD-3-Clause license_family: BSD purls: [] size: 7020 timestamp: 1752805919426 - conda: https://conda.anaconda.org/conda-forge/noarch/pytz-2026.2-pyhcf101f3_0.conda sha256: 5020863d629f584b5c057333a67a7aed43e3ed013ba15dd70f353501ccb5aff6 md5: 03cb60f505ad3ada0a95277af5faeb1a depends: - python >=3.10 - python license: MIT license_family: MIT purls: - pkg:pypi/pytz?source=hash-mapping size: 201747 timestamp: 1777892201250 - conda: https://conda.anaconda.org/conda-forge/noarch/readme_renderer-44.0-pyhd8ed1ab_1.conda sha256: 66f3adf6aaabf977cfcc22cb65607002b1de4a22bc9fac7be6bb774bc6f85a3a md5: c58dd5d147492671866464405364c0f1 depends: - cmarkgfm >=0.8.0 - docutils >=0.21.2 - nh3 >=0.2.14 - pygments >=2.5.1 - python >=3.9 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/readme-renderer?source=hash-mapping size: 17481 timestamp: 1734339765256 - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda sha256: 0577eedfb347ff94d0f2fa6c052c502989b028216996b45c7f21236f25864414 md5: 870293df500ca7e18bedefa5838a22ab depends: - attrs >=22.2.0 - python >=3.10 - rpds-py >=0.7.0 - typing_extensions >=4.4.0 - python license: MIT license_family: MIT purls: - pkg:pypi/referencing?source=hash-mapping size: 51788 timestamp: 1760379115194 - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.34.2-pyhcf101f3_0.conda sha256: 1715246b19c9f85ee022933b4845f2fc14ac9184981b7b7d9b728bec8e9588da md5: 4a85203c1d80c1059086ae860836ffb9 depends: - python >=3.10 - certifi >=2023.5.7 - charset-normalizer >=2,<4 - idna >=2.5,<4 - urllib3 >=1.26,<3 - python constrains: - chardet >=3.0.2,<8 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/requests?source=compressed-mapping size: 68709 timestamp: 1778851103479 - conda: https://conda.anaconda.org/conda-forge/noarch/requests-toolbelt-1.0.0-pyhd8ed1ab_1.conda sha256: c0b815e72bb3f08b67d60d5e02251bbb0164905b5f72942ff5b6d2a339640630 md5: 66de8645e324fda0ea6ef28c2f99a2ab depends: - python >=3.9 - requests >=2.0.1,<3.0.0 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/requests-toolbelt?source=hash-mapping size: 44285 timestamp: 1733734886897 - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda sha256: 2e4372f600490a6e0b3bac60717278448e323cab1c0fecd5f43f7c56535a99c5 md5: 36de09a8d3e5d5e6f4ee63af49e59706 depends: - python >=3.9 - six license: MIT license_family: MIT purls: - pkg:pypi/rfc3339-validator?source=hash-mapping size: 10209 timestamp: 1733600040800 - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-2.0.0-pyhd8ed1ab_1.conda sha256: d617373ba1a5108336cb87754d030b9e384dcf91796d143fa60fe61e76e5cfb0 md5: 43e14f832d7551e5a8910672bfc3d8c6 depends: - python >=3.9 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/rfc3986?source=hash-mapping size: 38028 timestamp: 1733921806657 - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2 sha256: 2a5b495a1de0f60f24d8a74578ebc23b24aa53279b1ad583755f223097c41c37 md5: 912a71cc01012ee38e6b90ddd561e36f depends: - python license: MIT license_family: MIT purls: - pkg:pypi/rfc3986-validator?source=hash-mapping size: 7818 timestamp: 1598024297745 - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda sha256: 70001ac24ee62058557783d9c5a7bbcfd97bd4911ef5440e3f7a576f9e43bc92 md5: 7234f99325263a5af6d4cd195035e8f2 depends: - python >=3.9 - lark >=1.2.2 - python license: MIT license_family: MIT purls: - pkg:pypi/rfc3987-syntax?source=hash-mapping size: 22913 timestamp: 1752876729969 - conda: https://conda.anaconda.org/conda-forge/noarch/rich-15.0.0-pyhcf101f3_0.conda sha256: 3d6ba2c0fcdac3196ba2f0615b4104e532525ffa1335b50a2878be5ff488814a md5: 0242025a3c804966bf71aa04eee82f66 depends: - markdown-it-py >=2.2.0 - pygments >=2.13.0,<3.0.0 - python >=3.10 - typing_extensions >=4.0.0,<5.0.0 - python license: MIT license_family: MIT purls: - pkg:pypi/rich?source=hash-mapping size: 208577 timestamp: 1775991661559 - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-4.1.0-pyhd8ed1ab_0.conda sha256: 30f3c04fcfb64c44d821d392a4a0b8915650dbd900c8befc20ade8fde8ec6aa2 md5: 0dc48b4b570931adc8641e55c6c17fe4 depends: - python >=3.10 license: 0BSD OR CC0-1.0 purls: - pkg:pypi/roman-numerals?source=hash-mapping size: 13814 timestamp: 1766003022813 - conda: https://conda.anaconda.org/conda-forge/noarch/roman-numerals-py-4.1.0-pyhd8ed1ab_0.conda sha256: ce21b50a412b87b388db9e8dfbf8eb16fc436c23750b29bf612ee1a74dd0beb2 md5: 28687768633154993d521aecfa4a56ac depends: - python >=3.10 - roman-numerals 4.1.0 license: 0BSD OR CC0-1.0 purls: - pkg:pypi/roman-numerals-py?source=hash-mapping size: 11074 timestamp: 1766025162370 - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-0.13.2-hd8ed1ab_3.conda noarch: python sha256: ea29a69b14dd6be5cdeeaa551bf50d78cafeaf0351e271e358f9b820fcab4cb0 md5: 62afb877ca2c2b4b6f9ecb37320085b6 depends: - seaborn-base 0.13.2 pyhd8ed1ab_3 - statsmodels >=0.12 license: BSD-3-Clause license_family: BSD purls: [] size: 6876 timestamp: 1733730113224 - conda: https://conda.anaconda.org/conda-forge/noarch/seaborn-base-0.13.2-pyhd8ed1ab_3.conda sha256: f209c9c18187570b85ec06283c72d64b8738f825b1b82178f194f4866877f8aa md5: fd96da444e81f9e6fcaac38590f3dd42 depends: - matplotlib-base >=3.4,!=3.6.1 - numpy >=1.20,!=1.24.0 - pandas >=1.2 - python >=3.9 - scipy >=1.7 constrains: - seaborn =0.13.2=*_3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/seaborn?source=hash-mapping size: 227843 timestamp: 1733730112409 - conda: https://conda.anaconda.org/conda-forge/noarch/send2trash-2.1.0-pyh5552912_1.conda sha256: 8fc024bf1a7b99fc833b131ceef4bef8c235ad61ecb95a71a6108be2ccda63e8 md5: b70e2d44e6aa2beb69ba64206a16e4c6 depends: - __osx - pyobjc-framework-cocoa - python >=3.10 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/send2trash?source=hash-mapping size: 22519 timestamp: 1770937603551 - conda: https://conda.anaconda.org/conda-forge/noarch/send2trash-2.1.0-pyha191276_1.conda sha256: 59656f6b2db07229351dfb3a859c35e57cc8e8bcbc86d4e501bff881a6f771f1 md5: 28eb91468df04f655a57bcfbb35fc5c5 depends: - __linux - python >=3.10 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/send2trash?source=hash-mapping size: 24108 timestamp: 1770937597662 - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda sha256: 82088a6e4daa33329a30bc26dc19a98c7c1d3f05c0f73ce9845d4eab4924e9e1 md5: 8e194e7b992f99a5015edbd4ebd38efd depends: - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/setuptools?source=hash-mapping size: 639697 timestamp: 1773074868565 - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda sha256: 458227f759d5e3fcec5d9b7acce54e10c9e1f4f4b7ec978f3bfd54ce4ee9853d md5: 3339e3b65d58accf4ca4fb8748ab16b3 depends: - python >=3.9 - python license: MIT license_family: MIT purls: - pkg:pypi/six?source=hash-mapping size: 18455 timestamp: 1753199211006 - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.6-pyhd8ed1ab_0.conda sha256: 7c21f0d9f2746f110f0dc626dea13ff17a541dd84fb499b31d29bcada8db65c6 md5: 89ce186da8f9894cd653b72374ff1734 depends: - python >=3.10 - scikit-learn >=1.2,<1.10 license: BSD-3-Clause purls: - pkg:pypi/sklearn-compat?source=hash-mapping size: 26179 timestamp: 1780859456474 - conda: https://conda.anaconda.org/conda-forge/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda sha256: dce518f45e24cd03f401cb0616917773159a210c19d601c5f2d4e0e5879d30ad md5: 03fe290994c5e4ec17293cfb6bdce520 depends: - python >=3.10 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/sniffio?source=hash-mapping size: 15698 timestamp: 1762941572482 - conda: https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.1.1-pyhd8ed1ab_0.conda sha256: ad89284ea94821c20ff87e64b948e4afc690cf5202d14c009355b0594cf23aea md5: 46b6abe31482f6bca064b965696ae807 depends: - python >=3.10 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/snowballstemmer?source=hash-mapping size: 74456 timestamp: 1780468201547 - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.4-pyhd8ed1ab_0.conda sha256: 2afa5fe9331c09b4c4689ddf6ace8fc16c837eae547c57dab325b844072fdd77 md5: 9e21f087f087f805debe877d88e00a14 depends: - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/soupsieve?source=compressed-mapping size: 38802 timestamp: 1779635534390 - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-8.2.3-pyhd8ed1ab_0.conda sha256: 995f58c662db0197d681fa345522fd9e7ac5f05330d3dff095ab2f102e260ab0 md5: f7af826063ed569bb13f7207d6f949b0 depends: - alabaster >=0.7.14 - babel >=2.13 - colorama >=0.4.6 - docutils >=0.20,<0.22 - imagesize >=1.3 - jinja2 >=3.1 - packaging >=23.0 - pygments >=2.17 - python >=3.11 - requests >=2.30.0 - roman-numerals-py >=1.0.0 - snowballstemmer >=2.2 - sphinxcontrib-applehelp >=1.0.7 - sphinxcontrib-devhelp >=1.0.6 - sphinxcontrib-htmlhelp >=2.0.6 - sphinxcontrib-jsmath >=1.0.1 - sphinxcontrib-qthelp >=1.0.6 - sphinxcontrib-serializinghtml >=1.1.9 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/sphinx?source=hash-mapping size: 1424416 timestamp: 1740956642838 - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.2-pyhd8ed1ab_1.conda sha256: 8cd892e49cb4d00501bc4439fb0c73ca44905f01a65b2b7fa05ba0e8f3924f19 md5: bf22cb9c439572760316ce0748af3713 depends: - python >=3.9 - sphinx >=1.8 license: MIT license_family: MIT purls: - pkg:pypi/sphinx-copybutton?source=hash-mapping size: 17893 timestamp: 1734573117732 - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-design-0.7.0-pyhd8ed1ab_0.conda sha256: 7f8437a97e6311bebf230cfd2ae3c5bdb2230e681c41daebdb894280bf8b4ab6 md5: 28eddfb8b9ecdd044a6f609f985398a7 depends: - python >=3.11 - sphinx >=7,<10 license: MIT license_family: MIT purls: - pkg:pypi/sphinx-design?source=hash-mapping size: 931118 timestamp: 1769032711360 - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.21.0-pyhd8ed1ab_0.conda sha256: e6d29bca607436e72362f07638b5425892e4453476f997fd93698dfae3893b60 md5: 9b783047bd5bef0998f129bef8fad477 depends: - pillow - python >=3.10 - sphinx >=4 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/sphinx-gallery?source=hash-mapping size: 398898 timestamp: 1777021908652 - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-applehelp-2.0.0-pyhd8ed1ab_1.conda sha256: d7433a344a9ad32a680b881c81b0034bc61618d12c39dd6e3309abeffa9577ba md5: 16e3f039c0aa6446513e94ab18a8784b depends: - python >=3.9 - sphinx >=5 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/sphinxcontrib-applehelp?source=hash-mapping size: 29752 timestamp: 1733754216334 - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-bibtex-2.7.0-pyhd8ed1ab_0.conda sha256: 3b9c6273f95302df966a48a267b33ddf78ed6e662c7d578d3e129e259e049314 md5: 7f562e153eedbdc92f1c3adb019a8792 depends: - docutils >=0.20 - pybtex >=0.25 - pybtex-docutils >=1.0.2 - python >=3.10 - sphinx >=7.4 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/sphinxcontrib-bibtex?source=hash-mapping size: 33489 timestamp: 1778364472446 - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-2.0.0-pyhd8ed1ab_1.conda sha256: 55d5076005d20b84b20bee7844e686b7e60eb9f683af04492e598a622b12d53d md5: 910f28a05c178feba832f842155cbfff depends: - python >=3.9 - sphinx >=5 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/sphinxcontrib-devhelp?source=hash-mapping size: 24536 timestamp: 1733754232002 - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.1.0-pyhd8ed1ab_1.conda sha256: c1492c0262ccf16694bdcd3bb62aa4627878ea8782d5cd3876614ffeb62b3996 md5: e9fb3fe8a5b758b4aff187d434f94f03 depends: - python >=3.9 - sphinx >=5 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/sphinxcontrib-htmlhelp?source=hash-mapping size: 32895 timestamp: 1733754385092 - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-pyhd8ed1ab_1.conda sha256: 578bef5ec630e5b2b8810d898bbbf79b9ae66d49b7938bcc3efc364e679f2a62 md5: fa839b5ff59e192f411ccc7dae6588bb depends: - python >=3.9 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/sphinxcontrib-jsmath?source=hash-mapping size: 10462 timestamp: 1733753857224 - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-2.0.0-pyhd8ed1ab_1.conda sha256: c664fefae4acdb5fae973bdde25836faf451f41d04342b64a358f9a7753c92ca md5: 00534ebcc0375929b45c3039b5ba7636 depends: - python >=3.9 - sphinx >=5 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/sphinxcontrib-qthelp?source=hash-mapping size: 26959 timestamp: 1733753505008 - conda: https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.10-pyhd8ed1ab_1.conda sha256: 64d89ecc0264347486971a94487cb8d7c65bfc0176750cf7502b8a272f4ab557 md5: 3bc61f7161d28137797e038263c04c54 depends: - python >=3.9 - sphinx >=5 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/sphinxcontrib-serializinghtml?source=hash-mapping size: 28669 timestamp: 1733750596111 - conda: https://conda.anaconda.org/conda-forge/noarch/stack_data-0.6.3-pyhd8ed1ab_1.conda sha256: 570da295d421661af487f1595045760526964f41471021056e993e73089e9c41 md5: b1b505328da7a6b246787df4b5a49fbc depends: - asttokens - executing - pure_eval - python >=3.9 license: MIT license_family: MIT purls: - pkg:pypi/stack-data?source=hash-mapping size: 26988 timestamp: 1733569565672 - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.16.2-pyhd8ed1ab_0.conda sha256: 471ed4bf7811b7a6ede1fd9a6c281cd07139b0b5694b3a18fe21a8d4bee032c7 md5: 78633141b91d2910b08fac6458e0ddb1 depends: - absl-py >=0.4 - grpcio >=1.48.2 - markdown >=2.6.8 - numpy >=1.12.0 - protobuf >=3.19.6,!=4.24.0 - python >=3.8 - setuptools >=41.0.0 - six >=1.9 - tensorboard-data-server >=0.7.0,<0.8.0 - werkzeug >=1.0.1 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tensorboard?source=hash-mapping size: 5174472 timestamp: 1708285949271 - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.18.0-pyhd8ed1ab_1.conda sha256: 769d8c95b691586ee197845c52a3c7e6a9be7b3008650a73794bc7727b6831ac md5: 90a735b377427589728e588b5241a253 depends: - absl-py >=0.4 - grpcio >=1.48.2 - markdown >=2.6.8 - numpy >=1.12.0 - packaging - protobuf >=3.19.6,!=4.24.0 - python >=3.9 - setuptools >=41.0.0 - six >1.9 - tensorboard-data-server >=0.7.0,<0.8.0 - werkzeug >=1.0.1 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tensorboard?source=hash-mapping size: 5188442 timestamp: 1733820813359 - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.19.0-pyhd8ed1ab_0.conda sha256: 348a60f31eb14fd2e10f00d3c7b5036981b9b2d957cf56b1c9fecbfa6c0782d5 md5: 59cc119240f48dc05369fa8bea07ea05 depends: - absl-py >=0.4 - grpcio >=1.48.2 - markdown >=2.6.8 - numpy >=1.12.0 - packaging - protobuf >=3.19.6,!=4.24.0 - python >=3.9 - setuptools >=41.0.0 - six >1.9 - tensorboard-data-server >=0.7.0,<0.8.0 - werkzeug >=1.0.1 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tensorboard?source=hash-mapping size: 5188674 timestamp: 1740522550333 - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.3.0-pyhd8ed1ab_0.conda sha256: 2107f959cd2a8a804d52e124434088e1a28c843942b62a7f8a3d84072861f0ef md5: bc6228906129e420c74a5ebaf0d63936 depends: - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/termcolor?source=hash-mapping size: 13259 timestamp: 1767096412722 - conda: https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyhc90fa1f_1.conda sha256: 6b6727a13d1ca6a23de5e6686500d0669081a117736a87c8abf444d60c1e40eb md5: 17b43cee5cc84969529d5d0b0309b2cb depends: - __unix - ptyprocess - python >=3.10 - tornado >=6.1.0 - python license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/terminado?source=hash-mapping size: 24749 timestamp: 1766513766867 - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-2.0.0-pyh5ca1d4c_0.tar.bz2 sha256: 9e02a7b85c7d103eee186a7d3f4ec2cb7005493fed7a9ae0b7dde6cec3f77f69 md5: 884543801746a703120fb16cbe1a483b depends: - python >=3.5 license: BSD 3-Clause license_family: BSD purls: - pkg:pypi/threadpoolctl?source=hash-mapping size: 14580 timestamp: 1582658451382 - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda sha256: 6016672e0e72c4cf23c0cf7b1986283bd86a9c17e8d319212d78d8e9ae42fdfd md5: 9d64911b31d57ca443e9f1e36b04385f depends: - python >=3.9 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/threadpoolctl?source=hash-mapping size: 23869 timestamp: 1741878358548 - conda: https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda sha256: cad582d6f978276522f84bd209a5ddac824742fe2d452af6acf900f8650a73a2 md5: f1acf5fdefa8300de697982bcb1761c9 depends: - python >=3.5 - webencodings >=0.4 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/tinycss2?source=hash-mapping size: 28285 timestamp: 1729802975370 - conda: https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhcf101f3_3.conda sha256: fd30e43699cb22ab32ff3134d3acf12d6010b5bbaa63293c37076b50009b91f8 md5: d0fc809fa4c4d85e959ce4ab6e1de800 depends: - python >=3.10 - python license: MIT license_family: MIT purls: - pkg:pypi/toml?source=hash-mapping size: 24017 timestamp: 1764486833072 - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda sha256: 91cafdb64268e43e0e10d30bd1bef5af392e69f00edd34dfaf909f69ab2da6bd md5: b5325cf06a000c5b14970462ff5e4d58 depends: - python >=3.10 - python license: MIT license_family: MIT purls: - pkg:pypi/tomli?source=hash-mapping size: 21561 timestamp: 1774492402955 - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.15.1-pyhcf101f3_0.conda sha256: b89a823edf524956b94a2a4db974866e4501f05c68976eff458c5dcf07f88431 md5: 37e3be7b6e2977d37b8fa5da229f5dc0 depends: - python >=3.10 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/traitlets?source=compressed-mapping size: 115158 timestamp: 1780507822178 - conda: https://conda.anaconda.org/conda-forge/noarch/twine-6.2.0-pyhcf101f3_0.conda sha256: 0370098cab22773e33755026bf78539c2f05645fce7dcc9713d01e21950756bb md5: 901a86453fa6183e914b937643619a03 depends: - id - importlib-metadata >=3.6 - keyring >=21.2.0 - packaging >=24.0 - python >=3.10 - readme_renderer >=35.0 - requests >=2.20 - requests-toolbelt >=0.8.0,!=0.9.0 - rfc3986 >=1.4.0 - rich >=12.0.0 - urllib3 >=1.26.0 - python license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/twine?source=hash-mapping size: 42488 timestamp: 1757013705407 - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda sha256: 7c2df5721c742c2a47b2c8f960e718c930031663ac1174da67c1ed5999f7938c md5: edd329d7d3a4ab45dcf905899a7a6115 depends: - typing_extensions ==4.15.0 pyhcf101f3_0 license: PSF-2.0 license_family: PSF purls: [] size: 91383 timestamp: 1756220668932 - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda sha256: 032271135bca55aeb156cee361c81350c6f3fb203f57d024d7e5a1fc9ef18731 md5: 0caa1af407ecff61170c9437a808404d depends: - python >=3.10 - python license: PSF-2.0 license_family: PSF purls: - pkg:pypi/typing-extensions?source=hash-mapping size: 51692 timestamp: 1756220668932 - conda: https://conda.anaconda.org/conda-forge/noarch/typing_utils-0.1.0-pyhd8ed1ab_1.conda sha256: 3088d5d873411a56bf988eee774559335749aed6f6c28e07bf933256afb9eb6c md5: f6d7aa696c67756a650e91e15e88223c depends: - python >=3.9 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/typing-utils?source=hash-mapping size: 15183 timestamp: 1733331395943 - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-hc9c84f9_1.conda sha256: 1d30098909076af33a35017eed6f2953af1c769e273a0626a04722ac4acaba3c md5: ad659d0a2b3e47e38d829aa8cad2d610 license: LicenseRef-Public-Domain purls: [] size: 119135 timestamp: 1767016325805 - conda: https://conda.anaconda.org/conda-forge/noarch/uri-template-1.3.0-pyhd8ed1ab_1.conda sha256: e0eb6c8daf892b3056f08416a96d68b0a358b7c46b99c8a50481b22631a4dfc0 md5: e7cb0f5745e4c5035a460248334af7eb depends: - python >=3.9 license: MIT license_family: MIT purls: - pkg:pypi/uri-template?source=hash-mapping size: 23990 timestamp: 1733323714454 - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.7.0-pyhd8ed1ab_0.conda sha256: feff959a816f7988a0893201aa9727bbb7ee1e9cec2c4f0428269b489eb93fb4 md5: cbb88288f74dbe6ada1c6c7d0a97223e depends: - backports.zstd >=1.0.0 - brotli-python >=1.2.0 - h2 >=4,<5 - pysocks >=1.5.6,<2.0,!=1.5.7 - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/urllib3?source=hash-mapping size: 103560 timestamp: 1778188657149 - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-21.4.2-pyhcf101f3_0.conda sha256: 83d10be1b436fef778e7982f24a1f117b7aa34817135ec8b0ad63ee4455943eb md5: 52434c636a05a06806d0978128d98c19 depends: - python >=3.10 - distlib >=0.3.7,<1 - filelock <4,>=3.24.2 - importlib-metadata >=6.6 - platformdirs >=3.9.1,<5 - python-discovery >=1.4 - typing_extensions >=4.13.2 - python license: MIT license_family: MIT purls: - pkg:pypi/virtualenv?source=compressed-mapping size: 5151645 timestamp: 1780253977667 - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.8.0-pyhd8ed1ab_0.conda sha256: 017f6b2777b3959f82ae1b04024569f7ed22928e58246be17e26a36ff6c85d52 md5: 65a429ac9a8352c7ba890eb326bbfaf5 depends: - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/wcwidth?source=compressed-mapping size: 136960 timestamp: 1780736911642 - conda: https://conda.anaconda.org/conda-forge/noarch/webcolors-25.10.0-pyhd8ed1ab_0.conda sha256: 21f6c8a20fe050d09bfda3fb0a9c3493936ce7d6e1b3b5f8b01319ee46d6c6f6 md5: 6639b6b0d8b5a284f027a2003669aa65 depends: - python >=3.10 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/webcolors?source=hash-mapping size: 18987 timestamp: 1761899393153 - conda: https://conda.anaconda.org/conda-forge/noarch/webencodings-0.5.1-pyhd8ed1ab_3.conda sha256: 19ff205e138bb056a46f9e3839935a2e60bd1cf01c8241a5e172a422fed4f9c6 md5: 2841eb5bfc75ce15e9a0054b98dcd64d depends: - python >=3.9 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/webencodings?source=hash-mapping size: 15496 timestamp: 1733236131358 - conda: https://conda.anaconda.org/conda-forge/noarch/websocket-client-1.9.0-pyhd8ed1ab_0.conda sha256: 42a2b61e393e61cdf75ced1f5f324a64af25f347d16c60b14117393a98656397 md5: 2f1ed718fcd829c184a6d4f0f2e07409 depends: - python >=3.10 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/websocket-client?source=hash-mapping size: 61391 timestamp: 1759928175142 - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.8-pyhcf101f3_0.conda sha256: 5108c1bf2f0512e5c9dc8191e31b144c23b7cf0e73423a246173006002368d79 md5: 3eeca039e7316268627f4116da9df64c depends: - markupsafe >=2.1.1 - python >=3.10 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/werkzeug?source=hash-mapping size: 258232 timestamp: 1775160081069 - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.47.0-pyhd8ed1ab_0.conda sha256: 9e156ffaefb8463437144326ada4b85d1de17961b9997ac5f1cbbaf747bd8bed md5: d0e3b2f0030cf4fca58bde71d246e94c depends: - packaging >=24.0 - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/wheel?source=hash-mapping size: 33491 timestamp: 1776878563806 - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-4.1.0-pyhcf101f3_0.conda sha256: 210bd31c22bb88f5e2a167df24c95bb5f152b2ada7502f9b8c49d1f5366db423 md5: ba3dcdc8584155c97c648ae9c044b7a3 depends: - python >=3.10 - python license: MIT license_family: MIT purls: - pkg:pypi/zipp?source=compressed-mapping size: 24190 timestamp: 1779159948016 - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda build_number: 7 sha256: 30006902a9274de8abdad5a9f02ef7c8bb3d69a503486af0c1faee30b023e5b7 md5: eaac87c21aff3ed21ad9656697bb8326 depends: - llvm-openmp >=9.0.1 license: BSD-3-Clause license_family: BSD purls: [] size: 8328 timestamp: 1764092562779 - conda: https://conda.anaconda.org/conda-forge/osx-64/argon2-cffi-bindings-25.1.0-py311hf197a57_2.conda sha256: 4e54d6fef36a1954f5289aa0b5f01b6ae6f0b4f37bb77e644635f8e085d7e4dc md5: 32981d5201015f7391caa145a6c75c7d depends: - __osx >=10.13 - cffi >=1.0.1 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/argon2-cffi-bindings?source=hash-mapping size: 33645 timestamp: 1762509779250 - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-auth-0.10.3-h32d7887_1.conda sha256: 4cc41a089965cdfd7fddc3228bb8b35cc9be0fe1c02c72f0c1acb426eecf765d md5: 4bc3c0f49068e7503c7d3e7621a75967 depends: - __osx >=11.0 - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 - aws-c-io >=0.26.3,<0.26.4.0a0 - aws-c-common >=0.13.1,<0.13.2.0a0 - aws-c-http >=0.11.0,<0.11.1.0a0 - aws-c-cal >=0.9.14,<0.9.15.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 120730 timestamp: 1779877686667 - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-cal-0.9.14-hf143bd1_1.conda sha256: cd186f4b13be350b616352de5b771d6fbab65f37f1e48b2661118fd86db8769b md5: 20bdb3984b9537bb2fc710db02ea7740 depends: - __osx >=11.0 - aws-c-common >=0.13.1,<0.13.2.0a0 license: Apache-2.0 license_family: Apache purls: [] size: 45785 timestamp: 1779822391033 - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-common-0.13.1-ha1e9b39_0.conda sha256: 221d095f477894caf67f11547f59d8869e1cf8a6d3ffe2e52e3aa06d854402cd md5: 145897116d174f9ec2dae47040d9ad0f depends: - __osx >=11.0 license: Apache-2.0 license_family: Apache purls: [] size: 233190 timestamp: 1779303084466 - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-compression-0.3.2-h36b48e2_1.conda sha256: d2855fcfbf0406311b80f0336941fc702569a4bf5b2abbf9d9a8a49637ed18b2 md5: 3509d319cc218dcd23af68508dc7fe30 depends: - __osx >=11.0 - aws-c-common >=0.13.1,<0.13.2.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 21500 timestamp: 1779463005554 - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-http-0.11.0-h2d71af0_1.conda sha256: 06ede0d99387d5b3b4c7844983ddd69489932ecea7765fd673ba515664da231c md5: f4ab6693384d36f28f343f4189c3e96d depends: - __osx >=11.0 - aws-c-cal >=0.9.14,<0.9.15.0a0 - aws-c-compression >=0.3.2,<0.3.3.0a0 - aws-c-common >=0.13.1,<0.13.2.0a0 - aws-c-io >=0.26.3,<0.26.4.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 197080 timestamp: 1779871012037 - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-io-0.26.3-h7049b4b_3.conda sha256: bcfbca9179f1684cb1ccdae195d2d6abc599e46989605c10cbed8a4d10bf1af7 md5: d38398f6b0bbd31f42ad5f03d4befdad depends: - __osx >=11.0 - aws-c-cal >=0.9.14,<0.9.15.0a0 - aws-c-common >=0.13.1,<0.13.2.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 182733 timestamp: 1779864654480 - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-s3-0.12.4-h8390798_0.conda sha256: ddd645708c222e0ab186e590ee49119eae2713d998b84bb16d9fda43d5bcd152 md5: 06b0a4cca64d3ba908717e59334e14fd depends: - __osx >=11.0 - aws-c-common >=0.13.1,<0.13.2.0a0 - aws-c-io >=0.26.3,<0.26.4.0a0 - aws-c-http >=0.11.0,<0.11.1.0a0 - aws-c-cal >=0.9.14,<0.9.15.0a0 - aws-checksums >=0.2.10,<0.2.11.0a0 - aws-c-auth >=0.10.3,<0.10.4.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 135722 timestamp: 1779887901967 - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-c-sdkutils-0.2.4-h36b48e2_5.conda sha256: 92d1a560bfd51ad96f026f479dc9c27a8bfd5f06619c1c95a856c767dda70b23 md5: cef8af6fe37a833f1cfd5b82cd41b619 depends: - __osx >=11.0 - aws-c-common >=0.13.1,<0.13.2.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 55962 timestamp: 1779464629742 - conda: https://conda.anaconda.org/conda-forge/osx-64/aws-checksums-0.2.10-h36b48e2_1.conda sha256: f7958f93129da751be3a5fd6ac358c1135e0696c628f15e1bebf231b822aa267 md5: d64770af452bbffec2950a9d81975608 depends: - __osx >=11.0 - aws-c-common >=0.13.1,<0.13.2.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 95608 timestamp: 1779464786806 - conda: https://conda.anaconda.org/conda-forge/osx-64/backports.zstd-1.5.0-py310h3e16e02_0.conda sha256: e4a15c6005256f84beb46d51669094c1b4345c272847f50bcf10ee85588734dc md5: 9ffb513b7c501f25c4fbf0a8333a5a66 depends: - python - __osx >=11.0 - python_abi 3.10.* *_cp310 - zstd >=1.5.7,<1.6.0a0 license: BSD-3-Clause AND MIT AND EPL-2.0 purls: - pkg:pypi/backports-zstd?source=hash-mapping size: 192807 timestamp: 1778594191475 - conda: https://conda.anaconda.org/conda-forge/osx-64/backports.zstd-1.5.0-py311h1fd9c89_0.conda sha256: 762f85d94aac2c01af440f51efa7ca3a6ac8e33d2d8dacee405585196a7b0310 md5: c950354cc6d69b3e126e4090429aa123 depends: - python - __osx >=11.0 - zstd >=1.5.7,<1.6.0a0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause AND MIT AND EPL-2.0 purls: - pkg:pypi/backports-zstd?source=hash-mapping size: 246860 timestamp: 1778594104556 - conda: https://conda.anaconda.org/conda-forge/osx-64/backports.zstd-1.5.0-py312h5f4ecc6_0.conda sha256: f6166347ee8dd7e5c71029c27e89cc5d4a84ccb3f374761363d5bce08ce92227 md5: c987aba92ae6e528ed495f1ea71d4834 depends: - python - __osx >=11.0 - python_abi 3.12.* *_cp312 - zstd >=1.5.7,<1.6.0a0 license: BSD-3-Clause AND MIT AND EPL-2.0 purls: - pkg:pypi/backports-zstd?source=hash-mapping size: 240621 timestamp: 1778594129022 - conda: https://conda.anaconda.org/conda-forge/osx-64/black-23.3.0-py311h6eed73b_1.conda sha256: 78b007b394be2e0ddd42224970be1b212639a8941fee0e7d724986155e517e24 md5: 2783c68e84c0573fece0880488c7c001 depends: - click >=8.0.0 - mypy_extensions >=0.4.3 - packaging >=22.0 - pathspec >=0.9 - platformdirs >=2 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/black?source=hash-mapping size: 355441 timestamp: 1682492328351 - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-1.2.0-hf139dec_1.conda sha256: c838c71ded28ada251589f6462fc0f7c09132396799eea2701277566a1a863bf md5: 149d8ee7d6541a02a6117d8814fd9413 depends: - __osx >=10.13 - brotli-bin 1.2.0 h8616949_1 - libbrotlidec 1.2.0 h8616949_1 - libbrotlienc 1.2.0 h8616949_1 license: MIT license_family: MIT purls: [] size: 20194 timestamp: 1764017661405 - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-bin-1.2.0-h8616949_1.conda sha256: dcb5a2b29244b82af2545efad13dfdf8dddb86f88ce64ff415be9e7a10cc0383 md5: 34803b20dfec7af32ba675c5ccdbedbf depends: - __osx >=10.13 - libbrotlidec 1.2.0 h8616949_1 - libbrotlienc 1.2.0 h8616949_1 license: MIT license_family: MIT purls: [] size: 18589 timestamp: 1764017635544 - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-python-1.2.0-py310hab27952_1.conda sha256: 40a9f24620cb3ce71956b287f77e01c5b2668ff97b967f5a0d42e54331c0f3d0 md5: fdf6c61fb14f19c006d068cb146a219d depends: - __osx >=10.13 - libcxx >=19 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 constrains: - libbrotlicommon 1.2.0 h8616949_1 license: MIT license_family: MIT purls: - pkg:pypi/brotli?source=hash-mapping size: 389600 timestamp: 1764017722648 - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-python-1.2.0-py311h7e844b6_1.conda sha256: 292026d98fd60bb25852792e2fd6ee97be35515057cfe258416ea6e1998e3564 md5: ae49e04114f7f1673920fdbf326a047f depends: - __osx >=10.13 - libcxx >=19 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 constrains: - libbrotlicommon 1.2.0 h8616949_1 license: MIT license_family: MIT purls: - pkg:pypi/brotli?source=hash-mapping size: 389997 timestamp: 1764017848151 - conda: https://conda.anaconda.org/conda-forge/osx-64/brotli-python-1.2.0-py312h4b46afd_1.conda sha256: 8854a80360128157e8d05eb57c1c7e7c1cb10977e4c4557a77d29c859d1f104b md5: 01fdbccc39e0a7698e9556e8036599b7 depends: - __osx >=10.13 - libcxx >=19 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 constrains: - libbrotlicommon 1.2.0 h8616949_1 license: MIT license_family: MIT purls: - pkg:pypi/brotli?source=hash-mapping size: 389534 timestamp: 1764017976737 - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_9.conda sha256: 9f242f13537ef1ce195f93f0cc162965d6cc79da578568d6d8e50f70dd025c42 md5: 4173ac3b19ec0a4f400b4f782910368b depends: - __osx >=10.13 license: bzip2-1.0.6 license_family: BSD purls: [] size: 133427 timestamp: 1771350680709 - conda: https://conda.anaconda.org/conda-forge/osx-64/c-ares-1.34.6-hb5e19a0_0.conda sha256: 2f5bc0292d595399df0d168355b4e9820affc8036792d6984bd751fdda2bcaea md5: fc9a153c57c9f070bebaa7eef30a8f17 depends: - __osx >=10.13 license: MIT license_family: MIT purls: [] size: 186122 timestamp: 1765215100384 - conda: https://conda.anaconda.org/conda-forge/osx-64/cffi-2.0.0-py311h26bcf6e_1.conda sha256: 5519d7a6fb4709454971a9212105b40d95b44b0f1f37ccc2112f45368bfa61b4 md5: 9a3f0928baf6f2754d9d437984c79b00 depends: - __osx >=10.13 - libffi >=3.5.2,<3.6.0a0 - pycparser - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/cffi?source=hash-mapping size: 295108 timestamp: 1761203293287 - conda: https://conda.anaconda.org/conda-forge/osx-64/cmarkgfm-2024.11.20-py311h13e5629_1.conda sha256: 43f635383c8e3e1918b6e17ce02e240dbda0ecc96b91c5d7f6c5eeeff6a6b4fc md5: 078bd61976ee4b1eb7b90d5ff6b808ce depends: - __osx >=10.13 - cffi >=1.0.0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/cmarkgfm?source=hash-mapping size: 122757 timestamp: 1760363212602 - conda: https://conda.anaconda.org/conda-forge/osx-64/contourpy-1.3.3-py311h9d13916_4.conda sha256: 4990e8c2418f07b787d764aee3171df4a1c7681050d00e10b4433eed3b997e9d md5: 12f0c1059f868ede7bd3394a1959ae0f depends: - numpy >=1.25 - python - libcxx >=19 - __osx >=10.13 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/contourpy?source=hash-mapping size: 296923 timestamp: 1769156062903 - conda: https://conda.anaconda.org/conda-forge/osx-64/contourpy-1.3.3-py312hb0c38da_4.conda sha256: 6c03943009b07c6deb3a64afa094b6ca694062b58127a4da6f656a13d508c340 md5: 625f08687ba33cc9e57865e7bf8e8123 depends: - numpy >=1.25 - python - __osx >=10.13 - libcxx >=19 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/contourpy?source=hash-mapping size: 298198 timestamp: 1769156053873 - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.1-py310h399bfa0_0.conda sha256: 58b27687c55ea12d9a6a8b80c8e0fb0457ff8db0ef3e2a442f972339731c1cd5 md5: c42f13916bb2ea9bb93b126681997909 depends: - __osx >=11.0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - tomli license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/coverage?source=hash-mapping size: 313710 timestamp: 1779838468314 - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.1-py311ha8ae342_0.conda sha256: 286d4d6bf016f56d71f58c2dd495410213fb5e4053a0545d48638bfd346683bc md5: 145a45fb204a76aac34696a6fac42ece depends: - __osx >=11.0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - tomli license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/coverage?source=hash-mapping size: 396933 timestamp: 1779838320779 - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.1-py312heb39f77_0.conda sha256: 5fa71aeb0be43ccf8f5b5ac4102efd23d22e44d4e713e802251d5456a90bb62c md5: 0b1ec37d2e54ba95731d07e273228789 depends: - __osx >=11.0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - tomli license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/coverage?source=hash-mapping size: 388114 timestamp: 1779838145107 - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.14.1-py314h77fa6c7_0.conda sha256: 29db019fee55fe7709db55c65f8919ab8f10ece710b149b7a4648cc86c95b938 md5: 0b15b52281394a1b864c5192c845e49d depends: - __osx >=11.0 - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 - tomli license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/coverage?source=hash-mapping size: 414951 timestamp: 1779838238137 - conda: https://conda.anaconda.org/conda-forge/osx-64/debugpy-1.8.21-py311hc3ea09e_0.conda sha256: e64eee6cdfcd3c6ce2fc2f04244ea70a144b9cbdde8ff40c05eefff61e6686a9 md5: 08e0b0baab92a971f491965dbc6ea4d3 depends: - python - libcxx >=19 - __osx >=11.0 - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/debugpy?source=compressed-mapping size: 2678681 timestamp: 1780390210234 - conda: https://conda.anaconda.org/conda-forge/osx-64/flatbuffers-24.12.23-hd70532e_0.conda sha256: 3d3d9d242c5ad60303637cbc858aa78c9a67ce94f82b740a56c2952f9084773e md5: d4b89a999846df9400075aeffb83ed17 depends: - __osx >=10.13 - libcxx >=18 license: Apache-2.0 license_family: APACHE purls: [] size: 1341374 timestamp: 1736587317636 - conda: https://conda.anaconda.org/conda-forge/osx-64/flatbuffers-24.3.25-h73e2aa4_0.conda sha256: 3648f1822b69a4212b368b0cfa266a77accaeb60794af2b90e4178c263b2abda md5: 728a9638664ac180b3d970b3d04cf218 depends: - libcxx >=16 license: Apache-2.0 license_family: APACHE purls: [] size: 1330937 timestamp: 1711467455228 - conda: https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.63.0-py311ha8ae342_0.conda sha256: c2639b42d3053ebb001a55711896e5abafa5d007d7443d3cfed48f685d2b2ab1 md5: 4112a45453836123e6448c77adcb3ce4 depends: - __osx >=11.0 - brotli - munkres - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - unicodedata2 >=15.1.0 license: MIT license_family: MIT purls: - pkg:pypi/fonttools?source=hash-mapping size: 2989716 timestamp: 1778770848075 - conda: https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.63.0-py312heb39f77_0.conda sha256: eb76350b1653a7e6e61c0fdc7dde79e32a0ba662c6c9471b2557720fb7db802d md5: 86857c4f51ca02be0aa72fb98ea80ef9 depends: - __osx >=11.0 - brotli - munkres - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - unicodedata2 >=15.1.0 license: MIT license_family: MIT purls: - pkg:pypi/fonttools?source=hash-mapping size: 2914614 timestamp: 1778770861388 - conda: https://conda.anaconda.org/conda-forge/osx-64/freetype-2.14.3-h694c41f_0.conda sha256: 5ddd46a88a0b6483e3dec52cabb62414504c94ee0e77369a4717f61a656c535a md5: 6ab1403cc6cb284d56d0464f19251075 depends: - libfreetype 2.14.3 h694c41f_0 - libfreetype6 2.14.3 h58fbd8d_0 license: GPL-2.0-only OR FTL purls: [] size: 174060 timestamp: 1774298809296 - conda: https://conda.anaconda.org/conda-forge/osx-64/giflib-5.2.2-h10d778d_0.conda sha256: 2c825df829097536314a195ae5cacaa8695209da6b4400135a65d8e23c008ff8 md5: 03e8c9b4d3da5f3d6eabdd020c2d63ac license: MIT license_family: MIT purls: [] size: 74516 timestamp: 1712692686914 - conda: https://conda.anaconda.org/conda-forge/osx-64/grpcio-1.62.2-py310h271164d_0.conda sha256: b2300b5295667f374c345d2a86fd98cc074f63fc1866f75ba1c9905c23c63846 md5: 85ee004347022bdcdfcd074f6f8047c9 depends: - __osx >=10.13 - libcxx >=16 - libgrpc 1.62.2 h384b2fc_0 - libzlib >=1.2.13,<2.0.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/grpcio?source=hash-mapping size: 947303 timestamp: 1713393021339 - conda: https://conda.anaconda.org/conda-forge/osx-64/grpcio-1.67.1-py311ha489736_2.conda sha256: 9a57648471ff1c2bb77bfdef180097d5d35e10d0e1927357a5933a8217bafb40 md5: f153d95f01531504b1cd18d65c67ef55 depends: - __osx >=10.13 - libcxx >=18 - libgrpc 1.67.1 h4896ac0_2 - libzlib >=1.3.1,<2.0a0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/grpcio?source=hash-mapping size: 832226 timestamp: 1740799704424 - conda: https://conda.anaconda.org/conda-forge/osx-64/grpcio-1.67.1-py312h145213c_2.conda sha256: 0a038d553c366f6177b4864603be7bce067c9202b32f9bd4fab5388730a69509 md5: 5cde66c6535f44e6d0279d0731906d06 depends: - __osx >=10.13 - libcxx >=18 - libgrpc 1.67.1 h4896ac0_2 - libzlib >=1.3.1,<2.0a0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/grpcio?source=hash-mapping size: 827142 timestamp: 1740799253917 - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.16.0-nompi_py310h05ade02_102.conda sha256: d2b8d17218bc9eb98cf31a23a66542b37c8b3751c5d56d8f07a5d7da285fb2e5 md5: 3278edbbb6ecb6453b7de7fdb5eeb210 depends: - __osx >=11.0 - cached-property - hdf5 >=2.1.0,<3.0a0 - numpy >=1.21,<3 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/h5py?source=hash-mapping size: 1106896 timestamp: 1775581866927 - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.16.0-nompi_py311ha7e68cf_102.conda sha256: 0a4c211e80ffec89f568910b87790471319a59d9ab04db8e32b6d22f2eee351a md5: f29ebc1fb2896ab15bf2712cf530e843 depends: - __osx >=11.0 - cached-property - hdf5 >=2.1.0,<3.0a0 - numpy >=1.23,<3 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/h5py?source=hash-mapping size: 1196742 timestamp: 1775581895921 - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.16.0-nompi_py312h82c48cf_102.conda sha256: 705c25f8ef6e3795f4add3dd34a0563698b07f5a3d1c6c920d4279a15beb0841 md5: 52167ce7748139f6ec29d8aa86e0ec38 depends: - __osx >=11.0 - cached-property - hdf5 >=2.1.0,<3.0a0 - numpy >=1.23,<3 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/h5py?source=hash-mapping size: 1188195 timestamp: 1775581932391 - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-2.1.0-nompi_hf2f48e3_106.conda sha256: 10f289f4ac7854af063b4b090c77c791893e5f23c5165d1d1217612e495c37fc md5: 3f2a3042c826161c7f221d69df4cba3e depends: - __osx >=11.0 - aws-c-auth >=0.10.3,<0.10.4.0a0 - aws-c-common >=0.13.1,<0.13.2.0a0 - aws-c-http >=0.11.0,<0.11.1.0a0 - aws-c-io >=0.26.3,<0.26.4.0a0 - aws-c-s3 >=0.12.4,<0.12.5.0a0 - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 - libaec >=1.1.5,<2.0a0 - libcurl >=8.20.0,<9.0a0 - libcxx >=19 - libgfortran - libgfortran5 >=14.3.0 - libzlib >=1.3.2,<2.0a0 - openssl >=3.5.6,<4.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 3882363 timestamp: 1780578781356 - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-73.2-hf5e326d_0.conda sha256: f66362dc36178ac9b7c7a9b012948a9d2d050b3debec24bbd94aadbc44854185 md5: 5cc301d759ec03f28328428e28f65591 license: MIT license_family: MIT purls: [] size: 11787527 timestamp: 1692901622519 - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-75.1-h120a0e1_0.conda sha256: 2e64307532f482a0929412976c8450c719d558ba20c0962832132fd0d07ba7a7 md5: d68d48a3060eb5abdc1cdc8e2a3a5966 depends: - __osx >=10.13 license: MIT license_family: MIT purls: [] size: 11761697 timestamp: 1720853679409 - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.3-h25d91c4_0.conda sha256: 1294117122d55246bb83ad5b589e2a031aacdf2d0b1f99fd338aa4394f881735 md5: 627eca44e62e2b665eeec57a984a7f00 depends: - __osx >=11.0 license: MIT license_family: MIT purls: [] size: 12273764 timestamp: 1773822733780 - conda: https://conda.anaconda.org/conda-forge/osx-64/kiwisolver-1.5.0-py311h2886188_0.conda sha256: a1ede405c8ad3ec2bb52e226507653329c52245fd61584dda28df52b3204b876 md5: 3a855eedf75caa9ac24240b1304fa058 depends: - python - libcxx >=19 - __osx >=11.0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/kiwisolver?source=hash-mapping size: 67881 timestamp: 1773067263173 - conda: https://conda.anaconda.org/conda-forge/osx-64/kiwisolver-1.5.0-py312hb1dc2e7_0.conda sha256: 6ab69d441b3400cdf773f67e20f9fae7c37f076d32c31d06843f12d2099e70ce md5: 4a38b6e74b5a7ea22f1840226e5103a8 depends: - python - libcxx >=19 - __osx >=11.0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/kiwisolver?source=hash-mapping size: 69432 timestamp: 1773067281295 - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.22.2-h207b36a_0.conda sha256: df009385e8262c234c0dae9016540b86dad3d299f0d9366d08e327e8e7731634 md5: e66e2c52d2fdddcf314ad750fb4ebb4a depends: - __osx >=10.13 - libcxx >=19 - libedit >=3.1.20250104,<3.2.0a0 - libedit >=3.1.20250104,<4.0a0 - openssl >=3.5.5,<4.0a0 license: MIT license_family: MIT purls: [] size: 1193620 timestamp: 1769770267475 - conda: https://conda.anaconda.org/conda-forge/osx-64/lcms2-2.19.1-h5ea7634_1.conda sha256: 8bae1207dc7cf0e670ae920a549b1d55486514213ca808b8119067cbad0db43a md5: f8c168eefc1f75ada2e2cd8f2e6212f5 depends: - __osx >=11.0 - libjpeg-turbo >=3.1.4.1,<4.0a0 - libtiff >=4.7.1,<4.8.0a0 license: MIT license_family: MIT purls: [] size: 229477 timestamp: 1780211969520 - conda: https://conda.anaconda.org/conda-forge/osx-64/lerc-4.1.0-h35c7297_0.conda sha256: f918716c71c8bebbc0c40e1050878aa512fea92c1d17c363ca35650bc60f6c35 md5: d2fe7e177d1c97c985140bd54e2a5e33 depends: - __osx >=11.0 - libcxx >=19 license: Apache-2.0 license_family: Apache purls: [] size: 215089 timestamp: 1773114468701 - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20240116.2-cxx17_hf036a51_1.conda sha256: 396d18f39d5207ecae06fddcbc6e5f20865718939bc4e0ea9729e13952833aac md5: d6c78ca84abed3fea5f308ac83b8f54e depends: - __osx >=10.13 - libcxx >=16 constrains: - abseil-cpp =20240116.2 - libabseil-static =20240116.2=cxx17* license: Apache-2.0 license_family: Apache purls: [] size: 1124364 timestamp: 1720857589333 - conda: https://conda.anaconda.org/conda-forge/osx-64/libabseil-20240722.0-cxx17_h0e468a2_4.conda sha256: 375e98c007cbe2535b89adccf4d417480d54ce2fb4b559f0b700da294dee3985 md5: 03dd3d0563d01c2b82881734ee0eb334 depends: - __osx >=10.13 - libcxx >=18 constrains: - abseil-cpp =20240722.0 - libabseil-static =20240722.0=cxx17* license: Apache-2.0 license_family: Apache purls: [] size: 1163503 timestamp: 1736008705613 - conda: https://conda.anaconda.org/conda-forge/osx-64/libaec-1.1.5-he7c3a48_0.conda sha256: b42ac9c684c730cb97cb3931a0a97aaf791da38bace4f6944eca10de609e5946 md5: 975f98248cde8d54884c6d1eb5184e13 depends: - __osx >=10.13 - libcxx >=19 license: BSD-2-Clause license_family: BSD purls: [] size: 30555 timestamp: 1769222189944 - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-8_he492b99_openblas.conda build_number: 8 sha256: 55cf9f92a2d07c33f8a32c44ff1528ea48fd69677cc003a4532d09b71cb8a316 md5: 7da1e8ab7c4498db9457c191d82930a3 depends: - libopenblas >=0.3.33,<0.3.34.0a0 - libopenblas >=0.3.33,<1.0a0 constrains: - mkl <2027 - blas 2.308 openblas - liblapacke 3.11.0 8*_openblas - libcblas 3.11.0 8*_openblas - liblapack 3.11.0 8*_openblas license: BSD-3-Clause license_family: BSD purls: [] size: 19048 timestamp: 1779860008916 - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.9.0-20_osx64_openblas.conda build_number: 20 sha256: 89cac4653b52817d44802d96c13e5f194320e2e4ea805596641d0f3e22e32525 md5: 1673476d205d14a9042172be795f63cb depends: - libopenblas >=0.3.25,<0.3.26.0a0 - libopenblas >=0.3.25,<1.0a0 constrains: - blas * openblas - liblapack 3.9.0 20_osx64_openblas - liblapacke 3.9.0 20_osx64_openblas - libcblas 3.9.0 20_osx64_openblas license: BSD-3-Clause license_family: BSD purls: [] size: 14739 timestamp: 1700568675962 - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlicommon-1.2.0-h8616949_1.conda sha256: 4c19b211b3095f541426d5a9abac63e96a5045e509b3d11d4f9482de53efe43b md5: f157c098841474579569c85a60ece586 depends: - __osx >=10.13 license: MIT license_family: MIT purls: [] size: 78854 timestamp: 1764017554982 - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlidec-1.2.0-h8616949_1.conda sha256: 729158be90ae655a4e0427fe4079767734af1f9b69ff58cf94ca6e8d4b3eb4b7 md5: 63186ac7a8a24b3528b4b14f21c03f54 depends: - __osx >=10.13 - libbrotlicommon 1.2.0 h8616949_1 license: MIT license_family: MIT purls: [] size: 30835 timestamp: 1764017584474 - conda: https://conda.anaconda.org/conda-forge/osx-64/libbrotlienc-1.2.0-h8616949_1.conda sha256: 8ece7b41b6548d6601ac2c2cd605cf2261268fc4443227cc284477ed23fbd401 md5: 12a58fd3fc285ce20cf20edf21a0ff8f depends: - __osx >=10.13 - libbrotlicommon 1.2.0 h8616949_1 license: MIT license_family: MIT purls: [] size: 310355 timestamp: 1764017609985 - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-8_h9b27e0a_openblas.conda build_number: 8 sha256: 50eb650a17a34ea45fe2b31e60a98632d1f8c203308014dcef93043d54612482 md5: 4f116127b172bbba835c1e0491efd86f depends: - libblas 3.11.0 8_he492b99_openblas constrains: - liblapacke 3.11.0 8*_openblas - blas 2.308 openblas - liblapack 3.11.0 8*_openblas license: BSD-3-Clause license_family: BSD purls: [] size: 19049 timestamp: 1779860025163 - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.9.0-20_osx64_openblas.conda build_number: 20 sha256: b0a4eab6d22b865d9b0e39f358f17438602621709db66b8da159197bedd2c5eb md5: b324ad206d39ce529fb9073f9d062062 depends: - libblas 3.9.0 20_osx64_openblas constrains: - liblapack 3.9.0 20_osx64_openblas - liblapacke 3.9.0 20_osx64_openblas - blas * openblas license: BSD-3-Clause license_family: BSD purls: [] size: 14648 timestamp: 1700568722960 - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.20.0-h8f0b9e4_0.conda sha256: 5d3d8a82ca43347e96f1d79048921f3a7c25e32514bc7feb53ed2a040dcca54d md5: 4a0085ccf90dc514f0fc0909a874045e depends: - __osx >=11.0 - krb5 >=1.22.2,<1.23.0a0 - libnghttp2 >=1.68.1,<2.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.2,<2.0a0 - openssl >=3.5.6,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT purls: [] size: 419676 timestamp: 1777462238769 - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-22.1.7-h19cb2f5_0.conda sha256: c03c298355dea54b729ed6c5f1e6dbd0e2426906039eba8aa2ba1254d005b7d8 md5: 423373b842c3861da6cfa8c8915798ce depends: - __osx >=11.0 license: Apache-2.0 WITH LLVM-exception license_family: Apache purls: [] size: 564939 timestamp: 1780442565078 - conda: https://conda.anaconda.org/conda-forge/osx-64/libdeflate-1.25-h517ebb2_0.conda sha256: 025f8b1e85dd8254e0ca65f011919fb1753070eb507f03bca317871a884d24de md5: 31aa65919a729dc48180893f62c25221 depends: - __osx >=10.13 license: MIT license_family: MIT purls: [] size: 70840 timestamp: 1761980008502 - conda: https://conda.anaconda.org/conda-forge/osx-64/libedit-3.1.20250104-pl5321ha958ccf_0.conda sha256: 6cc49785940a99e6a6b8c6edbb15f44c2dd6c789d9c283e5ee7bdfedd50b4cd6 md5: 1f4ed31220402fcddc083b4bff406868 depends: - ncurses - __osx >=10.13 - ncurses >=6.5,<7.0a0 license: BSD-2-Clause license_family: BSD purls: [] size: 115563 timestamp: 1738479554273 - conda: https://conda.anaconda.org/conda-forge/osx-64/libev-4.33-h10d778d_2.conda sha256: 0d238488564a7992942aa165ff994eca540f687753b4f0998b29b4e4d030ff43 md5: 899db79329439820b7e8f8de41bca902 license: BSD-2-Clause license_family: BSD purls: [] size: 106663 timestamp: 1702146352558 - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.8.1-hcc62823_0.conda sha256: 460afe7ba0882e6d2fcc0ad1568dce27025110ec09c2b9ce9e3b49d61e52ce6b md5: f95dc08366f2a452005062b5bcceac51 depends: - __osx >=11.0 constrains: - expat 2.8.1.* license: MIT license_family: MIT purls: [] size: 75654 timestamp: 1779279058576 - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-hd1f9c09_0.conda sha256: 951958d1792238006fdc6fce7f71f1b559534743b26cc1333497d46e5903a2d6 md5: 66a0dc7464927d0853b590b6f53ba3ea depends: - __osx >=10.13 license: MIT license_family: MIT purls: [] size: 53583 timestamp: 1769456300951 - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype-2.14.3-h694c41f_0.conda sha256: b5daa4cee3beb98a0317e81a20aa507b9f897a9e21b11fe0b2e32852e372f746 md5: 63b822fcf984c891f0afab2eedfcfaf4 depends: - libfreetype6 >=2.14.3 license: GPL-2.0-only OR FTL purls: [] size: 8088 timestamp: 1774298785964 - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype6-2.14.3-h58fbd8d_0.conda sha256: 9d34b5b2be6ebdd3bcd9e21d6598d493afce4d3fcd2d419f3356022cb4d746fd md5: 27515b8ab8bf4abd8d3d90cf11212411 depends: - __osx >=11.0 - libpng >=1.6.55,<1.7.0a0 - libzlib >=1.3.2,<2.0a0 constrains: - freetype >=2.14.3 license: GPL-2.0-only OR FTL purls: [] size: 364828 timestamp: 1774298783922 - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_19.conda sha256: 17a5dcd818f89173db51d7d1acd77615cb77db7b4c2b5f571d4dafe559430ab5 md5: 4bf33d5ca73f4b89d3495285a42414a4 depends: - _openmp_mutex constrains: - libgomp 15.2.0 19 - libgcc-ng ==15.2.0=*_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 424164 timestamp: 1778271183296 - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_19.conda sha256: 519045363b87b870be779d38f0bfd325d4b787acdaa0a2136a92c1081eff5112 md5: d362f41203d0a1d2d4940446f95374c9 depends: - libgfortran5 15.2.0 hd16e46c_19 constrains: - libgfortran-ng ==15.2.0=*_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 139925 timestamp: 1778271458366 - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_19.conda sha256: c7f5f6e80357d6d5bc69588c16144205b0c79cf32cd090ccb5afef9d557632af md5: 1cddb3f7e54f5871297afc0fafa61c2c depends: - libgcc >=15.2.0 constrains: - libgfortran 15.2.0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 1063687 timestamp: 1778271196574 - conda: https://conda.anaconda.org/conda-forge/osx-64/libgrpc-1.62.2-h384b2fc_0.conda sha256: 7c228040e7dac4e5e7e6935a4decf6bc2155cc05fcfb0811d25ccb242d0036ba md5: 9421f67cf8b4bc976fe5d0c3ab42de18 depends: - __osx >=10.13 - c-ares >=1.28.1,<2.0a0 - libabseil * cxx17* - libabseil >=20240116.1,<20240117.0a0 - libcxx >=16 - libprotobuf >=4.25.3,<4.25.4.0a0 - libre2-11 >=2023.9.1 - libzlib >=1.2.13,<2.0.0a0 - openssl >=3.2.1,<4.0a0 - re2 constrains: - grpc-cpp =1.62.2 license: Apache-2.0 license_family: APACHE purls: [] size: 5189573 timestamp: 1713392887258 - conda: https://conda.anaconda.org/conda-forge/osx-64/libgrpc-1.67.1-h4896ac0_2.conda sha256: 1704fc25a408d89d5efd841ad0a3b42ba1a8b189afa40b89995c74da83058d91 md5: c1f24237a5024ae9b3820401511a1660 depends: - __osx >=10.13 - c-ares >=1.34.4,<2.0a0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - libcxx >=18 - libprotobuf >=5.28.3,<5.28.4.0a0 - libre2-11 >=2024.7.2 - libzlib >=1.3.1,<2.0a0 - openssl >=3.4.1,<4.0a0 - re2 constrains: - grpc-cpp =1.67.1 license: Apache-2.0 license_family: APACHE purls: [] size: 5204405 timestamp: 1740799079753 - conda: https://conda.anaconda.org/conda-forge/osx-64/libjpeg-turbo-3.1.4.1-ha1e9b39_0.conda sha256: 6b809d8acb6b97bbb1a858eb4ba7b7163c67257b6c3f199dd9d1e0751f4c5b18 md5: 57cc1464d457d01ac78f5860b9ca1714 depends: - __osx >=11.0 constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib purls: [] size: 587997 timestamp: 1775963139212 - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-8_h859234e_openblas.conda build_number: 8 sha256: 56a68fce5a63d4583a42c212324d62ac292376b8bf05986a551bd640e7fa137d md5: e11ee849bd2a573a0f6e53b1b67ebf37 depends: - libblas 3.11.0 8_he492b99_openblas constrains: - liblapacke 3.11.0 8*_openblas - libcblas 3.11.0 8*_openblas - blas 2.308 openblas license: BSD-3-Clause license_family: BSD purls: [] size: 19030 timestamp: 1779860046842 - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.9.0-20_osx64_openblas.conda build_number: 20 sha256: d64e11b93dada339cd0dcc057b3f3f6a5114b8c9bdf90cf6c04cbfa75fb02104 md5: 704bfc2af1288ea973b6755281e6ad32 depends: - libblas 3.9.0 20_osx64_openblas constrains: - blas * openblas - liblapacke 3.9.0 20_osx64_openblas - libcblas 3.9.0 20_osx64_openblas license: BSD-3-Clause license_family: BSD purls: [] size: 14658 timestamp: 1700568740660 - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.3-hbb4bfdb_0.conda sha256: d9e2006051529aec5578c6efeb13bb6a7200a014b2d5a77a579e83a8049d5f3c md5: becdfbfe7049fa248e52aa37a9df09e2 depends: - __osx >=11.0 constrains: - xz 5.8.3.* license: 0BSD purls: [] size: 105724 timestamp: 1775826029494 - conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-hf3981d6_1.conda sha256: 1096c740109386607938ab9f09a7e9bca06d86770a284777586d6c378b8fb3fd md5: ec88ba8a245855935b871a7324373105 depends: - __osx >=10.13 license: BSD-2-Clause license_family: BSD purls: [] size: 79899 timestamp: 1769482558610 - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.68.1-h70048d4_0.conda sha256: 899551e16aac9dfb85bfc2fd98b655f4d1b7fea45720ec04ccb93d95b4d24798 md5: dba4c95e2fe24adcae4b77ebf33559ae depends: - __osx >=11.0 - c-ares >=1.34.6,<2.0a0 - libcxx >=19 - libev >=4.33,<4.34.0a0 - libev >=4.33,<5.0a0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.5,<4.0a0 license: MIT license_family: MIT purls: [] size: 606749 timestamp: 1773854765508 - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.25-openmp_hfef2a42_0.conda sha256: 9895bccdbaa34958ab7dd1f29de66d1dfb94c551c7bb5a663666a500c67ee93c md5: a01b96f00c3155c830d98a518c7dcbfb depends: - libgfortran >=5 - libgfortran5 >=12.3.0 - llvm-openmp >=16.0.6 constrains: - openblas >=0.3.25,<0.3.26.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 6019426 timestamp: 1700537709900 - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.33-openmp_h9e49c7b_0.conda sha256: 2c2ffe7c3ab7becd47ad308946873d2bdc219625af32a53d10efbaa54b595d31 md5: 30666a6f0afe1471e999eca7ae5c8179 depends: - __osx >=11.0 - libgfortran - libgfortran5 >=14.3.0 - llvm-openmp >=19.1.7 constrains: - openblas >=0.3.33,<0.3.34.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 6287889 timestamp: 1776996499823 - conda: https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.58-he930e7c_0.conda sha256: a669b22978e546484d18d99a210801b1823360a266d7035c713d8d1facd035f7 md5: 9744d43d5200f284260637304a069ddd depends: - __osx >=11.0 - libzlib >=1.3.2,<2.0a0 license: zlib-acknowledgement purls: [] size: 299206 timestamp: 1776315286816 - conda: https://conda.anaconda.org/conda-forge/osx-64/libprotobuf-4.25.3-hd4aba4c_1.conda sha256: f509cb24a164b84553b28837ec1e8311ceb0212a1dbb8c7fd99ca383d461ea6c md5: 64ad501f0fd74955056169ec9c42c5c0 depends: - __osx >=10.13 - libabseil * cxx17* - libabseil >=20240116.2,<20240117.0a0 - libcxx >=17 - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 2212274 timestamp: 1727160957452 - conda: https://conda.anaconda.org/conda-forge/osx-64/libprotobuf-5.28.3-h6401091_1.conda sha256: 7bd8467402040312cf1030d98427b6bdce9905e519a1979cd7aa5f0fb0902cad md5: 5601e7ce099eb72741e9cd6413f42a07 depends: - __osx >=10.13 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - libcxx >=18 - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 2312598 timestamp: 1735576514825 - conda: https://conda.anaconda.org/conda-forge/osx-64/libre2-11-2023.09.01-h81f5012_2.conda sha256: 384b72a09bd4bb29c1aa085110b2f940dba431587ffb4e2c1a28f605887a1867 md5: c5c36ec64e3c86504728c38b79011d08 depends: - __osx >=10.13 - libabseil * cxx17* - libabseil >=20240116.1,<20240117.0a0 - libcxx >=16 constrains: - re2 2023.09.01.* license: BSD-3-Clause license_family: BSD purls: [] size: 184017 timestamp: 1708947106275 - conda: https://conda.anaconda.org/conda-forge/osx-64/libre2-11-2024.07.02-h0e468a2_2.conda sha256: 8d29abd9b800f55b56e60b5acb02fab3f3269f5518a7fb4286ca93ca7fef0eff md5: 975743594ba5382fe7e71cda599ac6e8 depends: - __osx >=10.13 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - libcxx >=18 constrains: - re2 2024.07.02.* license: BSD-3-Clause license_family: BSD purls: [] size: 179212 timestamp: 1735541074638 - conda: https://conda.anaconda.org/conda-forge/osx-64/libsodium-1.0.22-ha3d0635_1.conda sha256: 07f0f37463564d93530fc23e2a77cc1aad58ba8724b1842f8713dbf6cde17cb0 md5: bf0ce6af8f7628e34cdb399f6aa82e53 depends: - __osx >=11.0 license: ISC purls: [] size: 281370 timestamp: 1779164249823 - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h77d7759_0.conda sha256: e092c945764c0194298af892bc79c89dbdacac7fab6fa0cd315f91deb0780c03 md5: 78bad38060b6d8bd30e1f43474dcf77c depends: - __osx >=11.0 - libzlib >=1.3.2,<2.0a0 license: blessing purls: [] size: 1006060 timestamp: 1780574903119 - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.53.2-h8f8c405_0.conda sha256: 4d4f3135d390d192ab9cdf3711d87e3be6bb7f3959c52a96e2f333b30960d6fb md5: 4c019bd25570899d0f9755de01b89021 depends: - __osx >=11.0 - icu >=78.3,<79.0a0 - libzlib >=1.3.2,<2.0a0 license: blessing purls: [] size: 1010419 timestamp: 1780575011758 - conda: https://conda.anaconda.org/conda-forge/osx-64/libssh2-1.11.1-hed3591d_0.conda sha256: 00654ba9e5f73aa1f75c1f69db34a19029e970a4aeb0fa8615934d8e9c369c3c md5: a6cb15db1c2dc4d3a5f6cf3772e09e81 depends: - __osx >=10.13 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.0,<4.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 284216 timestamp: 1745608575796 - conda: https://conda.anaconda.org/conda-forge/osx-64/libtensorflow_cc-2.18.0-cpu_hce6ddfb_1.conda sha256: 2655f265a093160d1249f1dc0ad1e610e8a583901f8a0d1a6b52a6b1cb2fa9c8 md5: 55e004998ebaed9356c6217962731ed1 depends: - __osx >=10.13 - flatbuffers >=24.12.23,<24.12.24.0a0 - giflib >=5.2.2,<5.3.0a0 - icu >=75.1,<76.0a0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - libcurl >=8.14.1,<9.0a0 - libcxx >=18 - libgrpc >=1.67.1,<1.68.0a0 - libjpeg-turbo >=3.1.0,<4.0a0 - libml_dtypes-headers >=0.5.0,<0.6 - libpng >=1.6.50,<1.7.0a0 - libprotobuf >=5.28.3,<5.28.4.0a0 - libsqlite >=3.50.4,<4.0a0 - libtensorflow_framework 2.18.0 cpu_h38b2b02_1 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.2,<4.0a0 - snappy >=1.2.2,<1.3.0a0 license: Apache-2.0 license_family: Apache purls: [] size: 138349626 timestamp: 1754517183864 - conda: https://conda.anaconda.org/conda-forge/osx-64/libtensorflow_framework-2.18.0-cpu_h38b2b02_1.conda sha256: d6c24882c9742eaf6dc1e2eb7a6e6ce9583fd5a795270d5464d1f2679f267a03 md5: f0d5fde91b0f589e393328a515f738fb depends: - __osx >=10.13 - giflib >=5.2.2,<5.3.0a0 - icu >=75.1,<76.0a0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - libcurl >=8.14.1,<9.0a0 - libcxx >=18 - libgrpc >=1.67.1,<1.68.0a0 - libjpeg-turbo >=3.1.0,<4.0a0 - libpng >=1.6.50,<1.7.0a0 - libprotobuf >=5.28.3,<5.28.4.0a0 - libsqlite >=3.50.4,<4.0a0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.2,<4.0a0 - snappy >=1.2.2,<1.3.0a0 license: Apache-2.0 license_family: Apache purls: [] size: 8460411 timestamp: 1754516861583 - conda: https://conda.anaconda.org/conda-forge/osx-64/libtiff-4.7.1-ha0a348c_1.conda sha256: e53424c34147301beae2cd9223ebf593720d94c038b3f03cacd0535e12c9668e md5: 9d4344f94de4ab1330cdc41c40152ea6 depends: - __osx >=10.13 - lerc >=4.0.0,<5.0a0 - libcxx >=19 - libdeflate >=1.25,<1.26.0a0 - libjpeg-turbo >=3.1.0,<4.0a0 - liblzma >=5.8.1,<6.0a0 - libwebp-base >=1.6.0,<2.0a0 - libzlib >=1.3.1,<2.0a0 - zstd >=1.5.7,<1.6.0a0 license: HPND purls: [] size: 404591 timestamp: 1762022511178 - conda: https://conda.anaconda.org/conda-forge/osx-64/libwebp-base-1.6.0-hb807250_0.conda sha256: 00dbfe574b5d9b9b2b519acb07545380a6bc98d1f76a02695be4995d4ec91391 md5: 7bb6608cf1f83578587297a158a6630b depends: - __osx >=10.13 constrains: - libwebp 1.6.0 license: BSD-3-Clause license_family: BSD purls: [] size: 365086 timestamp: 1752159528504 - conda: https://conda.anaconda.org/conda-forge/osx-64/libxcb-1.17.0-hf1f96e2_0.conda sha256: 8896cd5deff6f57d102734f3e672bc17120613647288f9122bec69098e839af7 md5: bbeca862892e2898bdb45792a61c4afc depends: - __osx >=10.13 - pthread-stubs - xorg-libxau >=1.0.11,<2.0a0 - xorg-libxdmcp license: MIT license_family: MIT purls: [] size: 323770 timestamp: 1727278927545 - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.2-hbb4bfdb_2.conda sha256: 4c6da089952b2d70150c74234679d6f7ac04f4a98f9432dec724968f912691e7 md5: 30439ff30578e504ee5e0b390afc8c65 depends: - __osx >=11.0 constrains: - zlib 1.3.2 *_2 license: Zlib license_family: Other purls: [] size: 59000 timestamp: 1774073052242 - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-22.1.7-h0d3cbff_0.conda sha256: c8eeb6bca45680db8974b78e0524b2ab3c285a9916a0b3356329d1f949b1311b md5: 301c1db2d75ac8a91f46d21652e08dd6 depends: - __osx >=11.0 constrains: - openmp 22.1.7|22.1.7.* - intel-openmp <0.0a0 license: Apache-2.0 WITH LLVM-exception license_family: APACHE purls: [] size: 310879 timestamp: 1780456054580 - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py310h399bfa0_1.conda sha256: db3087d9114a3dc529737e90e95f7869cef076a492fd6b92fe9d349bf63f989a md5: e85337b6741ec3c1144d3175ee127d57 depends: - __osx >=11.0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 constrains: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/markupsafe?source=hash-mapping size: 23539 timestamp: 1772445447729 - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py311ha8ae342_1.conda sha256: 8702d79aa3f5622d8ede5d5cc94faf135deab77b5caf95401f25f25179b22607 md5: e14b7beea23c9e28b6500c24f5093d62 depends: - __osx >=11.0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 constrains: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/markupsafe?source=hash-mapping size: 25987 timestamp: 1772445597250 - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py312heb39f77_1.conda sha256: 0eb418d4776a1a54c1869b11a5c4ae096ef9a46c8d7e481e32fa814561c5cfed md5: d596f9d03043acd4ec711c844060da59 depends: - __osx >=11.0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 constrains: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/markupsafe?source=hash-mapping size: 25095 timestamp: 1772445399364 - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.10.9-py311h6eed73b_0.conda sha256: 3668dd1091354fd7d74f63d7b3bb1f5a7e53aebfd0074ec4565a0f402b5ced06 md5: 880515050a760e23b0352b8dd3e472e2 depends: - matplotlib-base >=3.10.9,<3.10.10.0a0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - tornado >=5 license: PSF-2.0 license_family: PSF purls: [] size: 17800 timestamp: 1777001299483 - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.10.9-py312hb401068_0.conda sha256: e236285396458870bd69842978a8c7aa412464e73e608105c59ca537d470d40a md5: 4277a65bf3b72f018c778d719338e13a depends: - matplotlib-base >=3.10.9,<3.10.10.0a0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - tornado >=5 license: PSF-2.0 license_family: PSF purls: [] size: 17692 timestamp: 1777000814535 - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.10.9-py311hc159c88_0.conda sha256: 79d91fca10afa48bea29df716da7f6d6136cc485177ede70c15c66713433ecf9 md5: 0acde17c884bf7dde06a6ab21c37a150 depends: - __osx >=11.0 - contourpy >=1.0.1 - cycler >=0.10 - fonttools >=4.22.0 - freetype - kiwisolver >=1.3.1 - libcxx >=19 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - numpy >=1.23 - numpy >=1.23,<3 - packaging >=20.0 - pillow >=8 - pyparsing >=2.3.1 - python >=3.11,<3.12.0a0 - python-dateutil >=2.7 - python_abi 3.11.* *_cp311 - qhull >=2020.2,<2020.3.0a0 license: PSF-2.0 license_family: PSF purls: - pkg:pypi/matplotlib?source=hash-mapping size: 8349954 timestamp: 1777001264605 - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.10.9-py312h7609456_0.conda sha256: 7cec29b831c25744736c92d3fa9afe0e2c090087ce2387e7f1c7ecf08414b23e md5: c7133e403f67d8e34af93e0be4286478 depends: - __osx >=11.0 - contourpy >=1.0.1 - cycler >=0.10 - fonttools >=4.22.0 - freetype - kiwisolver >=1.3.1 - libcxx >=19 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - numpy >=1.23 - numpy >=1.23,<3 - packaging >=20.0 - pillow >=8 - pyparsing >=2.3.1 - python >=3.12,<3.13.0a0 - python-dateutil >=2.7 - python_abi 3.12.* *_cp312 - qhull >=2020.2,<2020.3.0a0 license: PSF-2.0 license_family: PSF purls: - pkg:pypi/matplotlib?source=hash-mapping size: 8284805 timestamp: 1777000784713 - conda: https://conda.anaconda.org/conda-forge/osx-64/ml_dtypes-0.3.2-py310h276d7da_0.conda sha256: e0a9a60919ee96539850a90bd2b22c3ff503f11ad2235e8c69385c92d867e686 md5: 8d6495715c5eca87bb1a85ee76894da8 depends: - libcxx >=15 - numpy >=1.22.4,<2.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 license: MPL-2.0 AND Apache-2.0 purls: - pkg:pypi/ml-dtypes?source=hash-mapping size: 133986 timestamp: 1704728085963 - conda: https://conda.anaconda.org/conda-forge/osx-64/ml_dtypes-0.4.0-py311haeb46be_2.conda sha256: 38dc75c0b4de3fd56707f03398d42a22572b809669936697773280c9385f3943 md5: ab651699c3147a94b4e5bbbf2c466238 depends: - __osx >=10.13 - libcxx >=17 - numpy >=1.19,<3 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: MPL-2.0 AND Apache-2.0 purls: - pkg:pypi/ml-dtypes?source=hash-mapping size: 133229 timestamp: 1725475266320 - conda: https://conda.anaconda.org/conda-forge/osx-64/ml_dtypes-0.4.0-py312h98e817e_2.conda sha256: d84d8801663c4bdcd66177362735c304ed9470fd276d166401be5db2ce9f5e35 md5: 8d5261586fc38c8bb4cac7aea898251a depends: - __osx >=10.13 - libcxx >=17 - numpy >=1.19,<3 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: MPL-2.0 AND Apache-2.0 purls: - pkg:pypi/ml-dtypes?source=hash-mapping size: 136193 timestamp: 1725475212538 - conda: https://conda.anaconda.org/conda-forge/osx-64/ml_dtypes-0.5.4-np2py310hafb1f31_1.conda sha256: 8ad1b286bf45b4cbdf7da0e7c2fe3fe3d8e036548f59dd3027e1036db5279056 md5: fa63116f8a2fbf101acfe03b2f93601e depends: - python - libcxx >=19 - __osx >=10.13 - numpy >=1.21,<3 - python_abi 3.10.* *_cp310 license: MPL-2.0 AND Apache-2.0 purls: - pkg:pypi/ml-dtypes?source=hash-mapping size: 276172 timestamp: 1771362391856 - conda: https://conda.anaconda.org/conda-forge/osx-64/ml_dtypes-0.5.4-np2py311hca72124_1.conda sha256: 973709b3c3c4ed16e46b4bc56677d7cdbe4a1a2423d98c0bb59288d69cf363c2 md5: f9cc89191baff5ab7b802a35300177d5 depends: - python - libcxx >=19 - __osx >=10.13 - python_abi 3.11.* *_cp311 - numpy >=1.23,<3 license: MPL-2.0 AND Apache-2.0 purls: - pkg:pypi/ml-dtypes?source=hash-mapping size: 278952 timestamp: 1771362390400 - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.6-hcc0dc9a_0.conda sha256: f5f7e006ff4271305ab4cc08eedd855c67a571793c3d18aff73f645f088a8cae md5: 31b8740cf1b2588d4e61c81191004061 depends: - __osx >=11.0 license: X11 AND BSD-3-Clause purls: [] size: 831711 timestamp: 1777423052277 - conda: https://conda.anaconda.org/conda-forge/osx-64/nh3-0.3.5-py310hb9b2626_1.conda noarch: python sha256: 4a69080058ad5e6b41352b5cacb18a8012811a3dc26c60ffa7e14ef17f216898 md5: 5a6375a3990ae19d8e856f56d97610cb depends: - python - __osx >=11.0 - _python_abi3_support 1.* - cpython >=3.10 constrains: - __osx >=10.13 license: MIT license_family: MIT purls: - pkg:pypi/nh3?source=hash-mapping size: 658479 timestamp: 1777219253389 - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-1.25.2-py310h7451ae0_0.conda sha256: 77dbf044b2b9149e2038fdc4e99bacbff5e6163136abf84c3f94bbc438ead546 md5: a0f919ff93f102cb1720f71448010c61 depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=15.0.7 - liblapack >=3.9.0,<4.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping size: 6475227 timestamp: 1691057173893 - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-1.26.4-py310h4bfa8fc_0.conda sha256: 914476e2d3273fdf9c0419a7bdcb7b31a5ec25949e4afbc847297ff3a50c62c8 md5: cd6a2298387f558c9ea70ee73a189791 depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=16 - liblapack >=3.9.0,<4.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping size: 6491938 timestamp: 1707226191321 - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.4.6-py311h2c4eb96_0.conda sha256: 47fa3ad9a49348efb5662d7850a433607ee4fabf259709731437a969c3006fa9 md5: 0cb49ff5e81a76c101f1a561cf1f2a76 depends: - python - __osx >=11.0 - libcxx >=19 - liblapack >=3.9.0,<4.0a0 - python_abi 3.11.* *_cp311 - libcblas >=3.9.0,<4.0a0 - libblas >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping size: 8593034 timestamp: 1779169256521 - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.4.6-py312h746d82c_0.conda sha256: e7837f62b874c987c1bd2eda335ae9b977caf61a5227c23e4e8cceef88bb21b6 md5: 86c91d10224283ed367225057a09e4a3 depends: - python - libcxx >=19 - __osx >=11.0 - libcblas >=3.9.0,<4.0a0 - python_abi 3.12.* *_cp312 - libblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping size: 7997002 timestamp: 1779782916096 - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.4.6-py314h7b24d9b_0.conda sha256: 8127ecc9ffbb291830cd6849a8e4f8d9027b130672d277c9444b1d36949f0a38 md5: e04ed878a4f06bb20201dabf7a25f9ee depends: - python - libcxx >=19 - __osx >=11.0 - libblas >=3.9.0,<4.0a0 - python_abi 3.14.* *_cp314 - liblapack >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping size: 8155498 timestamp: 1779169315894 - conda: https://conda.anaconda.org/conda-forge/osx-64/openjpeg-2.5.4-h52bb76a_0.conda sha256: 9a37ecf9c086f3a50d0132e6087dcbe7ea978d80e2da267fa3199c486529b311 md5: 46e628da6e796c948fa8ec9d6d10bda3 depends: - __osx >=11.0 - libcxx >=19 - libpng >=1.6.55,<1.7.0a0 - libtiff >=4.7.1,<4.8.0a0 - libzlib >=1.3.1,<2.0a0 license: BSD-2-Clause license_family: BSD purls: [] size: 335227 timestamp: 1772625294157 - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.2-hc881268_0.conda sha256: 334fd49ea31b99114f5afb1ec44555dc8c90640648302a4f8f838ee345d1ec50 md5: 5cf0ece4375c73d7a5765e83565a69c7 depends: - __osx >=11.0 - ca-certificates license: Apache-2.0 license_family: Apache purls: [] size: 2776564 timestamp: 1775589970694 - conda: https://conda.anaconda.org/conda-forge/osx-64/optree-0.19.1-py310h2b03148_0.conda sha256: 9ce8a42445f14825cb4d089941820292bba6c9cd36b82a2d41f39f77f4ce6ea4 md5: c4d3da880b6ab512b1158a94b607e035 depends: - __osx >=11.0 - libcxx >=19 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - typing-extensions >=4.6 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 419732 timestamp: 1778048407206 - conda: https://conda.anaconda.org/conda-forge/osx-64/optree-0.19.1-py311hd8befaf_0.conda sha256: 2b0c746adc067a25dd8734a4d1be6a78205a319d3f52de61d15a16fb49a31ee7 md5: 37796596797c5161ff14e942189f0886 depends: - __osx >=11.0 - libcxx >=19 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - typing-extensions >=4.6 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 454118 timestamp: 1778048366112 - conda: https://conda.anaconda.org/conda-forge/osx-64/optree-0.19.1-py312hdb80668_0.conda sha256: 73c12390838d5a1ba3fbc2e096b45b641cf42d39a6e2f43a3cf65b0ce28f740f md5: 6be8ebfb1fb03ab8d5173d3eeaaf3bc5 depends: - __osx >=11.0 - libcxx >=19 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - typing-extensions >=4.6 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 461167 timestamp: 1778048201837 - conda: https://conda.anaconda.org/conda-forge/osx-64/pandas-2.0.3-py310h5e4fcda_1.conda sha256: 797be0eff944a01ec3bbac43dbe2b860e42ae530d2f4188c05409c6f95174402 md5: 5b1a8f096180ca4c39ddea7ccdb9d4a7 depends: - libcxx >=15.0.7 - numpy >=1.21.6,<2.0a0 - python >=3.10,<3.11.0a0 - python-dateutil >=2.8.1 - python-tzdata >=2022a - python_abi 3.10.* *_cp310 - pytz >=2020.1 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pandas?source=hash-mapping size: 11741378 timestamp: 1688741606761 - conda: https://conda.anaconda.org/conda-forge/osx-64/pandas-2.3.3-py311hca9a5ca_2.conda sha256: f0dd9bca13b82972ce260941437a1e2efc1b71766b44cc73ac8b878f7d352e9c md5: bd00254b6a77b575177c78f64c5fb366 depends: - __osx >=10.13 - libcxx >=19 - numpy >=1.22.4 - numpy >=1.23,<3 - python >=3.11,<3.12.0a0 - python-dateutil >=2.8.2 - python-tzdata >=2022.7 - python_abi 3.11.* *_cp311 - pytz >=2020.1 constrains: - matplotlib >=3.6.3 - zstandard >=0.19.0 - tabulate >=0.9.0 - pyarrow >=10.0.1 - fastparquet >=2022.12.0 - openpyxl >=3.1.0 - html5lib >=1.1 - pyqt5 >=5.15.9 - odfpy >=1.4.1 - xlrd >=2.0.1 - numexpr >=2.8.4 - xlsxwriter >=3.0.5 - fsspec >=2022.11.0 - xarray >=2022.12.0 - pandas-gbq >=0.19.0 - lxml >=4.9.2 - qtpy >=2.3.0 - tzdata >=2022.7 - blosc >=1.21.3 - beautifulsoup4 >=4.11.2 - pyxlsb >=1.0.10 - pytables >=3.8.0 - pyreadstat >=1.2.0 - scipy >=1.10.0 - gcsfs >=2022.11.0 - bottleneck >=1.3.6 - s3fs >=2022.11.0 - python-calamine >=0.1.7 - psycopg2 >=2.9.6 - sqlalchemy >=2.0.0 - numba >=0.56.4 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pandas?source=hash-mapping size: 14375008 timestamp: 1764615325020 - conda: https://conda.anaconda.org/conda-forge/osx-64/pandas-2.3.3-py312h86abcb1_2.conda sha256: 112273ffd9572a4733c98b9d80a243f38db4d0fce5d34befaf9eb6f64ed39ba3 md5: d7dfad2b9a142319cec4736fe88d8023 depends: - __osx >=10.13 - libcxx >=19 - numpy >=1.22.4 - numpy >=1.23,<3 - python >=3.12,<3.13.0a0 - python-dateutil >=2.8.2 - python-tzdata >=2022.7 - python_abi 3.12.* *_cp312 - pytz >=2020.1 constrains: - pyarrow >=10.0.1 - tabulate >=0.9.0 - html5lib >=1.1 - s3fs >=2022.11.0 - pandas-gbq >=0.19.0 - matplotlib >=3.6.3 - qtpy >=2.3.0 - scipy >=1.10.0 - zstandard >=0.19.0 - bottleneck >=1.3.6 - numexpr >=2.8.4 - pyxlsb >=1.0.10 - tzdata >=2022.7 - psycopg2 >=2.9.6 - pytables >=3.8.0 - fsspec >=2022.11.0 - python-calamine >=0.1.7 - xarray >=2022.12.0 - numba >=0.56.4 - pyqt5 >=5.15.9 - xlrd >=2.0.1 - blosc >=1.21.3 - odfpy >=1.4.1 - openpyxl >=3.1.0 - fastparquet >=2022.12.0 - xlsxwriter >=3.0.5 - pyreadstat >=1.2.0 - sqlalchemy >=2.0.0 - gcsfs >=2022.11.0 - beautifulsoup4 >=4.11.2 - lxml >=4.9.2 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pandas?source=hash-mapping size: 14008759 timestamp: 1764615365220 - conda: https://conda.anaconda.org/conda-forge/osx-64/pandas-2.3.3-py314hc4308db_2.conda sha256: 66df07b283018490ca7e75fd869a4ad8e542e61bf916f17463c8ad022cce7ffd md5: b082e18eb2696625aa09c80e0fbd1997 depends: - __osx >=10.13 - libcxx >=19 - numpy >=1.22.4 - numpy >=1.23,<3 - python >=3.14,<3.15.0a0 - python-dateutil >=2.8.2 - python-tzdata >=2022.7 - python_abi 3.14.* *_cp314 - pytz >=2020.1 constrains: - openpyxl >=3.1.0 - lxml >=4.9.2 - tzdata >=2022.7 - blosc >=1.21.3 - pandas-gbq >=0.19.0 - pyarrow >=10.0.1 - odfpy >=1.4.1 - sqlalchemy >=2.0.0 - bottleneck >=1.3.6 - gcsfs >=2022.11.0 - beautifulsoup4 >=4.11.2 - fsspec >=2022.11.0 - numba >=0.56.4 - pyxlsb >=1.0.10 - scipy >=1.10.0 - pyqt5 >=5.15.9 - xarray >=2022.12.0 - qtpy >=2.3.0 - numexpr >=2.8.4 - tabulate >=0.9.0 - pyreadstat >=1.2.0 - zstandard >=0.19.0 - html5lib >=1.1 - matplotlib >=3.6.3 - xlsxwriter >=3.0.5 - fastparquet >=2022.12.0 - python-calamine >=0.1.7 - xlrd >=2.0.1 - pytables >=3.8.0 - psycopg2 >=2.9.6 - s3fs >=2022.11.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pandas?source=hash-mapping size: 14362288 timestamp: 1764615196689 - conda: https://conda.anaconda.org/conda-forge/osx-64/pillow-12.2.0-py311h27c473c_0.conda sha256: d0f8be0acae899fee5e83a61d2519e98f1b3422e4f620a888dd8aff40c2803b8 md5: d437c033df28ade4d7704d8cd8762c1f depends: - python - __osx >=11.0 - python_abi 3.11.* *_cp311 - zlib-ng >=2.3.3,<2.4.0a0 - openjpeg >=2.5.4,<3.0a0 - libjpeg-turbo >=3.1.2,<4.0a0 - tk >=8.6.13,<8.7.0a0 - libtiff >=4.7.1,<4.8.0a0 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - lcms2 >=2.18,<3.0a0 - libxcb >=1.17.0,<2.0a0 - libwebp-base >=1.6.0,<2.0a0 license: HPND purls: - pkg:pypi/pillow?source=hash-mapping size: 988490 timestamp: 1775060319565 - conda: https://conda.anaconda.org/conda-forge/osx-64/pillow-12.2.0-py312he84af14_0.conda sha256: 720a4bb7aa1cb6a1d4fa026350f69785f5f11fe4899730d575e32292399f470a md5: 2062ffb1b958b050e65ddd8556bcb4d8 depends: - python - __osx >=11.0 - openjpeg >=2.5.4,<3.0a0 - libjpeg-turbo >=3.1.2,<4.0a0 - libxcb >=1.17.0,<2.0a0 - libtiff >=4.7.1,<4.8.0a0 - zlib-ng >=2.3.3,<2.4.0a0 - libwebp-base >=1.6.0,<2.0a0 - tk >=8.6.13,<8.7.0a0 - lcms2 >=2.18,<3.0a0 - python_abi 3.12.* *_cp312 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 license: HPND purls: - pkg:pypi/pillow?source=hash-mapping size: 973346 timestamp: 1775060319565 - conda: https://conda.anaconda.org/conda-forge/osx-64/protobuf-4.25.3-py310h533c97b_1.conda sha256: 97799a7482fd2b9865dd581f6c965a36d94b79e37671aed9a9f38322e68c8321 md5: fddd3718db0f26d38132b955b2db1e81 depends: - __osx >=10.13 - libabseil * cxx17* - libabseil >=20240116.2,<20240117.0a0 - libcxx >=17 - libprotobuf >=4.25.3,<4.25.4.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - setuptools license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/protobuf?source=hash-mapping size: 315927 timestamp: 1725018684969 - conda: https://conda.anaconda.org/conda-forge/osx-64/protobuf-5.28.3-py311hc356e98_0.conda sha256: 649e58ab635f3427385ca50ee140378f18fa2ad7c859cfc1467a35943c84e2e2 md5: 285db01a934e27ed55ee2d0c7457d1ff depends: - __osx >=10.13 - libcxx >=18 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 constrains: - libprotobuf 5.28.3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/protobuf?source=hash-mapping size: 460403 timestamp: 1731366830835 - conda: https://conda.anaconda.org/conda-forge/osx-64/protobuf-5.28.3-py312haafddd8_0.conda sha256: b42f78b07c09ba48ccd8aefce8fc28eb2c07a820667965a2219138b0b81cd56e md5: 519d8df0d097c2c2fcccf87bc2d65fe1 depends: - __osx >=10.13 - libcxx >=18 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 constrains: - libprotobuf 5.28.3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/protobuf?source=hash-mapping size: 452197 timestamp: 1731366580337 - conda: https://conda.anaconda.org/conda-forge/osx-64/psutil-7.2.2-py311ha332486_0.conda sha256: 374933b76115cd0946f3c47f20a7934ee0ce7bd4b667d14f7a704db1e85ee459 md5: 29d06d0775445ec3f42f3a1be72b3f02 depends: - python - __osx >=10.13 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/psutil?source=hash-mapping size: 242642 timestamp: 1769678292465 - conda: https://conda.anaconda.org/conda-forge/osx-64/psutil-7.2.2-py312hf7082af_0.conda sha256: 517c17b24349476535db4da7d1cd31538dadf2c77f9f7f7d8be6b7dc5dfbb636 md5: 1fd947fae149960538fc941b8f122bc1 depends: - python - __osx >=10.13 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/psutil?source=hash-mapping size: 236338 timestamp: 1769678402626 - conda: https://conda.anaconda.org/conda-forge/osx-64/pthread-stubs-0.4-h00291cd_1002.conda sha256: 05944ca3445f31614f8c674c560bca02ff05cb51637a96f665cb2bbe496099e5 md5: 8bcf980d2c6b17094961198284b8e862 depends: - __osx >=10.13 license: MIT license_family: MIT purls: [] size: 8364 timestamp: 1726802331537 - conda: https://conda.anaconda.org/conda-forge/osx-64/pyobjc-core-12.1-py311h0e44a47_0.conda sha256: 20a2f2f6f628aa5f7d82eda7c35d8f122503593190e520186df8a187e2d737e8 md5: 42e664a537b521008cb7c51b4043b6f5 depends: - __osx >=10.13 - libffi >=3.5.2,<3.6.0a0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - setuptools license: MIT license_family: MIT purls: - pkg:pypi/pyobjc-core?source=hash-mapping size: 488334 timestamp: 1763151533453 - conda: https://conda.anaconda.org/conda-forge/osx-64/pyobjc-framework-cocoa-12.1-py311hbebd54f_0.conda sha256: 63e8f062a9e97b6a008d31ae4ef8dc2318bac1e646b31ef6e94019546e681db8 md5: 6690fcf51e6af57e5c70f482ccf14d48 depends: - __osx >=10.13 - libffi >=3.5.2,<3.6.0a0 - pyobjc-core 12.1.* - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/pyobjc-framework-cocoa?source=hash-mapping size: 377431 timestamp: 1763160580006 - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.10.20-hea035f4_0_cpython.conda sha256: b6b9d6a85003b21ac17cc1485e196906bd704759caaab1315f6f8eeb85f26202 md5: bc2a1cfdea76213972b98c65be1e2023 depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.4,<3.0a0 - libffi >=3.4,<4.0a0 - liblzma >=5.8.2,<6.0a0 - libsqlite >=3.51.2,<4.0a0 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.5,<4.0a0 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.10.* *_cp310 license: Python-2.0 purls: [] size: 13083662 timestamp: 1772730522090 - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.11.15-ha9537fe_0_cpython.conda sha256: e02e12cd8d391f18bb3bf91d07e16b993592ec0d76ee37cf639390b766e0e687 md5: 93b802a91de90b2c17b808608726bf45 depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.4,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.2,<6.0a0 - libsqlite >=3.51.2,<4.0a0 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.5,<4.0a0 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.11.* *_cp311 license: Python-2.0 purls: [] size: 15664115 timestamp: 1772730794934 - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.12.13-ha9537fe_0_cpython.conda sha256: fb592ceb1bc247d19247d5535083da4a79721553e29e1290f5d81c07d4f086b5 md5: ec05996c0d914a4e98ee3c7d789083f8 depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.4,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.2,<6.0a0 - libsqlite >=3.51.2,<4.0a0 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.5,<4.0a0 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.12.* *_cp312 license: Python-2.0 purls: [] size: 13672169 timestamp: 1772730464626 - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.14.5-h7c6738f_100_cp314.conda build_number: 100 sha256: f99fd77c51d52319f02b7732971b35921a987ac49ca9b60f9c2e280b0dcdd409 md5: 915728f929ae3610f084aecdf62f5272 depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.8.0,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.3,<6.0a0 - libmpdec >=4.0.0,<5.0a0 - libsqlite >=3.53.1,<4.0a0 - libzlib >=1.3.2,<2.0a0 - ncurses >=6.6,<7.0a0 - openssl >=3.5.6,<4.0a0 - python_abi 3.14.* *_cp314 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata - zstd >=1.5.7,<1.6.0a0 license: Python-2.0 purls: [] size: 14450441 timestamp: 1779239702259 python_site_packages_path: lib/python3.14/site-packages - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py311h53ebfaf_1.conda sha256: db6ce0be1a9e0e57d829e6522ba932643fadd9e5238875ff299925246a01b6b2 md5: 398a9df67c68780701415da829315730 depends: - __osx >=10.13 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT purls: - pkg:pypi/pyyaml?source=hash-mapping size: 194735 timestamp: 1770223769285 - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py312h51361c1_1.conda sha256: d85e3be523b7173a194a66ae05a585ac1e14ccfbe81a9201b8047d6e45f2f7d9 md5: 9029301bf8a667cf57d6e88f03a6726b depends: - __osx >=10.13 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT purls: - pkg:pypi/pyyaml?source=hash-mapping size: 190417 timestamp: 1770223755226 - conda: https://conda.anaconda.org/conda-forge/osx-64/pyzmq-27.1.0-py311h83aef28_3.conda sha256: 4420e88e2b1246c25b09a2e8643254a246cff8d1613f396291bc988d7fea0730 md5: 19e8a922a273fd04d3aeb1289761642c depends: - python - __osx >=11.0 - libcxx >=19 - python_abi 3.11.* *_cp311 - zeromq >=4.3.5,<4.4.0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pyzmq?source=hash-mapping size: 362537 timestamp: 1779484074493 - conda: https://conda.anaconda.org/conda-forge/osx-64/qhull-2020.2-h3c5361c_5.conda sha256: 79d804fa6af9c750e8b09482559814ae18cd8df549ecb80a4873537a5a31e06e md5: dd1ea9ff27c93db7c01a7b7656bd4ad4 depends: - __osx >=10.13 - libcxx >=16 license: LicenseRef-Qhull purls: [] size: 528122 timestamp: 1720814002588 - conda: https://conda.anaconda.org/conda-forge/osx-64/re2-2023.09.01-hb168e87_2.conda sha256: 5739ed2cfa62ed7f828eb4b9e6e69ff1df56cb9a9aacdc296451a3cb647034eb md5: 266f8ca8528fc7e0fa31066c309ad864 depends: - libre2-11 2023.09.01 h81f5012_2 license: BSD-3-Clause license_family: BSD purls: [] size: 26814 timestamp: 1708947195067 - conda: https://conda.anaconda.org/conda-forge/osx-64/re2-2024.07.02-ha5e900a_2.conda sha256: 960729dd943daff21bf2b1f5a9380c17420c5307d4d250766525e266bd0acca7 md5: 5fd6022c97d78c252f1cc8d7433e97d0 depends: - libre2-11 2024.07.02 h0e468a2_2 license: BSD-3-Clause license_family: BSD purls: [] size: 26920 timestamp: 1735541096841 - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda sha256: 4614af680aa0920e82b953fece85a03007e0719c3399f13d7de64176874b80d5 md5: eefd65452dfe7cce476a519bece46704 depends: - __osx >=10.13 - ncurses >=6.5,<7.0a0 license: GPL-3.0-only license_family: GPL purls: [] size: 317819 timestamp: 1765813692798 - conda: https://conda.anaconda.org/conda-forge/osx-64/rpds-py-2026.5.1-py311hcd3406c_0.conda sha256: ab831997e25b2aa6fc9aa4c9aecb7669d6a7333e7d19e4eedc1b19cd05dc6cf3 md5: 2b0c2677e09e5625fcbf7720b12c3779 depends: - python - __osx >=11.0 - python_abi 3.11.* *_cp311 constrains: - __osx >=11.0 license: MIT license_family: MIT purls: - pkg:pypi/rpds-py?source=compressed-mapping size: 309448 timestamp: 1779977313819 - conda: https://conda.anaconda.org/conda-forge/osx-64/ruff-0.14.2-hba89d1c_0.conda noarch: python sha256: 2e9cde0ed16334f991292b56b1de3ea90f62be90911f5726864dc641f3d94e68 md5: 6c76d7b3fd40b65ac03385bfaf1eee1e depends: - python - __osx >=10.13 constrains: - __osx >=10.13 license: MIT license_family: MIT purls: - pkg:pypi/ruff?source=hash-mapping size: 10968811 timestamp: 1761253502632 - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.4.2-py310h9d65eca_1.conda sha256: 2c371b40a43c66d80011421ce59ad676ad1f0146201d5a51e5a55c964f32df54 md5: 768e956ba883484746968b17f551f520 depends: - __osx >=10.13 - joblib >=1.2.0 - libcxx >=16 - llvm-openmp >=16.0.6 - llvm-openmp >=18.1.5 - numpy >=1.19,<3 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - scipy - threadpoolctl >=2.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 8076634 timestamp: 1715870044393 - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.4.2-py311h3c3ac6d_1.conda sha256: 2cf5eba017eb53d09dcad6116615cb2209321d134e46f4501c4bd435525db156 md5: eb6f2d36c84d6702eab64ffbe166f3fa depends: - __osx >=10.13 - joblib >=1.2.0 - libcxx >=16 - llvm-openmp >=16.0.6 - llvm-openmp >=18.1.5 - numpy >=1.19,<3 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - scipy - threadpoolctl >=2.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9363425 timestamp: 1715869957926 - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.5.2-py311ha1d5734_1.conda sha256: 2bec7d70f518550b2d573f2cbe083db95dd65e5b4f0fb417a0c94300bfd146e1 md5: 2797c34b77d64a38c092dd9b21ed908b depends: - __osx >=10.13 - joblib >=1.2.0 - libcxx >=17 - llvm-openmp >=17.0.6 - numpy >=1.19,<3 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - scipy - threadpoolctl >=3.1.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9712928 timestamp: 1726083255913 - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.6.1-py312he1a5313_0.conda sha256: dcdb37893344a321442ce97fd37a5d45b2c6d93a6638fb6e876c638284088d2c md5: c177b3800953875a115ecba027a66d63 depends: - __osx >=10.13 - joblib >=1.2.0 - libcxx >=18 - llvm-openmp >=18.1.8 - numpy >=1.19,<3 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - scipy - threadpoolctl >=3.1.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9721328 timestamp: 1736497397042 - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.7.2-py310h4e9a27a_0.conda sha256: c273750e53f8c0b1e30d7073d89fcdd26d1957659a8435397c53be2ae668c3d2 md5: 3e97ce26235622de420ac7dcbdaebdf1 depends: - __osx >=10.13 - joblib >=1.2.0 - libcxx >=19 - llvm-openmp >=19.1.7 - numpy >=1.21,<3 - numpy >=1.22.0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - scipy >=1.8.0 - threadpoolctl >=3.1.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 7859773 timestamp: 1757407063435 - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.9.0-np2py311he512aeb_0.conda sha256: 31a4530baaf89e5847b39f2082f02aba26e834be7227c93b8dbc88d892d6ee64 md5: 7e15bbbef843bb05744626dc8d1cd377 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.4.0 - threadpoolctl >=3.5.0 - narwhals >=2.0.1 - __osx >=11.0 - libcxx >=19 - llvm-openmp >=19.1.7 - numpy >=1.23,<3 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=compressed-mapping size: 9822126 timestamp: 1780401178108 - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.9.0-np2py312h691318a_0.conda sha256: 10f41e90b84e1f998d2d752ecf8e6223f11fa56e86f8104692229f961f24240c md5: 1c8b1c621007743653c544b9ec69d8bd depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.4.0 - threadpoolctl >=3.5.0 - narwhals >=2.0.1 - llvm-openmp >=19.1.7 - libcxx >=19 - __osx >=11.0 - numpy >=1.23,<3 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=compressed-mapping size: 9558861 timestamp: 1780401307679 - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.9.0-np2py314h67cc4f9_0.conda sha256: 7268e37918343fa0068a2e874017e832e939afc06727941fcaec143b6794ff93 md5: 16ea65f5aad1ad455d8caf1cb756fb16 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.4.0 - threadpoolctl >=3.5.0 - narwhals >=2.0.1 - __osx >=11.0 - llvm-openmp >=19.1.7 - libcxx >=19 - numpy >=1.23,<3 - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9831645 timestamp: 1780401231057 - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.11.4-py310h3f1db6d_0.conda sha256: e47f31119e786352e696ce7877ff97e6e198a43eafbb257b19747b5ae0eadb41 md5: e09db12a57141c4f74ae12e0a5d9629a depends: - __osx >=10.9 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=16.0.6 - libgfortran >=5 - libgfortran5 >=12.3.0 - libgfortran5 >=13.2.0 - liblapack >=3.9.0,<4.0a0 - numpy >=1.22.4,<1.28 - numpy >=1.22.4,<2.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 constrains: - libopenblas <0.3.26 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 15000837 timestamp: 1700813888140 - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.15.2-py310hef62574_0.conda sha256: da86efbfa72e4eb3e4748e5471d04fdbe3f9887f367b6302c1dcdb155bbf712b md5: e79860e43d87b020a0254f0b3f5017c5 depends: - __osx >=10.13 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=18 - libgfortran >=5 - libgfortran5 >=13.2.0 - liblapack >=3.9.0,<4.0a0 - numpy <2.5 - numpy >=1.19,<3 - numpy >=1.23.5 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 14682985 timestamp: 1739792429025 - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.15.2-py311h0c91ca8_0.conda sha256: 796252d7772df42edd29a45ae70eb18843a7e476d42c96c273cd6e677ec148c8 md5: 58c17d411ed0cd1220ed3e824a3efc82 depends: - __osx >=10.13 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=18 - libgfortran >=5 - libgfortran5 >=13.2.0 - liblapack >=3.9.0,<4.0a0 - numpy <2.5 - numpy >=1.19,<3 - numpy >=1.23.5 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 15759628 timestamp: 1739792317052 - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.15.2-py312hd04560d_0.conda sha256: 4c34ef6a688c3ea99a11a9c32941133800f4e10ff5af0074998abed80392c75a md5: cea880e674e00193c7fb915eea6c8200 depends: - __osx >=10.13 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=18 - libgfortran >=5 - libgfortran5 >=13.2.0 - liblapack >=3.9.0,<4.0a0 - numpy <2.5 - numpy >=1.19,<3 - numpy >=1.23.5 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 15547115 timestamp: 1739791861956 - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.17.1-py311h556693a_1.conda sha256: 4e68affca9e1d14cdb1fe6910c459ec4bd01b1217a867f7cfbf40830951f80aa md5: 972007d34efaf5755603391a91e7d50f depends: - __osx >=11.0 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=19 - libgfortran - libgfortran5 >=14.3.0 - liblapack >=3.9.0,<4.0a0 - numpy <2.7 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 15513987 timestamp: 1779875850168 - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.17.1-py312h6309490_1.conda sha256: 4b3663a4f1a92c881ba0fc4d317eee04831adc44400d85bac5b27c503445f6ad md5: 284f71322e48e8ae8ce23d48356df042 depends: - __osx >=11.0 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=19 - libgfortran - libgfortran5 >=14.3.0 - liblapack >=3.9.0,<4.0a0 - numpy <2.7 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 15137152 timestamp: 1779876260804 - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.17.1-py314h5727af0_1.conda sha256: a252c61411227f8677b812f9f24bb7e3afde744a8a6183211b3c63a0dff9e375 md5: 61e649e36316f3224362981421ff9ca0 depends: - __osx >=11.0 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=19 - libgfortran - libgfortran5 >=14.3.0 - liblapack >=3.9.0,<4.0a0 - numpy <2.7 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 15624049 timestamp: 1779875471270 - conda: https://conda.anaconda.org/conda-forge/osx-64/snappy-1.2.2-h01f5ddf_1.conda sha256: 1525e6d8e2edf32dabfe2a8e2fc8bf2df81c5ef9f0b5374a3d4ccfa672bfd949 md5: 2e993292ec18af5cd480932d448598cf depends: - libcxx >=19 - __osx >=10.13 license: BSD-3-Clause license_family: BSD purls: [] size: 40023 timestamp: 1762948053450 - conda: https://conda.anaconda.org/conda-forge/osx-64/statsmodels-0.14.6-py311ha837bc1_0.conda sha256: 4b4f588288f41c2b4ff0d7fa7f7eca91d245b932ddcf8fe4fa7b7e49e38644cb md5: 1edfb3d6c93d0023bd24340ebde2483e depends: - __osx >=10.13 - numpy <3,>=1.22.3 - numpy >=1.23,<3 - packaging >=21.3 - pandas !=2.1.0,>=1.4 - patsy >=0.5.6 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - scipy !=1.9.2,>=1.8 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/statsmodels?source=hash-mapping size: 11766447 timestamp: 1764984047029 - conda: https://conda.anaconda.org/conda-forge/osx-64/statsmodels-0.14.6-py312h391ab28_0.conda sha256: 3d35c37ec7fd764e04b67e5f395a5f936285925836e4a5192ccc503392260065 md5: 114bf0de85f665ce5586e9a0f0f077a8 depends: - __osx >=10.13 - numpy <3,>=1.22.3 - numpy >=1.23,<3 - packaging >=21.3 - pandas !=2.1.0,>=1.4 - patsy >=0.5.6 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - scipy !=1.9.2,>=1.8 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/statsmodels?source=hash-mapping size: 11516375 timestamp: 1764983568072 - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorboard-data-server-0.7.0-py310hdc94fca_4.conda sha256: 77b32fbf2649c5f61832172f4f4ebf5e69ea394b3281bfb8ec5c062765a2f1a4 md5: c120fd317ab36a3a03eee3e9fe5c5f92 depends: - __osx >=10.13 - openssl >=3.5.4,<4.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 constrains: - __osx >=10.13 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tensorboard-data-server?source=hash-mapping size: 3317689 timestamp: 1764930503481 - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorboard-data-server-0.7.0-py311h88b0467_4.conda sha256: de47e350b3f858020a90f13a341b34a7229f16da7a740c964ebe4b93ec414aea md5: a73a4b89ab380635205b415468cc78b2 depends: - __osx >=10.13 - openssl >=3.5.4,<4.0a0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 constrains: - __osx >=10.13 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tensorboard-data-server?source=hash-mapping size: 3316839 timestamp: 1764930188404 - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorboard-data-server-0.7.0-py312hc363c91_4.conda sha256: 976d26a1049f451b3ad568b94509e4605cfd330431b98e09b68117f71ee4277e md5: ea88076f55713bdcec49665180420931 depends: - __osx >=10.13 - openssl >=3.5.4,<4.0a0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 constrains: - __osx >=10.13 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tensorboard-data-server?source=hash-mapping size: 3316379 timestamp: 1764930547961 - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-2.16.1-cpu_py310hf35338a_0.conda sha256: d798767cc337274c66df24d9a1de910b6e431f26c68337d45f11524663de3226 md5: d8b2559558715946c652e53b4add39a0 depends: - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - tensorflow-base 2.16.1 cpu_py310h3690a3c_0 - tensorflow-estimator 2.16.1 cpu_py310h0134bc7_0 track_features: - tensorflow-cpu license: Apache-2.0 license_family: Apache purls: [] size: 42562 timestamp: 1716411024700 - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-2.18.0-cpu_py311h69da8e3_1.conda sha256: 0b584f16104f5e6733735ff92adf58aa9438ab2a1bd1f669706f4491a7166d34 md5: 1171ffdf103da305386db017f3a4e2c4 depends: - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - tensorflow-base 2.18.0 cpu_py311h8bba074_1 - tensorflow-estimator 2.18.0 cpu_py311hc2375f7_1 track_features: - tensorflow-cpu license: Apache-2.0 license_family: Apache purls: [] size: 46257 timestamp: 1754519950130 - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-2.18.0-cpu_py312hf9ba072_1.conda sha256: bfff3702f30296d9f9c1465635749c55ee9f498ffaf1a545072765dd64f564d8 md5: 7fddb414aa2046d50ef516faaa00ca7f depends: - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - tensorflow-base 2.18.0 cpu_py312h7973d44_1 - tensorflow-estimator 2.18.0 cpu_py312hd6a94b3_1 track_features: - tensorflow-cpu license: Apache-2.0 license_family: Apache purls: [] size: 46226 timestamp: 1754519930463 - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-base-2.16.1-cpu_py310h3690a3c_0.conda sha256: b8bd5c04bfc3db2039f389ecb196e6b412b20f607770b2b16bd23d19f233bba1 md5: 5da358ffaf12df22d55aa6b6830c148b depends: - __osx >=10.13 - absl-py >=1.0.0 - astunparse >=1.6.0 - flatbuffers >=24.3.25,<24.3.26.0a0 - gast >=0.2.1,!=0.5.0,!=0.5.1,!=0.5.2 - giflib >=5.2.2,<5.3.0a0 - google-pasta >=0.1.1 - grpcio 1.62.* - h5py >=3.10 - icu >=73.2,<74.0a0 - keras >=3.0 - libabseil * cxx17* - libabseil >=20240116.2,<20240117.0a0 - libcurl >=8.8.0,<9.0a0 - libcxx >=16 - libgrpc >=1.62.2,<1.63.0a0 - libjpeg-turbo >=3.0.0,<4.0a0 - libpng >=1.6.43,<1.7.0a0 - libprotobuf >=4.25.3,<4.25.4.0a0 - libsqlite >=3.45.3,<4.0a0 - libzlib >=1.2.13,<2.0.0a0 - ml_dtypes >=0.3.1,<0.4 - numpy >=1.22,<2.0a0 - numpy >=1.22.4,<2.0a0 - openssl >=3.3.0,<4.0a0 - opt_einsum >=2.3.2 - packaging - protobuf >=3.20.3,<5,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5 - python >=3.10,<3.11.0a0 - python-flatbuffers >=23.5.26 - python_abi 3.10.* *_cp310 - requests >=2.21.0,<3 - six >=1.12 - snappy >=1.2.0,<1.3.0a0 - tensorboard >=2.16,<2.17 - termcolor >=1.1.0 - typing_extensions >=3.6.6 - wrapt >=1.11.0 track_features: - tensorflow-cpu license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tensorflow?source=hash-mapping size: 167807146 timestamp: 1716410307202 - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-base-2.18.0-cpu_py311h8bba074_1.conda sha256: 5955e6710cf73b498ae599ae81350fcba14be6de76b6c426d559bb5cfeb689dc md5: 56c982bbc0c7627f3b027122b3947244 depends: - __osx >=10.13 - absl-py >=1.0.0 - astunparse >=1.6.0 - flatbuffers >=24.12.23,<24.12.24.0a0 - gast >=0.2.1,!=0.5.0,!=0.5.1,!=0.5.2 - giflib >=5.2.2,<5.3.0a0 - google-pasta >=0.1.1 - grpcio 1.67.* - h5py >=3.11 - icu >=75.1,<76.0a0 - keras >=3.0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - libcurl >=8.14.1,<9.0a0 - libcxx >=18 - libgrpc >=1.67.1,<1.68.0a0 - libjpeg-turbo >=3.1.0,<4.0a0 - libpng >=1.6.50,<1.7.0a0 - libprotobuf >=5.28.3,<5.28.4.0a0 - libsqlite >=3.50.4,<4.0a0 - libtensorflow_cc 2.18.0 cpu_hce6ddfb_1 - libtensorflow_framework 2.18.0 cpu_h38b2b02_1 - libzlib >=1.3.1,<2.0a0 - ml_dtypes >=0.4.0,<0.5 - numpy >=1.23,<3 - openssl >=3.5.2,<4.0a0 - opt_einsum >=2.3.2 - packaging - protobuf >=5.26,<6 - python >=3.11,<3.12.0a0 - python-flatbuffers >=24.3.25 - python_abi 3.11.* *_cp311 - requests >=2.21.0,<3 - six >=1.12 - snappy >=1.2.2,<1.3.0a0 - tensorboard >=2.18,<2.19 - termcolor >=1.1.0 - typing_extensions >=3.6.6 - wrapt >=1.11.0 track_features: - tensorflow-cpu license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tensorflow?source=hash-mapping size: 79179655 timestamp: 1754518297908 - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-base-2.18.0-cpu_py312h7973d44_1.conda sha256: 086579095ca677b836e71a2bdb3b43fe3fbc6ed891930465169fc1261c276137 md5: 024797cca216238d64c99b3f9e06adff depends: - __osx >=10.13 - absl-py >=1.0.0 - astunparse >=1.6.0 - flatbuffers >=24.12.23,<24.12.24.0a0 - gast >=0.2.1,!=0.5.0,!=0.5.1,!=0.5.2 - giflib >=5.2.2,<5.3.0a0 - google-pasta >=0.1.1 - grpcio 1.67.* - h5py >=3.11 - icu >=75.1,<76.0a0 - keras >=3.0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - libcurl >=8.14.1,<9.0a0 - libcxx >=18 - libgrpc >=1.67.1,<1.68.0a0 - libjpeg-turbo >=3.1.0,<4.0a0 - libpng >=1.6.50,<1.7.0a0 - libprotobuf >=5.28.3,<5.28.4.0a0 - libsqlite >=3.50.4,<4.0a0 - libtensorflow_cc 2.18.0 cpu_hce6ddfb_1 - libtensorflow_framework 2.18.0 cpu_h38b2b02_1 - libzlib >=1.3.1,<2.0a0 - ml_dtypes >=0.4.0,<0.5 - numpy >=1.23,<3 - openssl >=3.5.2,<4.0a0 - opt_einsum >=2.3.2 - packaging - protobuf >=5.26,<6 - python >=3.12,<3.13.0a0 - python-flatbuffers >=24.3.25 - python_abi 3.12.* *_cp312 - requests >=2.21.0,<3 - six >=1.12 - snappy >=1.2.2,<1.3.0a0 - tensorboard >=2.18,<2.19 - termcolor >=1.1.0 - typing_extensions >=3.6.6 - wrapt >=1.11.0 track_features: - tensorflow-cpu license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tensorflow?source=hash-mapping size: 78965333 timestamp: 1754517681229 - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-estimator-2.16.1-cpu_py310h0134bc7_0.conda sha256: 8fec0468662dd587963868f531712f0e4c94b39643e834e6c22dc8df24ee78a4 md5: d35c296411ec2bc8c97247db8185dced depends: - __osx >=10.13 - libcxx >=16 - openssl >=3.3.0,<4.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - tensorflow-base 2.16.1 cpu_py310h3690a3c_0 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tensorflow-estimator?source=hash-mapping size: 553428 timestamp: 1716411007761 - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-estimator-2.18.0-cpu_py311hc2375f7_1.conda sha256: e01005cf9f25c98b03c1fc3122583e5f1ef3e405721dc27ee2f53ffc4e206223 md5: cdd151c69b37fcf63f54688ce4c6d9f1 depends: - __osx >=10.13 - libcxx >=18 - openssl >=3.5.2,<4.0a0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - tensorflow-base 2.18.0 cpu_py311h8bba074_1 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tensorflow-estimator?source=hash-mapping size: 723569 timestamp: 1754519634255 - conda: https://conda.anaconda.org/conda-forge/osx-64/tensorflow-estimator-2.18.0-cpu_py312hd6a94b3_1.conda sha256: 94bfd5b0a9eb04d92d1be973a18d0215dd435d3dc4bca0c5d3ed427e3df830f4 md5: 46f044faaa6108df1e892f2662911a1e depends: - __osx >=10.13 - libcxx >=18 - openssl >=3.5.2,<4.0a0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - tensorflow-base 2.18.0 cpu_py312h7973d44_1 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tensorflow-estimator?source=hash-mapping size: 700860 timestamp: 1754519057340 - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-h7142dee_3.conda sha256: 7f0d9c320288532873e2d8486c331ec6d87919c9028208d3f6ac91dc8f99a67b md5: 6e6efb7463f8cef69dbcb4c2205bf60e depends: - __osx >=10.13 - libzlib >=1.3.1,<2.0a0 license: TCL license_family: BSD purls: [] size: 3282953 timestamp: 1769460532442 - conda: https://conda.anaconda.org/conda-forge/osx-64/tornado-6.5.6-py311h0a33410_0.conda sha256: 9f9b9bd4e6affc76e0e47d63f0dc845dc062406c75a6b5af5d2b153df5d93eff md5: c4ff5f108250526f37b54f00f5e06746 depends: - __osx >=11.0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tornado?source=hash-mapping size: 880849 timestamp: 1779916363557 - conda: https://conda.anaconda.org/conda-forge/osx-64/tornado-6.5.6-py312h933eb07_0.conda sha256: 526a7d61358628a05d692c088125fa09c5b85a4ede5be1b9e998bb41c09f460f md5: fb813cf264c3fe67d9f48e719071196a depends: - __osx >=11.0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tornado?source=hash-mapping size: 860844 timestamp: 1779916218534 - conda: https://conda.anaconda.org/conda-forge/osx-64/ukkonen-1.1.0-py311ha2ec6bf_0.conda sha256: 9e2d909a1f69303d6f6ba0010da064b133102398b1b7cc0bc87aadde694c9aa4 md5: 7a6cd675f8ae882d1f52389cb8778b6b depends: - __osx >=10.13 - cffi - libcxx >=19 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/ukkonen?source=hash-mapping size: 14181 timestamp: 1769439010863 - conda: https://conda.anaconda.org/conda-forge/osx-64/unicodedata2-17.0.1-py311hc71d4c3_0.conda sha256: fda73ec5e2ceb63c5e68c8c450b7ce011b8f25a91933f78d89d34fd72d637ce9 md5: 158bfb4e986285d72994b38fb76a794d depends: - __osx >=10.13 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/unicodedata2?source=hash-mapping size: 407111 timestamp: 1770909394894 - conda: https://conda.anaconda.org/conda-forge/osx-64/unicodedata2-17.0.1-py312h1a1c95f_0.conda sha256: 29bdc3648a4b8011c572003234ebfaa13666aa71760c9f89f798758ccebd7448 md5: 563dc18b746f873408b76e068e2b4baa depends: - __osx >=10.13 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/unicodedata2?source=hash-mapping size: 406056 timestamp: 1770909495553 - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.2.1-py310h5cd8a12_0.conda sha256: 720d238988e029849bdc1f98651f08e8d5e79602431058f75025d2f7def6c42e md5: 8bbb11ec49ab6e1b32b10b873d4e7bed depends: - __osx >=11.0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/wrapt?source=hash-mapping size: 96820 timestamp: 1779477839048 - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.2.1-py311h0a33410_0.conda sha256: 04f7585851d18d8dc2e718fdeb75734bf90610bdaf30fd54022b9ad6ba3e5ae8 md5: 3bbf67676a186e0bb3e3d3f2b678ba08 depends: - __osx >=11.0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/wrapt?source=hash-mapping size: 112502 timestamp: 1779477849369 - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.2.1-py312h933eb07_0.conda sha256: d836266c79039b60e2104d87d277a74ddc95b195c84ed08dc31185faf3dead6c md5: 9832454120b29950e9304a608d71c01a depends: - __osx >=11.0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/wrapt?source=hash-mapping size: 111048 timestamp: 1779477872468 - conda: https://conda.anaconda.org/conda-forge/osx-64/xorg-libxau-1.0.12-h8616949_1.conda sha256: 928f28bd278c7da674b57d71b2e7f4ac4e7c7ce56b0bf0f60d6a074366a2e76d md5: 47f1b8b4a76ebd0cd22bd7153e54a4dc depends: - __osx >=10.13 license: MIT license_family: MIT purls: [] size: 13810 timestamp: 1762977180568 - conda: https://conda.anaconda.org/conda-forge/osx-64/xorg-libxdmcp-1.1.5-h8616949_1.conda sha256: b7b291cc5fd4e1223058542fca46f462221027779920dd433d68b98e858a4afc md5: 435446d9d7db8e094d2c989766cfb146 depends: - __osx >=10.13 license: MIT license_family: MIT purls: [] size: 19067 timestamp: 1762977101974 - conda: https://conda.anaconda.org/conda-forge/osx-64/yaml-0.2.5-h4132b18_3.conda sha256: a335161bfa57b64e6794c3c354e7d49449b28b8d8a7c4ed02bf04c3f009953f9 md5: a645bb90997d3fc2aea0adf6517059bd depends: - __osx >=10.13 license: MIT license_family: MIT purls: [] size: 79419 timestamp: 1753484072608 - conda: https://conda.anaconda.org/conda-forge/osx-64/zeromq-4.3.5-h84953be_11.conda sha256: 2ba9b6a3131b5a97641068559d38c044f37fd29b54c10dd6d073f1cf81fc4e4c md5: 8a622d1db89d80a94477b1c03824d611 depends: - libcxx >=19 - __osx >=11.0 - krb5 >=1.22.2,<1.23.0a0 - libsodium >=1.0.22,<1.0.23.0a0 license: MPL-2.0 license_family: MOZILLA purls: [] size: 260817 timestamp: 1779124180318 - conda: https://conda.anaconda.org/conda-forge/osx-64/zlib-ng-2.3.3-h8bce59a_1.conda sha256: 4a1beb656761c7d8c9a53474bfd3932c30d82af5d93a32b8ef626c01c059d981 md5: b3ecb6480fd46194e3f7dd0ff4445dff depends: - __osx >=10.13 - libcxx >=19 license: Zlib license_family: Other purls: [] size: 120464 timestamp: 1770168263684 - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda sha256: 47101a4055a70a4876ffc87b750ab2287b67eca793f21c8224be5e1ee6394d3f md5: 727109b184d680772e3122f40136d5ca depends: - __osx >=10.13 - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 528148 timestamp: 1764777156963 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda build_number: 7 sha256: 7acaa2e0782cad032bdaf756b536874346ac1375745fb250e9bdd6a48a7ab3cd md5: a44032f282e7d2acdeb1c240308052dd depends: - llvm-openmp >=9.0.1 license: BSD-3-Clause license_family: BSD purls: [] size: 8325 timestamp: 1764092507920 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/argon2-cffi-bindings-25.1.0-py311h9408147_2.conda sha256: c15fc1aa6184185f1b31670a16b76d59f484d3efa25b467f28d59088cf0085c0 md5: 501c2a606787bcd55b9ddee776208333 depends: - __osx >=11.0 - cffi >=1.0.1 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/argon2-cffi-bindings?source=hash-mapping size: 34403 timestamp: 1762509963182 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-auth-0.10.3-h1e0905d_1.conda sha256: 244a393539ab2eab40f76da36589b2dfd66ad5c1618e984ba3a598a36eb00cc5 md5: eb2d7472f259989cf40125d1071c7474 depends: - __osx >=11.0 - aws-c-io >=0.26.3,<0.26.4.0a0 - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 - aws-c-common >=0.13.1,<0.13.2.0a0 - aws-c-cal >=0.9.14,<0.9.15.0a0 - aws-c-http >=0.11.0,<0.11.1.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 116735 timestamp: 1779877584592 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-cal-0.9.14-h2370581_1.conda sha256: 804faf819f787b32aa781441b8fcd3a67d30f5196f4c715195a42e5d08a5ee0b md5: cfeb11b0e83db3ffd763d92688144d23 depends: - __osx >=11.0 - aws-c-common >=0.13.1,<0.13.2.0a0 license: Apache-2.0 license_family: Apache purls: [] size: 45504 timestamp: 1779464097341 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-common-0.13.1-h84a0fba_0.conda sha256: 01f8f6626ea10bd4b27185056d2f53c9d87e89f2a6707ed5203270b6f3487ab1 md5: c51a71ed3b78ec35621c52de73cdb670 depends: - __osx >=11.0 license: Apache-2.0 license_family: Apache purls: [] size: 225880 timestamp: 1779303131815 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-compression-0.3.2-h729ab01_1.conda sha256: 376f4d95187388c4b3d9061e92970008ef856b9e9f01b0667ef9c607020c75f7 md5: bf4fa06ee03a198f26a6204e7890d9d4 depends: - __osx >=11.0 - aws-c-common >=0.13.1,<0.13.2.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 21548 timestamp: 1779462978120 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-http-0.11.0-h198760e_1.conda sha256: 3c9950c06451b9370168068446b67631f9f8db885bc90e4782adbb3167e7aa47 md5: bc193f5f58acd528f693773643c2b450 depends: - __osx >=11.0 - aws-c-io >=0.26.3,<0.26.4.0a0 - aws-c-common >=0.13.1,<0.13.2.0a0 - aws-c-compression >=0.3.2,<0.3.3.0a0 - aws-c-cal >=0.9.14,<0.9.15.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 177299 timestamp: 1779871105006 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-io-0.26.3-hee75b50_3.conda sha256: 3c133881302904108b39b7c1262180bd54b770693378377d0e842eaeac799d9d md5: 76d0aaeb8cd07030864f9d65e8d078b2 depends: - __osx >=11.0 - aws-c-common >=0.13.1,<0.13.2.0a0 - aws-c-cal >=0.9.14,<0.9.15.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 176952 timestamp: 1779864681476 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-s3-0.12.4-h45371db_0.conda sha256: 7013ca6dd459fdfd7641d79265d6d1989ca859f9bf4031763dd5e4a6ad959260 md5: 7d24812a3188fb41571f0eeed9127662 depends: - __osx >=11.0 - aws-c-auth >=0.10.3,<0.10.4.0a0 - aws-c-cal >=0.9.14,<0.9.15.0a0 - aws-c-common >=0.13.1,<0.13.2.0a0 - aws-checksums >=0.2.10,<0.2.11.0a0 - aws-c-http >=0.11.0,<0.11.1.0a0 - aws-c-io >=0.26.3,<0.26.4.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 132114 timestamp: 1779887925246 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-c-sdkutils-0.2.4-h729ab01_5.conda sha256: 746db875f8a999670b4b121ccdd7550a31e54d83546363a3bb41cb324412446d md5: 92168606c8339c081f20caae33d32ae4 depends: - __osx >=11.0 - aws-c-common >=0.13.1,<0.13.2.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 53726 timestamp: 1779464599891 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/aws-checksums-0.2.10-h729ab01_1.conda sha256: 95c4e2f2a74a67616faed1acf3923bb296918aa78909b20cb9a095fd0e30c8b2 md5: 380a338f1467fdfbe7cb24e2e263eb7b depends: - __osx >=11.0 - aws-c-common >=0.13.1,<0.13.2.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 91963 timestamp: 1779464838740 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.5.0-py310he3b5eba_0.conda sha256: 72a8b497b8c858d5c261cccee3ecdd88393e8b5c37a8746d0d722d9271f0149d md5: d7869018b0727d33642769a3c54a207c depends: - python - __osx >=11.0 - python_abi 3.10.* *_cp310 - zstd >=1.5.7,<1.6.0a0 license: BSD-3-Clause AND MIT AND EPL-2.0 purls: - pkg:pypi/backports-zstd?source=hash-mapping size: 193015 timestamp: 1778594070208 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.5.0-py311h36d4fbb_0.conda sha256: 45a281be7df77eb858a5355f6437b46db04add6908a7cbcb4dca5e5c5f16d29b md5: c2e8c0ad87a6e657908fd75e45b7f8eb depends: - python - __osx >=11.0 - python_abi 3.11.* *_cp311 - zstd >=1.5.7,<1.6.0a0 license: BSD-3-Clause AND MIT AND EPL-2.0 purls: - pkg:pypi/backports-zstd?source=compressed-mapping size: 246710 timestamp: 1778594075847 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.5.0-py312h87c4bb7_0.conda sha256: a492dcf07b1c58797b3192f11aef7e3beb18ec91646d6a5acfe5c6e61e66118d md5: 6ec306e02579965dc9c01092a5f4ce4c depends: - python - __osx >=11.0 - zstd >=1.5.7,<1.6.0a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause AND MIT AND EPL-2.0 purls: - pkg:pypi/backports-zstd?source=hash-mapping size: 240840 timestamp: 1778594074672 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/black-23.3.0-py311h267d04e_1.conda sha256: 6968e8bcc232a32cd34934d14ada2a9a2549db7f8276ba9709eb16595e5545fd md5: 6df8788f081abb97d20ffa83de27d029 depends: - click >=8.0.0 - mypy_extensions >=0.4.3 - packaging >=22.0 - pathspec >=0.9 - platformdirs >=2 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/black?source=hash-mapping size: 355456 timestamp: 1682492376012 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-1.2.0-h7d5ae5b_1.conda sha256: 422ac5c91f8ef07017c594d9135b7ae068157393d2a119b1908c7e350938579d md5: 48ece20aa479be6ac9a284772827d00c depends: - __osx >=11.0 - brotli-bin 1.2.0 hc919400_1 - libbrotlidec 1.2.0 hc919400_1 - libbrotlienc 1.2.0 hc919400_1 license: MIT license_family: MIT purls: [] size: 20237 timestamp: 1764018058424 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-bin-1.2.0-hc919400_1.conda sha256: e2d142052a83ff2e8eab3fe68b9079cad80d109696dc063a3f92275802341640 md5: 377d015c103ad7f3371be1777f8b584c depends: - __osx >=11.0 - libbrotlidec 1.2.0 hc919400_1 - libbrotlienc 1.2.0 hc919400_1 license: MIT license_family: MIT purls: [] size: 18628 timestamp: 1764018033635 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py310h6123dab_1.conda sha256: 317f9b0ab95739a6739e577dee1d4fe2d07fbbe1a97109d145f0de3204cfc7d6 md5: d9359ff9677b23fb89005e3b8dbe8139 depends: - __osx >=11.0 - libcxx >=19 - python >=3.10,<3.11.0a0 - python >=3.10,<3.11.0a0 *_cpython - python_abi 3.10.* *_cp310 constrains: - libbrotlicommon 1.2.0 hc919400_1 license: MIT license_family: MIT purls: - pkg:pypi/brotli?source=hash-mapping size: 359599 timestamp: 1764018669488 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py311hdc60ec4_1.conda sha256: 617545ec0e97d35ed2ff7852f2581a20c0dda80b366d0c42a43706687f971ba8 md5: 150cbf381febcf0a5e470a8d066e1bc0 depends: - __osx >=11.0 - libcxx >=19 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 constrains: - libbrotlicommon 1.2.0 hc919400_1 license: MIT license_family: MIT purls: - pkg:pypi/brotli?source=hash-mapping size: 359588 timestamp: 1764018467340 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py312h0dfefe5_1.conda sha256: 6178775a86579d5e8eec6a7ab316c24f1355f6c6ccbe84bb341f342f1eda2440 md5: 311fcf3f6a8c4eb70f912798035edd35 depends: - __osx >=11.0 - libcxx >=19 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 constrains: - libbrotlicommon 1.2.0 hc919400_1 license: MIT license_family: MIT purls: - pkg:pypi/brotli?source=hash-mapping size: 359503 timestamp: 1764018572368 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda sha256: 540fe54be35fac0c17feefbdc3e29725cce05d7367ffedfaaa1bdda234b019df md5: 620b85a3f45526a8bc4d23fd78fc22f0 depends: - __osx >=11.0 license: bzip2-1.0.6 license_family: BSD purls: [] size: 124834 timestamp: 1771350416561 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda sha256: 2995f2aed4e53725e5efbc28199b46bf311c3cab2648fc4f10c2227d6d5fa196 md5: bcb3cba70cf1eec964a03b4ba7775f01 depends: - __osx >=11.0 license: MIT license_family: MIT purls: [] size: 180327 timestamp: 1765215064054 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py311hd10dc20_1.conda sha256: 1ffde698463d6e7ed571bdb85cb17bfc1d3a107c026d20047995512dcc2749ec md5: 4d7f6780e36f18e7601811dddf3bbec5 depends: - __osx >=11.0 - libffi >=3.5.2,<3.6.0a0 - pycparser - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/cffi?source=hash-mapping size: 294848 timestamp: 1761203196617 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cmarkgfm-2024.11.20-py311h3696347_1.conda sha256: 28b92e9d1331fdc7f81a9d39257eea7f3ef69c8cf6d91cbb77ee1e608a1e5213 md5: a7c6978dd4d1489314b0d3bfb5d9d7ee depends: - __osx >=11.0 - cffi >=1.0.0 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/cmarkgfm?source=hash-mapping size: 116336 timestamp: 1760363201998 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py311h7d85929_4.conda sha256: 57b2c28cbb45e7dacb565541483d802a15c6beff5ccdabba19784a526191f4d3 md5: bd91dd35d73638e5c0f520a18850f6ba depends: - numpy >=1.25 - python - python 3.11.* *_cpython - __osx >=11.0 - libcxx >=19 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/contourpy?source=hash-mapping size: 286095 timestamp: 1769156091585 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312h3093aea_4.conda sha256: fa1b3967c644c1ffaf8beba3d7aee2301a8db32c0e9a56649a0e496cf3abd27c md5: f9cce0bc86b46533489a994a47d3c7d2 depends: - numpy >=1.25 - python - python 3.12.* *_cpython - __osx >=11.0 - libcxx >=19 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/contourpy?source=hash-mapping size: 286084 timestamp: 1769156157865 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.1-py310hb46c203_0.conda sha256: 4d23bba633067b9eb5a6c3b27a536292c50afe96028520c50699fa247b0af3bd md5: f4c432059a9776f1de567c8a726c8bae depends: - __osx >=11.0 - python >=3.10,<3.11.0a0 - python >=3.10,<3.11.0a0 *_cpython - python_abi 3.10.* *_cp310 - tomli license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/coverage?source=compressed-mapping size: 313126 timestamp: 1779838381806 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.1-py311hc290fe0_0.conda sha256: d9475f473084602003da38e373604b48b674b5fbd5939eb6f26b757cbda89f28 md5: 2e3107762a2b8bb31093fe14bab1fe17 depends: - __osx >=11.0 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 - tomli license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/coverage?source=hash-mapping size: 397978 timestamp: 1779838426505 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.1-py312h04c11ed_0.conda sha256: 62f7590a0e6456ff8f534c3f6213e4ec50443d3409fa3babfa30a38822a2b0fa md5: 86b295185747ca5b09e95d7d33280382 depends: - __osx >=11.0 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 - tomli license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/coverage?source=hash-mapping size: 388500 timestamp: 1779838256904 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.14.1-py314h6e9b3f0_0.conda sha256: b2c5285cf2610bf98d0df3c1474beb2e706d2d75b2ae4b1cd7f7f22ef6932c3a md5: 75074919bec101f674e64b0c00a8aa7c depends: - __osx >=11.0 - python >=3.14,<3.15.0a0 - python >=3.14,<3.15.0a0 *_cp314 - python_abi 3.14.* *_cp314 - tomli license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/coverage?source=hash-mapping size: 412237 timestamp: 1779838737834 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.21-py311h8948835_0.conda sha256: 2345e7ba2c9deeed28e83a3cf273c45bef23e5d7bfd9de557749dd1d3b85ded0 md5: 336c8a771fad6546f51b5fe92bd68c96 depends: - python - __osx >=11.0 - libcxx >=19 - python 3.11.* *_cpython - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/debugpy?source=compressed-mapping size: 2675614 timestamp: 1780390235468 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/flatbuffers-24.3.25-hebf3989_0.conda sha256: c95467f1ef83f358518cea13de8e00e3998427fc7f0dad5885f47c18aeb95ad4 md5: f23852b1b71bc82768a6a33f6122efff depends: - libcxx >=16 license: Apache-2.0 license_family: APACHE purls: [] size: 1280038 timestamp: 1711467768202 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/flatbuffers-25.9.23-h9e8ef45_0.conda sha256: b8f4ce2919f2542c6688af909c18f9672b2a19efdb57118c5f415dd5ff0fb3cd md5: 1d6e0829bc8d6907fae9a81f169414ce depends: - __osx >=11.0 - libcxx >=19 license: Apache-2.0 license_family: APACHE purls: [] size: 1299156 timestamp: 1761143339517 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.63.0-py311hc290fe0_0.conda sha256: e339446253b5aec4342526334cb2575a20beaf15478469d9baa3c5a11c7aa498 md5: 23ee082b5c5dc73c19dc0b6451d35079 depends: - __osx >=11.0 - brotli - munkres - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 - unicodedata2 >=15.1.0 license: MIT license_family: MIT purls: - pkg:pypi/fonttools?source=hash-mapping size: 2948507 timestamp: 1778771011007 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.63.0-py312h04c11ed_0.conda sha256: b78cc8df0d93ac0f0d59cb91cf54cc91effa6c124a78c8d2e8afc8011d9fb320 md5: 77e64a600d8426c3863942f67491f5fb depends: - __osx >=11.0 - brotli - munkres - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 - unicodedata2 >=15.1.0 license: MIT license_family: MIT purls: - pkg:pypi/fonttools?source=hash-mapping size: 2904377 timestamp: 1778771054844 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.3-hce30654_0.conda sha256: 5952bd9db12207a18a112e8924aa2ce8c2f9d57b62584d58a97d2f6afe1ea324 md5: 6dcc75ba2e04c555e881b72793d3282f depends: - libfreetype 2.14.3 hce30654_0 - libfreetype6 2.14.3 hdfa99f5_0 license: GPL-2.0-only OR FTL purls: [] size: 173313 timestamp: 1774298702053 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/giflib-5.2.2-h93a5062_0.conda sha256: 843b3f364ff844137e37d5c0a181f11f6d51adcedd216f019d074e5aa5d7e09c md5: 95fa1486c77505330c20f7202492b913 license: MIT license_family: MIT purls: [] size: 71613 timestamp: 1712692611426 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/grpcio-1.62.2-py310hf7687f1_0.conda sha256: c02dfeffddf5e45ca8ec24c6130a9664dff583929b60b573fb429827fa57fbdc md5: 620f17aeaa77ae3c8b3c6fce60a4f360 depends: - libcxx >=16 - libgrpc 1.62.2 h9c18a4f_0 - libzlib >=1.2.13,<2.0.0a0 - python >=3.10,<3.11.0a0 - python >=3.10,<3.11.0a0 *_cpython - python_abi 3.10.* *_cp310 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/grpcio?source=hash-mapping size: 933115 timestamp: 1713393249932 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/grpcio-1.78.1-py311h8fd116a_0.conda sha256: d322087845810b74935b7fa9f61bb588bb265982b7c8b4ea6464fc5b7ed198da md5: 19a09cc49600fcf8f292e1dc98e60379 depends: - __osx >=11.0 - libabseil * cxx17* - libabseil >=20260107.1,<20260108.0a0 - libcxx >=19 - libgrpc 1.78.1 h3e3f78d_0 - libzlib >=1.3.1,<2.0a0 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 - typing-extensions >=4.12,<5 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/grpcio?source=hash-mapping size: 773589 timestamp: 1774013384339 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/grpcio-1.78.1-py312h0c23288_0.conda sha256: 47ca1a2b85df8c16cab5f364e41aea61c70b22c1c23f3656e19947fe442fd783 md5: b498051579243a7f68016a90ee3c83d2 depends: - __osx >=11.0 - libabseil * cxx17* - libabseil >=20260107.1,<20260108.0a0 - libcxx >=19 - libgrpc 1.78.1 h3e3f78d_0 - libzlib >=1.3.1,<2.0a0 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 - typing-extensions >=4.12,<5 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/grpcio?source=hash-mapping size: 762460 timestamp: 1774013064868 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.16.0-nompi_py310hcf53ec8_102.conda sha256: 52ac401df6ff7b6c55837f00c5f9c836180ec40df8173696eacdc0b6bd62e15e md5: 736594a0045aa58223c24d868062b6f9 depends: - __osx >=11.0 - cached-property - hdf5 >=2.1.0,<3.0a0 - numpy >=1.21,<3 - python >=3.10,<3.11.0a0 - python >=3.10,<3.11.0a0 *_cpython - python_abi 3.10.* *_cp310 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/h5py?source=hash-mapping size: 1108137 timestamp: 1775583257878 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.16.0-nompi_py311hf10ccac_102.conda sha256: 3d8437c0633c9055228db8d1fa00daceea5137075bdbce4bb0f7747466da0b09 md5: 3921ef0b72feb738aa5f4107bb6bb084 depends: - __osx >=11.0 - cached-property - hdf5 >=2.1.0,<3.0a0 - numpy >=1.23,<3 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/h5py?source=hash-mapping size: 1193405 timestamp: 1775582985212 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.16.0-nompi_py312h585e8c8_102.conda sha256: 2ac08cff564c3b49f8a7f21eb9abdb81cad2fe2270ed1d48a5be05416d99f40c md5: d5c7d8decb6113045ac8168f66ad1de2 depends: - __osx >=11.0 - cached-property - hdf5 >=2.1.0,<3.0a0 - numpy >=1.23,<3 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/h5py?source=hash-mapping size: 1186119 timestamp: 1775583048662 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-2.1.0-nompi_h262911d_106.conda sha256: 3f88336c992cfa320f343f86e8525af576e9370ca9ebb13aceff6c2044785c41 md5: d9a0aa323c8e320abf1108db5db45817 depends: - __osx >=11.0 - aws-c-auth >=0.10.3,<0.10.4.0a0 - aws-c-common >=0.13.1,<0.13.2.0a0 - aws-c-http >=0.11.0,<0.11.1.0a0 - aws-c-io >=0.26.3,<0.26.4.0a0 - aws-c-s3 >=0.12.4,<0.12.5.0a0 - aws-c-sdkutils >=0.2.4,<0.2.5.0a0 - libaec >=1.1.5,<2.0a0 - libcurl >=8.20.0,<9.0a0 - libcxx >=19 - libgfortran - libgfortran5 >=14.3.0 - libzlib >=1.3.2,<2.0a0 - openssl >=3.5.6,<4.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 3463420 timestamp: 1780578472669 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-73.2-hc8870d7_0.conda sha256: ff9cd0c6cd1349954c801fb443c94192b637e1b414514539f3c49c56a39f51b1 md5: 8521bd47c0e11c5902535bb1a17c565f license: MIT license_family: MIT purls: [] size: 11997841 timestamp: 1692902104771 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-78.3-hef89b57_0.conda sha256: 3a7907a17e9937d3a46dfd41cffaf815abad59a569440d1e25177c15fd0684e5 md5: f1182c91c0de31a7abd40cedf6a5ebef depends: - __osx >=11.0 license: MIT license_family: MIT purls: [] size: 12361647 timestamp: 1773822915649 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py311h7d85929_0.conda sha256: bad01811dae8d727a7ff5a271c8304be495e7e594dfddb9f1d576e41ba7c1a76 md5: 9b4b32f37ebf95463c38636ae2f2ec56 depends: - python - __osx >=11.0 - python 3.11.* *_cpython - libcxx >=19 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/kiwisolver?source=hash-mapping size: 66903 timestamp: 1773067313219 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.5.0-py312h3093aea_0.conda sha256: 8de440f0e33ab6895e81f2c47c51e59d177349a832087a0367e8e259c97f4833 md5: 58261af35f0d33fd28e2257b208a1be0 depends: - python - __osx >=11.0 - libcxx >=19 - python 3.12.* *_cpython - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/kiwisolver?source=hash-mapping size: 68490 timestamp: 1773067215781 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.22.2-h385eeb1_0.conda sha256: c0a0bf028fe7f3defcdcaa464e536cf1b202d07451e18ad83fdd169d15bef6ed md5: e446e1822f4da8e5080a9de93474184d depends: - __osx >=11.0 - libcxx >=19 - libedit >=3.1.20250104,<3.2.0a0 - libedit >=3.1.20250104,<4.0a0 - openssl >=3.5.5,<4.0a0 license: MIT license_family: MIT purls: [] size: 1160828 timestamp: 1769770119811 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.19.1-hdfa7624_1.conda sha256: ccb5598fad3694e79bf54f0eb812e3b3c3dd63d1497e631f5978800eadb9bcc4 md5: d2f2c7c10e2957647d45589b7701a453 depends: - __osx >=11.0 - libjpeg-turbo >=3.1.4.1,<4.0a0 - libtiff >=4.7.1,<4.8.0a0 license: MIT license_family: MIT purls: [] size: 213747 timestamp: 1780212240694 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.1.0-h1eee2c3_0.conda sha256: 66e5ffd301a44da696f3efc2f25d6d94f42a9adc0db06c44ad753ab844148c51 md5: 095e5749868adab9cae42d4b460e5443 depends: - __osx >=11.0 - libcxx >=19 license: Apache-2.0 license_family: Apache purls: [] size: 164222 timestamp: 1773114244984 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240116.2-cxx17_h00cdb27_1.conda sha256: a9517c8683924f4b3b9380cdaa50fdd2009cd8d5f3918c92f64394238189d3cb md5: f16963d88aed907af8b90878b8d8a05c depends: - __osx >=11.0 - libcxx >=16 constrains: - abseil-cpp =20240116.2 - libabseil-static =20240116.2=cxx17* license: Apache-2.0 license_family: Apache purls: [] size: 1136123 timestamp: 1720857649214 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20260107.1-cxx17_h2062a1b_0.conda sha256: 756611fbb8d2957a5b4635d9772bd8432cb6ddac05580a6284cca6fdc9b07fca md5: bb65152e0d7c7178c0f1ee25692c9fd1 depends: - __osx >=11.0 - libcxx >=19 constrains: - abseil-cpp =20260107.1 - libabseil-static =20260107.1=cxx17* license: Apache-2.0 license_family: Apache purls: [] size: 1229639 timestamp: 1770863511331 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.5-h8664d51_0.conda sha256: af9cd8db11eb719e38a3340c88bb4882cf19b5b4237d93845224489fc2a13b46 md5: 13e6d9ae0efbc9d2e9a01a91f4372b41 depends: - __osx >=11.0 - libcxx >=19 license: BSD-2-Clause license_family: BSD purls: [] size: 30390 timestamp: 1769222133373 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-8_h51639a9_openblas.conda build_number: 8 sha256: 8f5ec18ead0619a9cf0f38b49796c22f6fc0f44850c0df2baea0f5277db16e75 md5: dbfe729181a32741ae63ecb41eefbac6 depends: - libopenblas >=0.3.33,<0.3.34.0a0 - libopenblas >=0.3.33,<1.0a0 constrains: - blas 2.308 openblas - liblapack 3.11.0 8*_openblas - liblapacke 3.11.0 8*_openblas - libcblas 3.11.0 8*_openblas - mkl <2027 license: BSD-3-Clause license_family: BSD purls: [] size: 18949 timestamp: 1779859141315 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.9.0-20_osxarm64_openblas.conda build_number: 20 sha256: 5b5b8394352c8ca06b15dcc9319d0af3e9f1dc03fc0a6f6deef05d664d6b763a md5: 49bc8dec26663241ee064b2d7116ec2d depends: - libopenblas >=0.3.25,<0.3.26.0a0 - libopenblas >=0.3.25,<1.0a0 constrains: - liblapack 3.9.0 20_osxarm64_openblas - liblapacke 3.9.0 20_osxarm64_openblas - libcblas 3.9.0 20_osxarm64_openblas - blas * openblas license: BSD-3-Clause license_family: BSD purls: [] size: 14722 timestamp: 1700568881837 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlicommon-1.2.0-hc919400_1.conda sha256: a7cb9e660531cf6fbd4148cff608c85738d0b76f0975c5fc3e7d5e92840b7229 md5: 006e7ddd8a110771134fcc4e1e3a6ffa depends: - __osx >=11.0 license: MIT license_family: MIT purls: [] size: 79443 timestamp: 1764017945924 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlidec-1.2.0-hc919400_1.conda sha256: 2eae444039826db0454b19b52a3390f63bfe24f6b3e63089778dd5a5bf48b6bf md5: 079e88933963f3f149054eec2c487bc2 depends: - __osx >=11.0 - libbrotlicommon 1.2.0 hc919400_1 license: MIT license_family: MIT purls: [] size: 29452 timestamp: 1764017979099 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libbrotlienc-1.2.0-hc919400_1.conda sha256: 01436c32bb41f9cb4bcf07dda647ce4e5deb8307abfc3abdc8da5317db8189d1 md5: b2b7c8288ca1a2d71ff97a8e6a1e8883 depends: - __osx >=11.0 - libbrotlicommon 1.2.0 hc919400_1 license: MIT license_family: MIT purls: [] size: 290754 timestamp: 1764018009077 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-8_hb0561ab_openblas.conda build_number: 8 sha256: f93efcd44bc24f97c2478c7474d3baa6801a057974f330e1d06bedc33e4c778f md5: 03a2ef3491da9e5b4d18c03e9f4b3109 depends: - libblas 3.11.0 8_h51639a9_openblas constrains: - blas 2.308 openblas - liblapack 3.11.0 8*_openblas - liblapacke 3.11.0 8*_openblas license: BSD-3-Clause license_family: BSD purls: [] size: 18911 timestamp: 1779859147634 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.9.0-20_osxarm64_openblas.conda build_number: 20 sha256: d3a74638f60e034202e373cf2950c69a8d831190d497881d13cbf789434d2489 md5: 89f4718753c08afe8cda4dd5791ba94c depends: - libblas 3.9.0 20_osxarm64_openblas constrains: - liblapack 3.9.0 20_osxarm64_openblas - liblapacke 3.9.0 20_osxarm64_openblas - blas * openblas license: BSD-3-Clause license_family: BSD purls: [] size: 14642 timestamp: 1700568912840 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.20.0-hd5a2499_0.conda sha256: 38c0bc634b61e542776e97cfd15d5d41edd304d4e47c333004d2d622439b2381 md5: 2f57b7d0c6adda88957586b7afd78438 depends: - __osx >=11.0 - krb5 >=1.22.2,<1.23.0a0 - libnghttp2 >=1.68.1,<2.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.2,<2.0a0 - openssl >=3.5.6,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT purls: [] size: 400568 timestamp: 1777462251987 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-22.1.7-h55c6f16_0.conda sha256: cceb668dc1b71f054b1036dd83eca2e02c0c3a4b2ba3ad28c74a982d819597a3 md5: 0325fbe13eb6dd39234eb305ac1b3cb8 depends: - __osx >=11.0 license: Apache-2.0 WITH LLVM-exception license_family: Apache purls: [] size: 568252 timestamp: 1780441702930 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libdeflate-1.25-hc11a715_0.conda sha256: 5e0b6961be3304a5f027a8c00bd0967fc46ae162cffb7553ff45c70f51b8314c md5: a6130c709305cd9828b4e1bd9ba0000c depends: - __osx >=11.0 license: MIT license_family: MIT purls: [] size: 55420 timestamp: 1761980066242 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libedit-3.1.20250104-pl5321hafb1f1b_0.conda sha256: 66aa216a403de0bb0c1340a88d1a06adaff66bae2cfd196731aa24db9859d631 md5: 44083d2d2c2025afca315c7a172eab2b depends: - ncurses - __osx >=11.0 - ncurses >=6.5,<7.0a0 license: BSD-2-Clause license_family: BSD purls: [] size: 107691 timestamp: 1738479560845 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libev-4.33-h93a5062_2.conda sha256: 95cecb3902fbe0399c3a7e67a5bed1db813e5ab0e22f4023a5e0f722f2cc214f md5: 36d33e440c31857372a72137f78bacf5 license: BSD-2-Clause license_family: BSD purls: [] size: 107458 timestamp: 1702146414478 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.8.1-hf6b4638_0.conda sha256: 3133fb6bfa871288b92c8b8752696686a841bf4ffe035aa3038033c9e15b738e md5: ef22e9ab1dc7c2f334252f565f90b3b8 depends: - __osx >=11.0 constrains: - expat 2.8.1.* license: MIT license_family: MIT purls: [] size: 69110 timestamp: 1779278728511 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-hcf2aa1b_0.conda sha256: 6686a26466a527585e6a75cc2a242bf4a3d97d6d6c86424a441677917f28bec7 md5: 43c04d9cb46ef176bb2a4c77e324d599 depends: - __osx >=11.0 license: MIT license_family: MIT purls: [] size: 40979 timestamp: 1769456747661 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.3-hce30654_0.conda sha256: a047a2f238362a37d484f9620e8cba29f513a933cd9eb68571ad4b270d6f8f3e md5: f73b109d49568d5d1dda43bb147ae37f depends: - libfreetype6 >=2.14.3 license: GPL-2.0-only OR FTL purls: [] size: 8091 timestamp: 1774298691258 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.3-hdfa99f5_0.conda sha256: ff764608e1f2839e95e2cf9b243681475f8778c36af7a42b3f78f476fdbb1dd3 md5: e98ba7b5f09a5f450eca083d5a1c4649 depends: - __osx >=11.0 - libpng >=1.6.55,<1.7.0a0 - libzlib >=1.3.2,<2.0a0 constrains: - freetype >=2.14.3 license: GPL-2.0-only OR FTL purls: [] size: 338085 timestamp: 1774298689297 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_19.conda sha256: 06644fa4d34d57c9e48f4d84b1256f9e5f654fdb37f43acc8a58a396952d42b7 md5: 644058123986582db33aebd4ae2ca184 depends: - _openmp_mutex constrains: - libgcc-ng ==15.2.0=*_19 - libgomp 15.2.0 19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 404080 timestamp: 1778273064154 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_19.conda sha256: d4837b3b9b30af3132d260225e91ab9dde83be04c59513f500cc81050fb37486 md5: 1ea03f87cdb1078fbc0e2b2deb63752c depends: - libgfortran5 15.2.0 hdae7583_19 constrains: - libgfortran-ng ==15.2.0=*_19 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 139675 timestamp: 1778273280875 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_19.conda sha256: d0a68b7a121d115b80c169e24d1265dcc25a3fe58d107df1bbc430797e226d88 md5: ba36d8c606a6a53fe0b8c12d47267b3d depends: - libgcc >=15.2.0 constrains: - libgfortran 15.2.0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 599691 timestamp: 1778273075448 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.62.2-h9c18a4f_0.conda sha256: d2c5b5a828f6f1242c11e8c91968f48f64446f7dd5cbfa1197545e465eb7d47a md5: e624fc11026dbb84c549435eccd08623 depends: - c-ares >=1.28.1,<2.0a0 - libabseil * cxx17* - libabseil >=20240116.1,<20240117.0a0 - libcxx >=16 - libprotobuf >=4.25.3,<4.25.4.0a0 - libre2-11 >=2023.9.1 - libzlib >=1.2.13,<2.0.0a0 - openssl >=3.2.1,<4.0a0 - re2 constrains: - grpc-cpp =1.62.2 license: Apache-2.0 license_family: APACHE purls: [] size: 5016525 timestamp: 1713392846329 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.78.1-h3e3f78d_0.conda sha256: a6e01573795484c2200e499ddffb825d24184888be6a596d4beaceebe6f8f525 md5: 17b9e07ba9b46754a6953999a948dcf7 depends: - __osx >=11.0 - c-ares >=1.34.6,<2.0a0 - libabseil * cxx17* - libabseil >=20260107.1,<20260108.0a0 - libcxx >=19 - libprotobuf >=6.33.5,<6.33.6.0a0 - libre2-11 >=2025.11.5 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.5,<4.0a0 - re2 constrains: - grpc-cpp =1.78.1 license: Apache-2.0 license_family: APACHE purls: [] size: 4820402 timestamp: 1774012715207 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.4.1-h84a0fba_0.conda sha256: 17e035ae6a520ff6a6bb5dd93a4a7c3895891f4f9743bcb8c6ef607445a31cd0 md5: b8a7544c83a67258b0e8592ec6a5d322 depends: - __osx >=11.0 constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib purls: [] size: 555681 timestamp: 1775962975624 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-8_hd9741b5_openblas.conda build_number: 8 sha256: 8a076fe82142a00fe85f5a5a5351e286e8064f0100fe13608d19182cd0018c25 md5: 85adeb3d469d082dbd9c8c39e36dec57 depends: - libblas 3.11.0 8_h51639a9_openblas constrains: - libcblas 3.11.0 8*_openblas - blas 2.308 openblas - liblapacke 3.11.0 8*_openblas license: BSD-3-Clause license_family: BSD purls: [] size: 18925 timestamp: 1779859153970 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.9.0-20_osxarm64_openblas.conda build_number: 20 sha256: e13f79828a7752f6e0a74cbe62df80c551285f6c37de86bc3bd9987c97faca57 md5: 1fefac78f2315455ce2d7f34782eac0a depends: - libblas 3.9.0 20_osxarm64_openblas constrains: - liblapacke 3.9.0 20_osxarm64_openblas - libcblas 3.9.0 20_osxarm64_openblas - blas * openblas license: BSD-3-Clause license_family: BSD purls: [] size: 14648 timestamp: 1700568930669 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.3-h8088a28_0.conda sha256: 34878d87275c298f1a732c6806349125cebbf340d24c6c23727268184bba051e md5: b1fd823b5ae54fbec272cea0811bd8a9 depends: - __osx >=11.0 constrains: - xz 5.8.3.* license: 0BSD purls: [] size: 92472 timestamp: 1775825802659 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h84a0fba_1.conda sha256: 1089c7f15d5b62c622625ec6700732ece83be8b705da8c6607f4dabb0c4bd6d2 md5: 57c4be259f5e0b99a5983799a228ae55 depends: - __osx >=11.0 license: BSD-2-Clause license_family: BSD purls: [] size: 73690 timestamp: 1769482560514 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.68.1-h8f3e76b_0.conda sha256: 2bc7bc3978066f2c274ebcbf711850cc9ab92e023e433b9631958a098d11e10a md5: 6ea18834adbc3b33df9bd9fb45eaf95b depends: - __osx >=11.0 - c-ares >=1.34.6,<2.0a0 - libcxx >=19 - libev >=4.33,<4.34.0a0 - libev >=4.33,<5.0a0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.5,<4.0a0 license: MIT license_family: MIT purls: [] size: 576526 timestamp: 1773854624224 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.25-openmp_h6c19121_0.conda sha256: b112e0d500bc0314ea8d393efac3ab8c67857e5a2b345348c98e703ee92723e5 md5: a1843550403212b9dedeeb31466ade03 depends: - libgfortran >=5 - libgfortran5 >=12.3.0 - llvm-openmp >=16.0.6 constrains: - openblas >=0.3.25,<0.3.26.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 2896390 timestamp: 1700535987588 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.33-openmp_he657e61_0.conda sha256: 9dd455b2d172aeedfa2058d324b5b5822b0bc1b7c1f32cd183d7078540d2f6eb md5: 909e41855c29f0d52ae630198cd57135 depends: - __osx >=11.0 - libgfortran - libgfortran5 >=14.3.0 - llvm-openmp >=19.1.7 constrains: - openblas >=0.3.33,<0.3.34.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 4304965 timestamp: 1776995497368 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.58-h132b30e_0.conda sha256: 66eae34546df1f098a67064970c92aa14ae7a7505091889e00468294d2882c36 md5: 2259ae0949dbe20c0665850365109b27 depends: - __osx >=11.0 - libzlib >=1.3.2,<2.0a0 license: zlib-acknowledgement purls: [] size: 289546 timestamp: 1776315246750 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-4.25.3-hc39d83c_1.conda sha256: f51bde2dfe73968ab3090c1098f520b65a8d8f11e945cb13bf74d19e30966b61 md5: fa77986d9170450c014586ab87e144f8 depends: - __osx >=11.0 - libabseil * cxx17* - libabseil >=20240116.2,<20240117.0a0 - libcxx >=17 - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 2177164 timestamp: 1727160770879 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-6.33.5-h2d4b707_1.conda sha256: 416c2244999d678dc9a4d8c3472336f8f754676125605399cf6e43956fa3d18b md5: 300fdae9d7ad150a90755f55b0a8a7a8 depends: - __osx >=11.0 - libabseil * cxx17* - libabseil >=20260107.1,<20260108.0a0 - libcxx >=19 - libzlib >=1.3.2,<2.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 2768714 timestamp: 1780004273744 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2023.09.01-h7b2c953_2.conda sha256: c8a0a6e7a627dc9c66ffb8858f8f6d499f67fd269b6636b25dc5169760610f05 md5: 0b7b2ced046d6b5fe6e9d46b1ee0324c depends: - libabseil * cxx17* - libabseil >=20240116.1,<20240117.0a0 - libcxx >=16 constrains: - re2 2023.09.01.* license: BSD-3-Clause license_family: BSD purls: [] size: 171443 timestamp: 1708947163461 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2025.11.05-h4c27e2a_1.conda sha256: 1e2d23bbc1ffca54e4912365b7b59992b7ae5cbeb892779a6dcd9eca9f71c428 md5: 40d8ad21be4ccfff83a314076c3563f4 depends: - __osx >=11.0 - libabseil * cxx17* - libabseil >=20260107.0,<20260108.0a0 - libcxx >=19 constrains: - re2 2025.11.05.* license: BSD-3-Clause license_family: BSD purls: [] size: 165851 timestamp: 1768190225157 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.22-h1a92334_1.conda sha256: 202be45db5726757a8ea1f374f85aacc18c504f5ff15b2558496dff4c8779c48 md5: 9ed5ab909c449bdcae72322e44875a18 depends: - __osx >=11.0 license: ISC purls: [] size: 247352 timestamp: 1779164136206 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1ae2325_0.conda sha256: 862463917e8ef5ac3ebdaf8f19914634b457609cc27ba678b7197124cefeb1f7 md5: 1ebde5c677f00765233a17e278571177 depends: - __osx >=11.0 - icu >=78.3,<79.0a0 - libzlib >=1.3.2,<2.0a0 license: blessing purls: [] size: 927724 timestamp: 1780575223548 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.2-h1b79a29_0.conda sha256: f06b6d9d50d5ad1bed09daada386eb1aa8ed7a9ca4618facd3aead75b82db9ff md5: 530ef68b7f9f7bee04f67db8d435f872 depends: - __osx >=11.0 - libzlib >=1.3.2,<2.0a0 license: blessing purls: [] size: 923664 timestamp: 1780574869893 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libssh2-1.11.1-h1590b86_0.conda sha256: 8bfe837221390ffc6f111ecca24fa12d4a6325da0c8d131333d63d6c37f27e0a md5: b68e8f66b94b44aaa8de4583d3d4cc40 depends: - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.0,<4.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 279193 timestamp: 1745608793272 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtensorflow_cc-2.19.1-cpu_he004e32_5.conda sha256: c837eabe8f2c7b525528066591893b4ef5824586699546e0d2fb88c19f73e178 md5: b3ce8a2d91f9e512afb54b1c275edca9 depends: - libml_dtypes-headers >=0.5.1,<0.6 - libtensorflow_framework ==2.19.1 cpu_h3441331_5 - __osx >=11.0 - libcxx >=19 - libprotobuf >=6.33.5,<6.33.6.0a0 - openssl >=3.5.6,<4.0a0 - libabseil >=20260107.1,<20260108.0a0 - libabseil * cxx17* - icu >=78.3,<79.0a0 - libcurl >=8.20.0,<9.0a0 - snappy >=1.2.2,<1.3.0a0 - libjpeg-turbo >=3.1.4.1,<4.0a0 - flatbuffers >=25.9.23,<25.9.24.0a0 - libsqlite >=3.53.1,<4.0a0 - libzlib >=1.3.2,<2.0a0 - libgrpc >=1.78.1,<1.79.0a0 - libpng >=1.6.58,<1.7.0a0 - giflib >=5.2.2,<5.3.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 137616400 timestamp: 1778106412562 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtensorflow_framework-2.19.1-cpu_h3441331_5.conda sha256: 8873ad429159f54a36eceecc21615a9e7a8f82b30c0731da9d8abfb65ddb8401 md5: a74afedaa52fde24ac79bbb04ab7a9ac depends: - __osx >=11.0 - libcxx >=19 - libprotobuf >=6.33.5,<6.33.6.0a0 - openssl >=3.5.6,<4.0a0 - libabseil >=20260107.1,<20260108.0a0 - libabseil * cxx17* - icu >=78.3,<79.0a0 - libcurl >=8.20.0,<9.0a0 - snappy >=1.2.2,<1.3.0a0 - libjpeg-turbo >=3.1.4.1,<4.0a0 - flatbuffers >=25.9.23,<25.9.24.0a0 - libsqlite >=3.53.1,<4.0a0 - libzlib >=1.3.2,<2.0a0 - libgrpc >=1.78.1,<1.79.0a0 - libpng >=1.6.58,<1.7.0a0 - giflib >=5.2.2,<5.3.0a0 license: Apache-2.0 license_family: APACHE purls: [] size: 8850906 timestamp: 1778106412562 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtiff-4.7.1-h4030677_1.conda sha256: e9248077b3fa63db94caca42c8dbc6949c6f32f94d1cafad127f9005d9b1507f md5: e2a72ab2fa54ecb6abab2b26cde93500 depends: - __osx >=11.0 - lerc >=4.0.0,<5.0a0 - libcxx >=19 - libdeflate >=1.25,<1.26.0a0 - libjpeg-turbo >=3.1.0,<4.0a0 - liblzma >=5.8.1,<6.0a0 - libwebp-base >=1.6.0,<2.0a0 - libzlib >=1.3.1,<2.0a0 - zstd >=1.5.7,<1.6.0a0 license: HPND purls: [] size: 373892 timestamp: 1762022345545 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libwebp-base-1.6.0-h07db88b_0.conda sha256: a4de3f371bb7ada325e1f27a4ef7bcc81b2b6a330e46fac9c2f78ac0755ea3dd md5: e5e7d467f80da752be17796b87fe6385 depends: - __osx >=11.0 constrains: - libwebp 1.6.0 license: BSD-3-Clause license_family: BSD purls: [] size: 294974 timestamp: 1752159906788 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libxcb-1.17.0-hdb1d25a_0.conda sha256: bd3816218924b1e43b275863e21a3e13a5db4a6da74cca8e60bc3c213eb62f71 md5: af523aae2eca6dfa1c8eec693f5b9a79 depends: - __osx >=11.0 - pthread-stubs - xorg-libxau >=1.0.11,<2.0a0 - xorg-libxdmcp license: MIT license_family: MIT purls: [] size: 323658 timestamp: 1727278733917 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda sha256: 361415a698514b19a852f5d1123c5da746d4642139904156ddfca7c922d23a05 md5: bc5a5721b6439f2f62a84f2548136082 depends: - __osx >=11.0 constrains: - zlib 1.3.2 *_2 license: Zlib license_family: Other purls: [] size: 47759 timestamp: 1774072956767 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-22.1.7-hc7d1edf_0.conda sha256: 6bf27376f11198c01a88a1c8234470f45bce0aa7502b7e7988ef03ef5db3a890 md5: 7c6a5897a8bc5b6d509a4ee9dec7fcc8 depends: - __osx >=11.0 constrains: - openmp 22.1.7|22.1.7.* - intel-openmp <0.0a0 license: Apache-2.0 WITH LLVM-exception license_family: APACHE purls: [] size: 285162 timestamp: 1780455637760 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py310hb46c203_1.conda sha256: c1a7cf542e15d5bcd1efbae5a60a75223f36f4870cc96c19ab05fcde642b0394 md5: 4d372362aa5dd174b9300828ac29f806 depends: - __osx >=11.0 - python >=3.10,<3.11.0a0 - python >=3.10,<3.11.0a0 *_cpython - python_abi 3.10.* *_cp310 constrains: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/markupsafe?source=hash-mapping size: 23871 timestamp: 1772445652936 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py311hc290fe0_1.conda sha256: d635f2b1d9e19e8e68c5d33150f7e4f62df08ef2ef0e85977f743e81939afc01 md5: ff068874356bbc7f9bd2d793f809f44b depends: - __osx >=11.0 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 constrains: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/markupsafe?source=hash-mapping size: 26511 timestamp: 1772445369187 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h04c11ed_1.conda sha256: 330394fb9140995b29ae215a19fad46fcc6691bdd1b7654513d55a19aaa091c1 md5: 11d95ab83ef0a82cc2de12c1e0b47fe4 depends: - __osx >=11.0 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 constrains: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/markupsafe?source=hash-mapping size: 25564 timestamp: 1772445846939 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.9-py311ha1ab1f8_0.conda sha256: 7a293da53a795407f5e23fe419c16cadb440d563f36df4df24f5c41d0d5cd4ca md5: 52c7ae2ec68b705441982df15189b8d1 depends: - matplotlib-base >=3.10.9,<3.10.10.0a0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - tornado >=5 license: PSF-2.0 license_family: PSF purls: [] size: 17848 timestamp: 1777001465464 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.9-py312h1f38498_0.conda sha256: 79d518d9556ce81ff4e55e3e947a5a8cb6b1c4a101536e86ab90c34d2c80efcf md5: 94174d301ce2f5962197fb9b880ea8ff depends: - matplotlib-base >=3.10.9,<3.10.10.0a0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - tornado >=5 license: PSF-2.0 license_family: PSF purls: [] size: 17770 timestamp: 1777001080046 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.9-py311h68bafec_0.conda sha256: 5d005eec8908fd10a6a2cf9aa157e96b4544b8705ba4136339c7bc750296d40d md5: bbf824cfa16d55856fb12f04026030c6 depends: - __osx >=11.0 - contourpy >=1.0.1 - cycler >=0.10 - fonttools >=4.22.0 - freetype - kiwisolver >=1.3.1 - libcxx >=19 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - numpy >=1.23 - numpy >=1.23,<3 - packaging >=20.0 - pillow >=8 - pyparsing >=2.3.1 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python-dateutil >=2.7 - python_abi 3.11.* *_cp311 - qhull >=2020.2,<2020.3.0a0 license: PSF-2.0 license_family: PSF purls: - pkg:pypi/matplotlib?source=hash-mapping size: 8362355 timestamp: 1777001412765 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.9-py312hf3defc7_0.conda sha256: c2dc997012fb8a901163cdad333ba5ba27733aa26d67a28a6501f4b4d69fcbce md5: e5d83f3ae6ada32d4e64e366a41f9ff6 depends: - __osx >=11.0 - contourpy >=1.0.1 - cycler >=0.10 - fonttools >=4.22.0 - freetype - kiwisolver >=1.3.1 - libcxx >=19 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - numpy >=1.23 - numpy >=1.23,<3 - packaging >=20.0 - pillow >=8 - pyparsing >=2.3.1 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python-dateutil >=2.7 - python_abi 3.12.* *_cp312 - qhull >=2020.2,<2020.3.0a0 license: PSF-2.0 license_family: PSF purls: - pkg:pypi/matplotlib?source=hash-mapping size: 8191891 timestamp: 1777001043842 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ml_dtypes-0.3.2-py310h401b61c_0.conda sha256: a4efd36ef6a235246d2c20147335e0bfcef517b0d11b097bbe71c3a8cf913397 md5: b1d0b21e241493b257497afe02b7a7cd depends: - libcxx >=15 - numpy >=1.22.4,<2.0a0 - python >=3.10,<3.11.0a0 - python >=3.10,<3.11.0a0 *_cpython - python_abi 3.10.* *_cp310 license: MPL-2.0 AND Apache-2.0 purls: - pkg:pypi/ml-dtypes?source=hash-mapping size: 124673 timestamp: 1704728245055 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ml_dtypes-0.5.4-np2py310h4beac76_1.conda sha256: 1255152ef668f3cfc1dc972610f8027c7b373b19412e6f88b9d9b6b0e0dded40 md5: 715fad9dfd174fd8cf49d28c3e4742a8 depends: - python - python 3.10.* *_cpython - libcxx >=19 - __osx >=11.0 - python_abi 3.10.* *_cp310 - numpy >=1.21,<3 license: MPL-2.0 AND Apache-2.0 purls: - pkg:pypi/ml-dtypes?source=hash-mapping size: 239947 timestamp: 1771362449782 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ml_dtypes-0.5.4-np2py311hb7ce6e1_1.conda sha256: d71365cdb660fe80cde6572482de85eaa499faa0ddb08d5e7bd5d8c384c88382 md5: fa76b2aaefadfc2e4908689a351f158a depends: - python - __osx >=11.0 - python 3.11.* *_cpython - libcxx >=19 - numpy >=1.23,<3 - python_abi 3.11.* *_cp311 license: MPL-2.0 AND Apache-2.0 purls: - pkg:pypi/ml-dtypes?source=hash-mapping size: 242654 timestamp: 1771362416807 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ml_dtypes-0.5.4-np2py312h60fbb24_1.conda sha256: 8a0d3e818b44af3b300d169396d807467eebd292505a96ffce5dd4d7c8731942 md5: 75b22b97f4e00dd1bcdf6e9c9d8610bb depends: - python - python 3.12.* *_cpython - libcxx >=19 - __osx >=11.0 - numpy >=1.23,<3 - python_abi 3.12.* *_cp312 license: MPL-2.0 AND Apache-2.0 purls: - pkg:pypi/ml-dtypes?source=hash-mapping size: 243518 timestamp: 1771362387850 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.6-h1d4f5a5_0.conda sha256: 4ea6c620b87bd1d42bb2ccc2c87cd2483fa2d7f9e905b14c223f11ff3f4c455d md5: 343d10ed5b44030a2f67193905aea159 depends: - __osx >=11.0 license: X11 AND BSD-3-Clause purls: [] size: 805509 timestamp: 1777423252320 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nh3-0.3.5-py310h3b8a9b8_1.conda noarch: python sha256: 5932e04c48ab4e08452ea0e681969706d8b1d8574f3147a39577bec02eac38a7 md5: 6337a614ffaf7f9383968196cf100601 depends: - python - __osx >=11.0 - _python_abi3_support 1.* - cpython >=3.10 constrains: - __osx >=11.0 license: MIT license_family: MIT purls: - pkg:pypi/nh3?source=hash-mapping size: 631222 timestamp: 1777219210592 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.25.2-py310haa1e00c_0.conda sha256: d8eddb8573609b03fa60f5daec29da55141a1faeff8c442bb4c6fd309e40411d md5: 6242d13bf330eccd490979aaf3b5f7e4 depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=15.0.7 - liblapack >=3.9.0,<4.0a0 - python >=3.10,<3.11.0a0 - python >=3.10,<3.11.0a0 *_cpython - python_abi 3.10.* *_cp310 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping size: 5765063 timestamp: 1691057015909 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-1.26.4-py310hd45542a_0.conda sha256: e3078108a4973e73c813b89228f4bd8095ec58f96ca29f55d2e45a6223a9a1db md5: 267ee89a3a0b8c8fa838a2353f9ea0c0 depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=16 - liblapack >=3.9.0,<4.0a0 - python >=3.10,<3.11.0a0 - python >=3.10,<3.11.0a0 *_cpython - python_abi 3.10.* *_cp310 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping size: 5475744 timestamp: 1707226187124 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py311hbd1492f_0.conda sha256: 08e5062ab9bce23adef1c62282a99d035780e43eb8a843b0f11d8a1e967fe123 md5: 7738446d4be7ac8b56e6d6e3bdb7e52b depends: - python - libcxx >=19 - __osx >=11.0 - python_abi 3.11.* *_cp311 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping size: 7456206 timestamp: 1779169211856 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py312ha003a3f_0.conda sha256: b09e03dace335a6f303352fc4e167243e04d7026d55008546fa643d224fb0bad md5: 9f554fdfa902971390975b489e678c03 depends: - python - __osx >=11.0 - libcxx >=19 - python_abi 3.12.* *_cp312 - liblapack >=3.9.0,<4.0a0 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=compressed-mapping size: 6843172 timestamp: 1779169213435 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.4.6-py314hb79c6fa_0.conda sha256: 538064b78042cd2751664f00c6255ecce81b38e9fa6dd9c1863327e6c759ed4a md5: e64e47cb372d92e3425816a2918f4605 depends: - python - __osx >=11.0 - libcxx >=19 - libblas >=3.9.0,<4.0a0 - python_abi 3.14.* *_cp314 - liblapack >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping size: 6995531 timestamp: 1779169217034 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hd9e9057_0.conda sha256: 60aca8b9f94d06b852b296c276b3cf0efba5a6eb9f25feb8708570d3a74f00e4 md5: 4b5d3a91320976eec71678fad1e3569b depends: - __osx >=11.0 - libcxx >=19 - libpng >=1.6.55,<1.7.0a0 - libtiff >=4.7.1,<4.8.0a0 - libzlib >=1.3.1,<2.0a0 license: BSD-2-Clause license_family: BSD purls: [] size: 319697 timestamp: 1772625397692 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.2-hd24854e_0.conda sha256: c91bf510c130a1ea1b6ff023e28bac0ccaef869446acd805e2016f69ebdc49ea md5: 25dcccd4f80f1638428613e0d7c9b4e1 depends: - __osx >=11.0 - ca-certificates license: Apache-2.0 license_family: Apache purls: [] size: 3106008 timestamp: 1775587972483 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.19.1-py310h1c35771_0.conda sha256: 4aaf5eca721a9dcfc22f4187ab63073ab1a4f46b4a27143c8e64ac71d1524957 md5: d24c97c658f3347d81b0afac67f2f118 depends: - __osx >=11.0 - libcxx >=19 - python >=3.10,<3.11.0a0 - python >=3.10,<3.11.0a0 *_cpython - python_abi 3.10.* *_cp310 - typing-extensions >=4.6 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 399464 timestamp: 1778048163527 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.19.1-py311h572238d_0.conda sha256: 3f0ce5b2bf6ade23ac8725e75bcfd401b91f2fb480ab0ff6a09cdfa4a8c376f7 md5: ecbec8f85d20eaa495938fa32ad49442 depends: - __osx >=11.0 - libcxx >=19 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 - typing-extensions >=4.6 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 431558 timestamp: 1778048194926 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.19.1-py312h766f71e_0.conda sha256: 9a5d3944bd6281fb2f2004d06ff3caa60846c642c7a739a9026b037833cda57f md5: af48088edf339bdea37725f49d4fefea depends: - __osx >=11.0 - libcxx >=19 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 - typing-extensions >=4.6 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 437049 timestamp: 1778048481638 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.0.3-py310h1cdf563_1.conda sha256: d2ac911bf24f39dd83a83ea15333291b175227961bf2d45bad31317243c4fdb1 md5: 680ece3f188b726782ef4aa84e8cca12 depends: - libcxx >=15.0.7 - numpy >=1.21.6,<2.0a0 - python >=3.10,<3.11.0a0 - python >=3.10,<3.11.0a0 *_cpython - python-dateutil >=2.8.1 - python-tzdata >=2022a - python_abi 3.10.* *_cp310 - pytz >=2020.1 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pandas?source=hash-mapping size: 11705997 timestamp: 1688741660429 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.3.3-py311hdb8e4fa_2.conda sha256: dd0c4ade846921acbf6ded79b7589e72f58689129dabc8290deec455539e9235 md5: da2e5c7c0a3061398c8a1e225f2e339e depends: - __osx >=11.0 - libcxx >=19 - numpy >=1.22.4 - numpy >=1.23,<3 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python-dateutil >=2.8.2 - python-tzdata >=2022.7 - python_abi 3.11.* *_cp311 - pytz >=2020.1 constrains: - sqlalchemy >=2.0.0 - xarray >=2022.12.0 - bottleneck >=1.3.6 - tzdata >=2022.7 - pyarrow >=10.0.1 - lxml >=4.9.2 - tabulate >=0.9.0 - matplotlib >=3.6.3 - qtpy >=2.3.0 - pytables >=3.8.0 - odfpy >=1.4.1 - html5lib >=1.1 - openpyxl >=3.1.0 - psycopg2 >=2.9.6 - numba >=0.56.4 - s3fs >=2022.11.0 - fastparquet >=2022.12.0 - gcsfs >=2022.11.0 - python-calamine >=0.1.7 - fsspec >=2022.11.0 - pandas-gbq >=0.19.0 - beautifulsoup4 >=4.11.2 - xlsxwriter >=3.0.5 - numexpr >=2.8.4 - pyxlsb >=1.0.10 - pyreadstat >=1.2.0 - xlrd >=2.0.1 - blosc >=1.21.3 - zstandard >=0.19.0 - pyqt5 >=5.15.9 - scipy >=1.10.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pandas?source=hash-mapping size: 14124254 timestamp: 1764615503256 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.3.3-py312h5978115_2.conda sha256: 93aa5b02e2394080a32fee9fb151da3384d317a42472586850abb37b28f314db md5: fcbba82205afa4956c39136c68929385 depends: - __osx >=11.0 - libcxx >=19 - numpy >=1.22.4 - numpy >=1.23,<3 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python-dateutil >=2.8.2 - python-tzdata >=2022.7 - python_abi 3.12.* *_cp312 - pytz >=2020.1 constrains: - xarray >=2022.12.0 - scipy >=1.10.0 - tabulate >=0.9.0 - pytables >=3.8.0 - xlsxwriter >=3.0.5 - pyxlsb >=1.0.10 - odfpy >=1.4.1 - zstandard >=0.19.0 - fastparquet >=2022.12.0 - gcsfs >=2022.11.0 - beautifulsoup4 >=4.11.2 - qtpy >=2.3.0 - xlrd >=2.0.1 - pandas-gbq >=0.19.0 - s3fs >=2022.11.0 - pyreadstat >=1.2.0 - tzdata >=2022.7 - html5lib >=1.1 - fsspec >=2022.11.0 - lxml >=4.9.2 - numexpr >=2.8.4 - blosc >=1.21.3 - openpyxl >=3.1.0 - pyarrow >=10.0.1 - python-calamine >=0.1.7 - numba >=0.56.4 - sqlalchemy >=2.0.0 - pyqt5 >=5.15.9 - psycopg2 >=2.9.6 - bottleneck >=1.3.6 - matplotlib >=3.6.3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pandas?source=hash-mapping size: 13893993 timestamp: 1764615503244 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.3.3-py314ha3d490a_2.conda sha256: f71fc63904d80ef7bf4e882b420426e167e02cf68b9bd71ea6beb0a9d0c37430 md5: 6e2f31aca92c525a884c509738aca93a depends: - __osx >=11.0 - libcxx >=19 - numpy >=1.22.4 - numpy >=1.23,<3 - python >=3.14,<3.15.0a0 - python >=3.14,<3.15.0a0 *_cp314 - python-dateutil >=2.8.2 - python-tzdata >=2022.7 - python_abi 3.14.* *_cp314 - pytz >=2020.1 constrains: - odfpy >=1.4.1 - zstandard >=0.19.0 - blosc >=1.21.3 - html5lib >=1.1 - numexpr >=2.8.4 - gcsfs >=2022.11.0 - sqlalchemy >=2.0.0 - numba >=0.56.4 - pyqt5 >=5.15.9 - fastparquet >=2022.12.0 - pandas-gbq >=0.19.0 - pytables >=3.8.0 - qtpy >=2.3.0 - fsspec >=2022.11.0 - s3fs >=2022.11.0 - pyreadstat >=1.2.0 - pyxlsb >=1.0.10 - pyarrow >=10.0.1 - xlrd >=2.0.1 - xarray >=2022.12.0 - beautifulsoup4 >=4.11.2 - tabulate >=0.9.0 - psycopg2 >=2.9.6 - bottleneck >=1.3.6 - matplotlib >=3.6.3 - python-calamine >=0.1.7 - lxml >=4.9.2 - openpyxl >=3.1.0 - scipy >=1.10.0 - xlsxwriter >=3.0.5 - tzdata >=2022.7 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pandas?source=hash-mapping size: 14130201 timestamp: 1764615862386 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.2.0-py311hd37aea2_0.conda sha256: b283397037294e56d3720ddd78489dd43d959eaf6453d51cb68d97bb0a52585f md5: 9b5458ae3fbc4fa5c3e427ff81e037cb depends: - python - __osx >=11.0 - python 3.11.* *_cpython - lcms2 >=2.18,<3.0a0 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - libxcb >=1.17.0,<2.0a0 - openjpeg >=2.5.4,<3.0a0 - python_abi 3.11.* *_cp311 - libtiff >=4.7.1,<4.8.0a0 - libwebp-base >=1.6.0,<2.0a0 - zlib-ng >=2.3.3,<2.4.0a0 - tk >=8.6.13,<8.7.0a0 - libjpeg-turbo >=3.1.2,<4.0a0 license: HPND purls: - pkg:pypi/pillow?source=hash-mapping size: 979746 timestamp: 1775060469004 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.2.0-py312h4e908a4_0.conda sha256: f7ee5d45bf16184d2b53f0d35c98c06e4e82e21688ce93e52b55c02ec7153bf3 md5: 0634560e556adb3e7924668e49ad53fc depends: - python - __osx >=11.0 - python 3.12.* *_cpython - libxcb >=1.17.0,<2.0a0 - libtiff >=4.7.1,<4.8.0a0 - libjpeg-turbo >=3.1.2,<4.0a0 - zlib-ng >=2.3.3,<2.4.0a0 - tk >=8.6.13,<8.7.0a0 - libfreetype >=2.14.3 - libfreetype6 >=2.14.3 - lcms2 >=2.18,<3.0a0 - python_abi 3.12.* *_cp312 - openjpeg >=2.5.4,<3.0a0 - libwebp-base >=1.6.0,<2.0a0 license: HPND purls: - pkg:pypi/pillow?source=hash-mapping size: 965082 timestamp: 1775060469004 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-4.25.3-py310ha1b16c5_1.conda sha256: 5b0c50ca224e12bd07eafe32b5708fe82d92513e23db404421f9ec00ddbe37e8 md5: 8d1dc9d4be78c814024a8a60fa62b5b7 depends: - __osx >=11.0 - libabseil * cxx17* - libabseil >=20240116.2,<20240117.0a0 - libcxx >=17 - libprotobuf >=4.25.3,<4.25.4.0a0 - python >=3.10,<3.11.0a0 - python >=3.10,<3.11.0a0 *_cpython - python_abi 3.10.* *_cp310 - setuptools license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/protobuf?source=hash-mapping size: 314747 timestamp: 1725018767731 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-6.33.5-py311h12b56a6_2.conda sha256: a83ba8e0f5c3c232defd2f42b510b11d43129a24e9a157e95db32a6b4a557675 md5: db128b2af43c5031b2f0a79a78618df5 depends: - __osx >=11.0 - libabseil * cxx17* - libabseil >=20260107.1,<20260108.0a0 - libcxx >=19 - libzlib >=1.3.1,<2.0a0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 constrains: - libprotobuf 6.33.5 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/protobuf?source=hash-mapping size: 473279 timestamp: 1773265432489 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-6.33.5-py312h857ab9a_2.conda sha256: 3e8720f23829f60103f61fc5e53ac36535da5896855a98d2bbbc6a35e78be430 md5: db3dd88ca6652ab2485d11f88a2b1d59 depends: - __osx >=11.0 - libabseil * cxx17* - libabseil >=20260107.1,<20260108.0a0 - libcxx >=19 - libzlib >=1.3.1,<2.0a0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 constrains: - libprotobuf 6.33.5 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/protobuf?source=hash-mapping size: 465012 timestamp: 1773265421534 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py311he363849_0.conda sha256: 2b774e8f4ccaac8783d1908f281e4bb20b7036d6708dc913ee7811b070f5b4b5 md5: 7cff50265141513c960f00ba586780ea depends: - python - python 3.11.* *_cpython - __osx >=11.0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/psutil?source=hash-mapping size: 245203 timestamp: 1769678306347 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py312hb3ab3e3_0.conda sha256: 6d0e21c76436374635c074208cfeee62a94d3c37d0527ad67fd8a7615e546a05 md5: fd856899666759403b3c16dcba2f56ff depends: - python - __osx >=11.0 - python 3.12.* *_cpython - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/psutil?source=hash-mapping size: 239031 timestamp: 1769678393511 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pthread-stubs-0.4-hd74edd7_1002.conda sha256: 8ed65e17fbb0ca944bfb8093b60086e3f9dd678c3448b5de212017394c247ee3 md5: 415816daf82e0b23a736a069a75e9da7 depends: - __osx >=11.0 license: MIT license_family: MIT purls: [] size: 8381 timestamp: 1726802424786 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-core-12.1-py311hce6e4fa_0.conda sha256: f6dced0ea4f220abc1278f6217e22ca1c631f6154dd086b0a7a2866589d6b78c md5: 812e22c5c7859e719ec225ece0c6f2c1 depends: - __osx >=11.0 - libffi >=3.5.2,<3.6.0a0 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 - setuptools license: MIT license_family: MIT purls: - pkg:pypi/pyobjc-core?source=hash-mapping size: 481434 timestamp: 1763151508974 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-framework-cocoa-12.1-py311h9049b8e_0.conda sha256: 67e209a5e9ebaf08aa36e2a4b9139ff91a7ecee7c17408d0a3a1ea1dc3a67681 md5: b215a767b5ef6f16347cacebc409e66b depends: - __osx >=11.0 - libffi >=3.5.2,<3.6.0a0 - pyobjc-core 12.1.* - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/pyobjc-framework-cocoa?source=hash-mapping size: 383445 timestamp: 1763160541362 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.10.20-h1b19095_0_cpython.conda sha256: f0f6fcbb6cfdee5a6b9c03b5b94d2bbe737f3b17a618006c7685cc48992ae667 md5: 55ec25b0d09379eb11c32dbe09ee28c4 depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.4,<3.0a0 - libffi >=3.4,<4.0a0 - liblzma >=5.8.2,<6.0a0 - libsqlite >=3.51.2,<4.0a0 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.5,<4.0a0 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.10.* *_cp310 license: Python-2.0 purls: [] size: 12468674 timestamp: 1772730636766 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.15-h8561d8f_0_cpython.conda sha256: 9a846065863925b2562126a5c6fecd7a972e84aaa4de9e686ad3715ca506acfa md5: 49c7d96c58b969585cf09fb01d74e08e depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.4,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.2,<6.0a0 - libsqlite >=3.51.2,<4.0a0 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.5,<4.0a0 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.11.* *_cp311 license: Python-2.0 purls: [] size: 14753109 timestamp: 1772730203101 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda sha256: e658e647a4a15981573d6018928dec2c448b10c77c557c29872043ff23c0eb6a md5: 8e7608172fa4d1b90de9a745c2fd2b81 depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.4,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.2,<6.0a0 - libsqlite >=3.51.2,<4.0a0 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.5,<4.0a0 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.12.* *_cp312 license: Python-2.0 purls: [] size: 12127424 timestamp: 1772730755512 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.5-h4c637c5_100_cp314.conda build_number: 100 sha256: 06dec0e2f50e2f7e6a8808fcb4aff23729a3f23bcb1fca4fcbc3a341d9e38a83 md5: f7331c9deaf21c79e5675e72b21d570b depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.8.0,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.3,<6.0a0 - libmpdec >=4.0.0,<5.0a0 - libsqlite >=3.53.1,<4.0a0 - libzlib >=1.3.2,<2.0a0 - ncurses >=6.6,<7.0a0 - openssl >=3.5.6,<4.0a0 - python_abi 3.14.* *_cp314 - readline >=8.3,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata - zstd >=1.5.7,<1.6.0a0 license: Python-2.0 purls: [] size: 13560854 timestamp: 1779238292621 python_site_packages_path: lib/python3.14/site-packages - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py311hc290fe0_1.conda sha256: 984e73d7957460689e10533059de8adb38a308853d298900a37acc58edd84cec md5: e4b908da7cd496b3fa6798c0f60a2a19 depends: - __osx >=11.0 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT purls: - pkg:pypi/pyyaml?source=hash-mapping size: 192948 timestamp: 1770223655988 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py312h04c11ed_1.conda sha256: 737959262d03c9c305618f2d48c7f1691fb996f14ae420bfd05932635c99f873 md5: 95a5f0831b5e0b1075bbd80fcffc52ac depends: - __osx >=11.0 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT purls: - pkg:pypi/pyyaml?source=hash-mapping size: 187278 timestamp: 1770223990452 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.1.0-py311h8463d66_3.conda sha256: e461e38dc527e2c24929b19debd9ebf006d36bca7fb222bb9465fd19dc41af40 md5: dc07ae3c579fe19354d3e10a90992415 depends: - python - libcxx >=19 - python 3.11.* *_cpython - __osx >=11.0 - python_abi 3.11.* *_cp311 - zeromq >=4.3.5,<4.4.0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pyzmq?source=hash-mapping size: 359949 timestamp: 1779484138996 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/qhull-2020.2-h420ef59_5.conda sha256: 873ac689484262a51fd79bc6103c1a1bedbf524924d7f0088fb80703042805e4 md5: 6483b1f59526e05d7d894e466b5b6924 depends: - __osx >=11.0 - libcxx >=16 license: LicenseRef-Qhull purls: [] size: 516376 timestamp: 1720814307311 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2023.09.01-h4cba328_2.conda sha256: 0e0d44414381c39a7e6f3da442cb41c637df0dcb383a07425f19c19ccffa0118 md5: 0342882197116478a42fa4ea35af79c1 depends: - libre2-11 2023.09.01 h7b2c953_2 license: BSD-3-Clause license_family: BSD purls: [] size: 26770 timestamp: 1708947220914 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/re2-2025.11.05-ha480c28_1.conda sha256: 5bab972e8f2bff1b5b3574ffec8ecb89f7937578bd107584ed3fde507ff132f9 md5: a1ff22f664b0affa3de712749ccfbf04 depends: - libre2-11 2025.11.05 h4c27e2a_1 license: BSD-3-Clause license_family: BSD purls: [] size: 27445 timestamp: 1768190259003 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda sha256: a77010528efb4b548ac2a4484eaf7e1c3907f2aec86123ed9c5212ae44502477 md5: f8381319127120ce51e081dce4865cf4 depends: - __osx >=11.0 - ncurses >=6.5,<7.0a0 license: GPL-3.0-only license_family: GPL purls: [] size: 313930 timestamp: 1765813902568 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-2026.5.1-py311haff49d3_0.conda sha256: 433b676c6de3759fe4ab89d17acb6ac7f3008f48b3d9fb1cb8fda82840676b5c md5: ee72a46b3af924481252a2154ee29797 depends: - python - __osx >=11.0 - python_abi 3.11.* *_cp311 constrains: - __osx >=11.0 license: MIT license_family: MIT purls: - pkg:pypi/rpds-py?source=compressed-mapping size: 292710 timestamp: 1779977125495 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ruff-0.14.2-h492a034_0.conda noarch: python sha256: 34fef6ff3447a243abd7ca98a95f6199af7a38aeb182c7d5d93a7e8681530571 md5: 22bcd3c3680cd6dcefe9bc73612a7132 depends: - python - __osx >=11.0 constrains: - __osx >=11.0 license: MIT license_family: MIT purls: - pkg:pypi/ruff?source=hash-mapping size: 10033698 timestamp: 1761253464229 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.4.2-py310h64e73be_1.conda sha256: ff8d8adeb7ac8416d1f6bf0b057bbe2155a3c58c2f1bf8a8b8e1fcd4f2b0c04d md5: 110b10ba3774411ffd1ed9fef8dac184 depends: - __osx >=11.0 - joblib >=1.2.0 - libcxx >=16 - llvm-openmp >=16.0.6 - llvm-openmp >=18.1.5 - numpy >=1.19,<3 - python >=3.10,<3.11.0a0 - python >=3.10,<3.11.0a0 *_cpython - python_abi 3.10.* *_cp310 - scipy - threadpoolctl >=2.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 8141101 timestamp: 1715870026027 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.4.2-py311hbfb48bc_1.conda sha256: e21e11fee63202ba6dc59df71af1db16351468b0a8c742a7080b1cb2f852c59a md5: eb4b192b29d412853c75a15102dfd832 depends: - __osx >=11.0 - joblib >=1.2.0 - libcxx >=16 - llvm-openmp >=16.0.6 - llvm-openmp >=18.1.5 - numpy >=1.19,<3 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 - scipy - threadpoolctl >=2.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9449312 timestamp: 1715870005992 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.5.2-py311h9e23f0f_1.conda sha256: cc9f8c3f12ef7cce384285c11e76e981349771542edf14ba069b8b2f22744b5e md5: ad77674e1f893b3ffe27ffa532e2724f depends: - __osx >=11.0 - joblib >=1.2.0 - libcxx >=17 - llvm-openmp >=17.0.6 - numpy >=1.19,<3 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 - scipy - threadpoolctl >=3.1.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9729967 timestamp: 1726083178729 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.6.1-py312h39203ce_0.conda sha256: 63e7751b861b5d8a6bfe32a58e67b446b8235f8768e860db955b394e4c7a9edc md5: 3d38707ed1991a65dd165c5460d7f3a2 depends: - __osx >=11.0 - joblib >=1.2.0 - libcxx >=18 - llvm-openmp >=18.1.8 - numpy >=1.19,<3 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 - scipy - threadpoolctl >=3.1.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9769459 timestamp: 1736497509734 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.7.2-py310h660f142_0.conda sha256: f7960d72c7d36e1830d985d70587bc85e571d4bdcdea3cfea6e81602e2a222ea md5: 26f940c6a467ed3fe2b4fbab43ab312e depends: - __osx >=11.0 - joblib >=1.2.0 - libcxx >=19 - llvm-openmp >=19.1.7 - numpy >=1.21,<3 - numpy >=1.22.0 - python >=3.10,<3.11.0a0 - python >=3.10,<3.11.0a0 *_cpython - python_abi 3.10.* *_cp310 - scipy >=1.8.0 - threadpoolctl >=3.1.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 7749690 timestamp: 1757407202358 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py311hf1dd2ad_0.conda sha256: 65772371eb10e008576d22a52982517153958e08c2cb64971bbd6e499ee65498 md5: f4c90a74c14bbbb86e1ae8f8526d75f8 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.4.0 - threadpoolctl >=3.5.0 - narwhals >=2.0.1 - llvm-openmp >=19.1.7 - libcxx >=19 - __osx >=11.0 - python 3.11.* *_cpython - python_abi 3.11.* *_cp311 - numpy >=1.23,<3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=compressed-mapping size: 9668485 timestamp: 1780401272693 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py312he5ca3e3_0.conda sha256: e140dce2c42d0a121a113e29fbde4fd95a69cdf528ea195558ba25b809b439b7 md5: b2b777cdad320a08da92bb1f58040bfd depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.4.0 - threadpoolctl >=3.5.0 - narwhals >=2.0.1 - python 3.12.* *_cpython - llvm-openmp >=19.1.7 - libcxx >=19 - __osx >=11.0 - numpy >=1.23,<3 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=compressed-mapping size: 9400478 timestamp: 1780401354221 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.9.0-np2py314h15f0f0f_0.conda sha256: c5dc417c26c46eecf7e8931c53a4c18bcd2c274c994ee80bae4767baeed4807c md5: 72cd17b6f8016221faaa96123711f8c9 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.4.0 - threadpoolctl >=3.5.0 - narwhals >=2.0.1 - python 3.14.* *_cp314 - __osx >=11.0 - llvm-openmp >=19.1.7 - libcxx >=19 - python_abi 3.14.* *_cp314 - numpy >=1.23,<3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=compressed-mapping size: 9667030 timestamp: 1780401292916 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.11.4-py310h2b794db_0.conda sha256: bf95a330f5568456378ba51311b859db04e1f0e2b61970d69aad9eea8ee88d8b md5: 63565e585933d2853fa3c33d2e4c1007 depends: - __osx >=10.9 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=16.0.6 - libgfortran >=5 - libgfortran5 >=12.3.0 - libgfortran5 >=13.2.0 - liblapack >=3.9.0,<4.0a0 - numpy >=1.22.4,<1.28 - numpy >=1.22.4,<2.0a0 - python >=3.10,<3.11.0a0 - python >=3.10,<3.11.0a0 *_cpython - python_abi 3.10.* *_cp310 constrains: - libopenblas <0.3.26 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 13965072 timestamp: 1700814632317 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.15.2-py310h32ab4ed_0.conda sha256: f6ff2c1ba4775300199e8bc0331d2e2ccb5906f58f3835c5426ddc591c9ad7bf md5: a389f540c808b22b3c696d7aea791a41 depends: - __osx >=11.0 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=18 - libgfortran >=5 - libgfortran5 >=13.2.0 - liblapack >=3.9.0,<4.0a0 - numpy <2.5 - numpy >=1.19,<3 - numpy >=1.23.5 - python >=3.10,<3.11.0a0 - python >=3.10,<3.11.0a0 *_cpython - python_abi 3.10.* *_cp310 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 13507343 timestamp: 1739792089317 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.15.2-py311h0675101_0.conda sha256: bc3e873e85c55deaaad446c410d9001d12a133c1b48fa2cb0050b4f46f926aa3 md5: df904770f3fdb6c0265a09cdc22acf54 depends: - __osx >=11.0 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=18 - libgfortran >=5 - libgfortran5 >=13.2.0 - liblapack >=3.9.0,<4.0a0 - numpy <2.5 - numpy >=1.19,<3 - numpy >=1.23.5 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 14569129 timestamp: 1739792318601 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.15.2-py312h99a188d_0.conda sha256: af61f6e29a0d3d4c66699a35b19ce6849d6e0fa15017d7a9ef6268cc1c4e1264 md5: b1d324bf5018b451152bbdc4ffd3d378 depends: - __osx >=11.0 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=18 - libgfortran >=5 - libgfortran5 >=13.2.0 - liblapack >=3.9.0,<4.0a0 - numpy <2.5 - numpy >=1.19,<3 - numpy >=1.23.5 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 14394729 timestamp: 1739792424558 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py311h9a58382_1.conda sha256: b45f87414da242a9e40eb934e89513a856e6236d681611c2c9a21d074b03ef5a md5: 15f96f91b13cbefddbf998368d06adef depends: - __osx >=11.0 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=19 - libgfortran - libgfortran5 >=14.3.0 - liblapack >=3.9.0,<4.0a0 - numpy <2.7 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=compressed-mapping size: 13954661 timestamp: 1779874558902 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py312h4519d97_1.conda sha256: c0ed2cbfa3485bac8570e52b577475778c603ee92d078a8b164d1ddec992e577 md5: 173d5eeba324363d9171946e86a81687 depends: - __osx >=11.0 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=19 - libgfortran - libgfortran5 >=14.3.0 - liblapack >=3.9.0,<4.0a0 - numpy <2.7 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=compressed-mapping size: 13936510 timestamp: 1779874824714 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.17.1-py314h18e1515_1.conda sha256: d9742c04d44f78d2628899ad017f23e404e08f28118fcfbbf6722259cbd56eab md5: 9958307c22c5b53165e46719ebe8972d depends: - __osx >=11.0 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libcxx >=19 - libgfortran - libgfortran5 >=14.3.0 - liblapack >=3.9.0,<4.0a0 - numpy <2.7 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=compressed-mapping size: 13975038 timestamp: 1779874613589 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/snappy-1.2.2-hada39a4_1.conda sha256: cb9305ede19584115f43baecdf09a3866bfcd5bcca0d9e527bd76d9a1dbe2d8d md5: fca4a2222994acd7f691e57f94b750c5 depends: - libcxx >=19 - __osx >=11.0 license: BSD-3-Clause license_family: BSD purls: [] size: 38883 timestamp: 1762948066818 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/statsmodels-0.14.6-py311h09efe57_0.conda sha256: 22736e40dc1f44af54ad1222832f294e3a9c6e601c09f7ebbaed0099e8c672b0 md5: 0add168f6a2b13e2de9cefef01889f3b depends: - __osx >=11.0 - numpy <3,>=1.22.3 - numpy >=1.23,<3 - packaging >=21.3 - pandas !=2.1.0,>=1.4 - patsy >=0.5.6 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 - scipy !=1.9.2,>=1.8 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/statsmodels?source=hash-mapping size: 11772228 timestamp: 1764983597603 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/statsmodels-0.14.6-py312ha11c99a_0.conda sha256: 18f8711f235e32d793938e1738057e7be1d0bfe98f7d27e3e4b98aa757deae92 md5: 31f49265d8de9776cd15b421f24b23e0 depends: - __osx >=11.0 - numpy <3,>=1.22.3 - numpy >=1.23,<3 - packaging >=21.3 - pandas !=2.1.0,>=1.4 - patsy >=0.5.6 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 - scipy !=1.9.2,>=1.8 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/statsmodels?source=hash-mapping size: 11537488 timestamp: 1764984166760 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorboard-data-server-0.7.0-py310h0cf7f6c_4.conda sha256: d47915ededc75fd82372def108e0de7558f832e7fe2018513a9baa8448625b72 md5: f1852a0a0fbe796e7be6fa79c991eed8 depends: - __osx >=11.0 - openssl >=3.5.4,<4.0a0 - python >=3.10,<3.11.0a0 - python >=3.10,<3.11.0a0 *_cpython - python_abi 3.10.* *_cp310 constrains: - __osx >=11.0 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tensorboard-data-server?source=hash-mapping size: 3206962 timestamp: 1764930740737 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorboard-data-server-0.7.0-py311h806dddb_4.conda sha256: 02eef87c0f4d66e476e047ffaca227da867becb4573e31ffd82825fbd25e005d md5: 7e4984b810edd834811a81c0939359cc depends: - __osx >=11.0 - openssl >=3.5.4,<4.0a0 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 constrains: - __osx >=11.0 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tensorboard-data-server?source=hash-mapping size: 3207444 timestamp: 1764930255107 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorboard-data-server-0.7.0-py312h9536bd2_4.conda sha256: a76bf47a5d9ad1134d6025c307773e20596f37b90682b14f1965c18f6b87a7da md5: 2c911f542edf82b500cb08f6164cd3e7 depends: - __osx >=11.0 - openssl >=3.5.4,<4.0a0 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 constrains: - __osx >=11.0 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tensorboard-data-server?source=hash-mapping size: 3207231 timestamp: 1764930138005 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-2.16.1-cpu_py310h5a6a72a_0.conda sha256: eec803cb4c61a7747bb7b768494e35d354dbc00c0a39821ae3d3f09c4689e557 md5: 465e37e7d9aec99b411df184a1a8b464 depends: - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - tensorflow-base 2.16.1 cpu_py310hf600a1e_0 - tensorflow-estimator 2.16.1 cpu_py310h85e80fb_0 track_features: - tensorflow-cpu license: Apache-2.0 license_family: Apache purls: [] size: 42718 timestamp: 1716417160662 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-2.19.1-cpu_py311he75e561_5.conda sha256: 163a9091d06c9a1c96f6350e5f9152a023b34d78be083961ab9c631fc240a9e3 md5: d8943be0b7c0f6ceabc1674f8d45b86d depends: - python - tensorflow-base ==2.19.1 cpu_py311hf3798af_5 - python_abi 3.11.* *_cp311 license: Apache-2.0 license_family: APACHE purls: [] size: 47384 timestamp: 1778106412562 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-2.19.1-cpu_py312hc65d1cb_5.conda sha256: 5e531150bb912ba9e8ea6cfc7c3af8e619829fc16b511cc9752e072444ac14d8 md5: 7119cefd8ff100262aa43ca9ffa76927 depends: - python - tensorflow-base ==2.19.1 cpu_py312h66a5cad_5 - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: APACHE purls: [] size: 47381 timestamp: 1778106412562 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-base-2.16.1-cpu_py310hf600a1e_0.conda sha256: 03dabfedcb35d8c2ba7aa6d0e1d711200280d41d5d1be40b96977ae793f30d63 md5: a1db4f5fab7f4d0e9652f7cc8035854b depends: - __osx >=11.0 - absl-py >=1.0.0 - astunparse >=1.6.0 - flatbuffers >=24.3.25,<24.3.26.0a0 - gast >=0.2.1,!=0.5.0,!=0.5.1,!=0.5.2 - giflib >=5.2.2,<5.3.0a0 - google-pasta >=0.1.1 - grpcio 1.62.* - h5py >=3.10 - icu >=73.2,<74.0a0 - keras >=3.0 - libabseil * cxx17* - libabseil >=20240116.2,<20240117.0a0 - libcurl >=8.8.0,<9.0a0 - libcxx >=16 - libgrpc >=1.62.2,<1.63.0a0 - libjpeg-turbo >=3.0.0,<4.0a0 - libpng >=1.6.43,<1.7.0a0 - libprotobuf >=4.25.3,<4.25.4.0a0 - libsqlite >=3.45.3,<4.0a0 - libzlib >=1.2.13,<2.0.0a0 - ml_dtypes >=0.3.1,<0.4 - numpy >=1.22,<2.0a0 - numpy >=1.22.4,<2.0a0 - openssl >=3.3.0,<4.0a0 - opt_einsum >=2.3.2 - packaging - protobuf >=3.20.3,<5,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5 - python >=3.10,<3.11.0a0 - python-flatbuffers >=23.5.26 - python_abi 3.10.* *_cp310 - requests >=2.21.0,<3 - six >=1.12 - snappy >=1.2.0,<1.3.0a0 - tensorboard >=2.16,<2.17 - termcolor >=1.1.0 - typing_extensions >=3.6.6 - wrapt >=1.11.0 track_features: - tensorflow-cpu license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tensorflow?source=hash-mapping size: 148970074 timestamp: 1716416801713 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-base-2.19.1-cpu_py311hf3798af_5.conda sha256: 2654f6c7f14e90f642ae67c82af76ba34d59edb6b1ec0c47afc53d8c097d933d md5: 96ea1c5ce0096c6eb34acd1ca4d716f7 depends: - python - packaging - absl-py >=1.0.0 - astunparse >=1.6.0 - gast >=0.2.1,!=0.5.0,!=0.5.1,!=0.5.2 - google-pasta >=0.1.1 - grpcio 1.78.* - h5py >=3.11 - ml_dtypes >=0.5.1,<1.0 - opt_einsum >=2.3.2 - protobuf >=5.26 - python-flatbuffers >=24.3.25 - requests >=2.21.0,<3 - six >=1.12 - termcolor >=1.1.0 - typing_extensions >=3.6.6 - wrapt >=1.11.0 - tensorboard >=2.19,<2.20 - keras >=3.5 - libtensorflow_framework ==2.19.1 cpu_h3441331_5 - libtensorflow_cc ==2.19.1 cpu_he004e32_5 - libcxx >=19 - __osx >=11.0 - python_abi 3.11.* *_cp311 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tensorflow?source=hash-mapping size: 315094886 timestamp: 1778106412562 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-base-2.19.1-cpu_py312h66a5cad_5.conda sha256: 1a5d05ac1577bc3bd435ac3f3985d43589b446be93038cf6bf7aaf294a7e6ad6 md5: 6afaa7d100fc4b093ed9a1e1da41dc82 depends: - python - packaging - absl-py >=1.0.0 - astunparse >=1.6.0 - gast >=0.2.1,!=0.5.0,!=0.5.1,!=0.5.2 - google-pasta >=0.1.1 - grpcio 1.78.* - h5py >=3.11 - ml_dtypes >=0.5.1,<1.0 - opt_einsum >=2.3.2 - protobuf >=5.26 - python-flatbuffers >=24.3.25 - requests >=2.21.0,<3 - six >=1.12 - termcolor >=1.1.0 - typing_extensions >=3.6.6 - wrapt >=1.11.0 - tensorboard >=2.19,<2.20 - keras >=3.5 - libtensorflow_framework ==2.19.1 cpu_h3441331_5 - libtensorflow_cc ==2.19.1 cpu_he004e32_5 - __osx >=11.0 - libcxx >=19 - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tensorflow?source=hash-mapping size: 314828014 timestamp: 1778106412562 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-estimator-2.16.1-cpu_py310h85e80fb_0.conda sha256: 15638b4d63bb2a761cf5f8e2f905be91b3186f5bf40ddbeb1f3afe49da2ef87e md5: 3a152ec5c177114053878a239868dadf depends: - __osx >=11.0 - libcxx >=16 - openssl >=3.3.0,<4.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - tensorflow-base 2.16.1 cpu_py310hf600a1e_0 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tensorflow-estimator?source=hash-mapping size: 553671 timestamp: 1716417145716 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda sha256: 799cab4b6cde62f91f750149995d149bc9db525ec12595e8a1d91b9317f038b3 md5: a9d86bc62f39b94c4661716624eb21b0 depends: - __osx >=11.0 - libzlib >=1.3.1,<2.0a0 license: TCL license_family: BSD purls: [] size: 3127137 timestamp: 1769460817696 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.6-py311hc949640_0.conda sha256: e1d5d991df31da1b47b42eccfe9c3632a7fc60b7ec1f3cc05ebb1143ccfaa1e0 md5: ffc478aa371d82f31d51447f06ac46af depends: - __osx >=11.0 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tornado?source=hash-mapping size: 879906 timestamp: 1779916446955 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.6-py312h2bbb03f_0.conda sha256: 5e4000ea70c309e30ccd09844c2f1e8b92b4e875b577827dccaf81a68ed161c0 md5: 44b900ed215ce286d655b6cc18ef7261 depends: - __osx >=11.0 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tornado?source=hash-mapping size: 862347 timestamp: 1779916294007 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ukkonen-1.1.0-py311h572238d_0.conda sha256: 1bf370d4d4698b339d54342b2d653d9ed26f5004af2da3e5556b641fb5e56e1b md5: 2855259ce88c41c1b2297041ec75f540 depends: - __osx >=11.0 - cffi - libcxx >=19 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/ukkonen?source=hash-mapping size: 14760 timestamp: 1769438970347 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py311hc949640_0.conda sha256: 984d3b0ddb0802c228c520db31d906b5b546b13da3eca1ce35c754d97c012497 md5: a9d9010d246205c63f824b0bcf050acd depends: - __osx >=11.0 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/unicodedata2?source=hash-mapping size: 416024 timestamp: 1770909604661 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.1-py312h2bbb03f_0.conda sha256: e935d0c11581e31e89ce4899a28b16f924d1a3c1af89f18f8a2c5f5728b3107f md5: 45b836f333fd3e282c16fff7dc82994e depends: - __osx >=11.0 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/unicodedata2?source=hash-mapping size: 415828 timestamp: 1770909782683 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.2.1-py310h72544b6_0.conda sha256: d17e29c05d2eeda00e78aad094356e47bc1ca67da56f9b0d1d901e004d9a6206 md5: 9c5e657ed3dc9d7b5ee5c4941e31c084 depends: - __osx >=11.0 - python >=3.10,<3.11.0a0 - python >=3.10,<3.11.0a0 *_cpython - python_abi 3.10.* *_cp310 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/wrapt?source=hash-mapping size: 97405 timestamp: 1779478046800 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.2.1-py311hc949640_0.conda sha256: 2541189672f4ab14af7df4e9b92adc03029c955023d5819e531790a7f423c63f md5: 7961f1a5c00e95c91a4e7c1ed4d7a860 depends: - __osx >=11.0 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/wrapt?source=hash-mapping size: 112977 timestamp: 1779477735853 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.2.1-py312h2bbb03f_0.conda sha256: baefb9a73519863a70f6e7a6f6acb982ee5485ae986a16960ff1e8cb99f1a023 md5: a82ae4e18607e068c9259136d07e4f87 depends: - __osx >=11.0 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/wrapt?source=hash-mapping size: 111458 timestamp: 1779477998634 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxau-1.0.12-hc919400_1.conda sha256: adae11db0f66f86156569415ed79cda75b2dbf4bea48d1577831db701438164f md5: 78b548eed8227a689f93775d5d23ae09 depends: - __osx >=11.0 license: MIT license_family: MIT purls: [] size: 14105 timestamp: 1762976976084 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/xorg-libxdmcp-1.1.5-hc919400_1.conda sha256: f7fa0de519d8da589995a1fe78ef74556bb8bc4172079ae3a8d20c3c81354906 md5: 9d1299ace1924aa8f4e0bc8e71dd0cf7 depends: - __osx >=11.0 license: MIT license_family: MIT purls: [] size: 19156 timestamp: 1762977035194 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda sha256: b03433b13d89f5567e828ea9f1a7d5c5d697bf374c28a4168d71e9464f5dafac md5: 78a0fe9e9c50d2c381e8ee47e3ea437d depends: - __osx >=11.0 license: MIT license_family: MIT purls: [] size: 83386 timestamp: 1753484079473 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-h10816f8_11.conda sha256: 01fd50d2801b23b59fafea6bf704a6c5faf0f5969104400eae0e6572cb2e5304 md5: d31c0e54c4f9c51100ec8c812ee925d1 depends: - libcxx >=19 - __osx >=11.0 - krb5 >=1.22.2,<1.23.0a0 - libsodium >=1.0.22,<1.0.23.0a0 license: MPL-2.0 license_family: MOZILLA purls: [] size: 245404 timestamp: 1779124076307 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.3-hed4e4f5_1.conda sha256: a339606a6b224bb230ff3d711e801934f3b3844271df9720165e0353716580d4 md5: d99c2a23a31b0172e90f456f580b695e depends: - __osx >=11.0 - libcxx >=19 license: Zlib license_family: Other purls: [] size: 94375 timestamp: 1770168363685 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda sha256: 9485ba49e8f47d2b597dd399e88f4802e100851b27c21d7525625b0b4025a5d9 md5: ab136e4c34e97f34fb621d2592a393d8 depends: - __osx >=11.0 - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 433413 timestamp: 1764777166076 - conda: https://conda.anaconda.org/conda-forge/win-64/black-23.3.0-py311h1ea47a8_1.conda sha256: 12f319748c11697ee3ea154413c7c4895db2dfcc18b44b0bdb2019da1144abdb md5: cff72d53d4ea05005adc2f478447a0a1 depends: - click >=8.0.0 - mypy_extensions >=0.4.3 - packaging >=22.0 - pathspec >=0.9 - platformdirs >=2 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/black?source=hash-mapping size: 369420 timestamp: 1682492626505 - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda sha256: 76dfb71df5e8d1c4eded2dbb5ba15bb8fb2e2b0fe42d94145d5eed4c75c35902 md5: 4cb8e6b48f67de0b018719cdf1136306 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: bzip2-1.0.6 license_family: BSD purls: [] size: 56115 timestamp: 1771350256444 - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py311h3485c13_1.conda sha256: c9caca6098e3d92b1a269159b759d757518f2c477fbbb5949cb9fee28807c1f1 md5: f02335db0282d5077df5bc84684f7ff9 depends: - pycparser - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT license_family: MIT purls: - pkg:pypi/cffi?source=hash-mapping size: 297941 timestamp: 1761203850323 - conda: https://conda.anaconda.org/conda-forge/win-64/coverage-7.14.1-py310hdb0e946_0.conda sha256: d17d7a12ead876dd09f1b34f798e175d3721edbef9224dd92318d657f09ab8f3 md5: fdceaa71d5c53f4aabffe51b7b6184a4 depends: - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - tomli - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/coverage?source=hash-mapping size: 340094 timestamp: 1779838004649 - conda: https://conda.anaconda.org/conda-forge/win-64/coverage-7.14.1-py311h3f79411_0.conda sha256: 491d53a03c413dc3699862d96feecbe22b0fda5d2f9e91066ed1eae6cb220793 md5: 0aa2991504a7e9144b5dae2f684fd4d6 depends: - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - tomli - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/coverage?source=hash-mapping size: 424714 timestamp: 1779838002255 - conda: https://conda.anaconda.org/conda-forge/win-64/coverage-7.14.1-py312h05f76fc_0.conda sha256: a37669c35673713cd5b1006958754d2f5ba8f0c10a85796579eea2dc9260f94c md5: 4b355df4eedcf321296c32d8ca3fcfb7 depends: - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - tomli - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/coverage?source=hash-mapping size: 413239 timestamp: 1779838010995 - conda: https://conda.anaconda.org/conda-forge/win-64/coverage-7.14.1-py314h2359020_0.conda sha256: 9bd2e2e705d44961482bc58339fe3d456cbbdbc16520c607be9609601c39e5ba md5: 442d8dfea629c6a1c46347db9a5ec974 depends: - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 - tomli - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/coverage?source=hash-mapping size: 440396 timestamp: 1779838003568 - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-8_h8455456_mkl.conda build_number: 8 sha256: 43a87b59e6d4c68d80b2e4de487b1b54d66fe1f9a06636909b5a5ab9eae27269 md5: 4a0ce24b1a946ff77ae9eaa7ef015a33 depends: - mkl >=2026.0.0,<2027.0a0 constrains: - libcblas 3.11.0 8*_mkl - liblapacke 3.11.0 8*_mkl - blas 2.308 mkl - liblapack 3.11.0 8*_mkl license: BSD-3-Clause license_family: BSD purls: [] size: 68103 timestamp: 1779859688049 - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-8_h2a3cdd5_mkl.conda build_number: 8 sha256: 2a5b6555b481df4603e44cba49a6ef727584fd2f3c5235dd4bcb3028fffbdfb5 md5: 09f1d8e4d2675d34ad2acb115211d10c depends: - libblas 3.11.0 8_h8455456_mkl constrains: - liblapacke 3.11.0 8*_mkl - blas 2.308 mkl - liblapack 3.11.0 8*_mkl license: BSD-3-Clause license_family: BSD purls: [] size: 68443 timestamp: 1779859701498 - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.8.1-hac47afa_0.conda sha256: a65e518c20d1482182bc0f1f6dd5d992f25ca44c3b32307be39ae8310db8f060 md5: 23eb9474a16d4b9f6f27429989e82002 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - expat 2.8.1.* license: MIT license_family: MIT purls: [] size: 71280 timestamp: 1779278786150 - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h3d046cb_0.conda sha256: 59d01f2dfa8b77491b5888a5ab88ff4e1574c9359f7e229da254cdfe27ddc190 md5: 720b39f5ec0610457b725eb3f396219a depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT license_family: MIT purls: [] size: 45831 timestamp: 1769456418774 - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.13.0-default_h049141e_1000.conda sha256: 2ee12e37223dfcd0acd050c80a91150c482b6e2899198521e1800dce66662467 md5: 6a01c986e30292c715038d2788aa1385 depends: - libwinpthread >=12.0.0.r4.gg4f2fc60ca - libxml2 - libxml2-16 >=2.14.6 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD purls: [] size: 2396128 timestamp: 1770954127918 - conda: https://conda.anaconda.org/conda-forge/win-64/libiconv-1.18-hc1393d2_2.conda sha256: 0dcdb1a5f01863ac4e8ba006a8b0dc1a02d2221ec3319b5915a1863254d7efa7 md5: 64571d1dd6cdcfa25d0664a5950fdaa2 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: LGPL-2.1-only purls: [] size: 696926 timestamp: 1754909290005 - conda: https://conda.anaconda.org/conda-forge/win-64/liblapack-3.11.0-8_hf9ab0e9_mkl.conda build_number: 8 sha256: 44999ed04bc0a56de44ee0ac8bd5b3702efd411a8b29491c0e3d3deb8619c94e md5: d584799b920ecae9b75a2b70743a3de7 depends: - libblas 3.11.0 8_h8455456_mkl constrains: - libcblas 3.11.0 8*_mkl - liblapacke 3.11.0 8*_mkl - blas 2.308 mkl license: BSD-3-Clause license_family: BSD purls: [] size: 81027 timestamp: 1779859714698 - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.3-hfd05255_0.conda sha256: d636d1a25234063642f9c531a7bb58d84c1c496411280a36ea000bd122f078f1 md5: 8f83619ab1588b98dd99c90b0bfc5c6d depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - xz 5.8.3.* license: 0BSD purls: [] size: 106486 timestamp: 1775825663227 - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-hfd05255_1.conda sha256: 40dcd0b9522a6e0af72a9db0ced619176e7cfdb114855c7a64f278e73f8a7514 md5: e4a9fc2bba3b022dad998c78856afe47 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: BSD-2-Clause license_family: BSD purls: [] size: 89411 timestamp: 1769482314283 - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.2-hf5d6505_0.conda sha256: 4cd81319dcc58fb758da20a6d5595950c021adc2c18d7cffeadcfb590529629f md5: df294e7f9f24a6063f0e226f4d028fda depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: blessing purls: [] size: 1313306 timestamp: 1780574491977 - conda: https://conda.anaconda.org/conda-forge/win-64/libwinpthread-12.0.0.r4.gg4f2fc60ca-h57928b3_10.conda sha256: 0fccf2d17026255b6e10ace1f191d0a2a18f2d65088fd02430be17c701f8ffe0 md5: 8a86073cf3b343b87d03f41790d8b4e5 depends: - ucrt constrains: - pthreads-win32 <0.0a0 - msys2-conda-epoch <0.0a0 license: MIT AND BSD-3-Clause-Clear purls: [] size: 36621 timestamp: 1759768399557 - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.3-h692994f_0.conda sha256: 8038084c60eda2006d0122d05e3364fe8db0a18935ca6ed0168b5ba5aa33f904 md5: f7d6fcda29570e20851b78d92ea2154e depends: - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - libzlib >=1.3.2,<2.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - libxml2 2.15.3 - icu <0.0a0 license: MIT license_family: MIT purls: [] size: 518869 timestamp: 1776376971242 - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.3-hbc0d294_0.conda sha256: da68af9d9d28d65a6916db1bef68f8a25c64c4fdcf759f32a2d2f2f143220adf md5: e3b5acbb857a12f5d59e8d174bc536c0 depends: - libiconv >=1.18,<2.0a0 - liblzma >=5.8.3,<6.0a0 - libxml2-16 2.15.3 h692994f_0 - libzlib >=1.3.2,<2.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - icu <0.0a0 license: MIT license_family: MIT purls: [] size: 43916 timestamp: 1776376994334 - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda sha256: 88609816e0cc7452bac637aaf65783e5edf4fee8a9f8e22bdc3a75882c536061 md5: dbabbd6234dea34040e631f87676292f depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - zlib 1.3.2 *_2 license: Zlib license_family: Other purls: [] size: 58347 timestamp: 1774072851498 - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-22.1.7-h4fa8253_0.conda sha256: 70140a1fa5d7cb801c6be3273b0704b5f0e418e2fff6b12b8ce9db13067a1ed5 md5: 0ca3373049a5be11689bc2f9b2f3a9d2 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - intel-openmp <0.0a0 - openmp 22.1.7|22.1.7.* license: Apache-2.0 WITH LLVM-exception license_family: APACHE purls: [] size: 347536 timestamp: 1780456277495 - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2026.0.0-hac47afa_908.conda sha256: f997bfc9bc4d4e14261cdcd1ad195d64a72ee44dca3145d24c1349f8d1311aa5 md5: 36ea6e1292e9d5e89374201da79646ef depends: - llvm-openmp >=22.1.5 - onemkl-license 2026.0.0 h57928b3_908 - tbb >=2023.0.0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: LicenseRef-IntelSimplifiedSoftwareOct2022 license_family: Proprietary purls: [] size: 114354729 timestamp: 1779293121860 - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-1.25.2-py310hd02465a_0.conda sha256: 4418a99e711ed162b69d57f3c277f5e4999501dc80c89fb75f51cc59abfbcdc4 md5: faeadcd33c1207e7bedd0ee8621ba25a depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping size: 6122526 timestamp: 1691057309489 - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.4.6-py311h65cb7f3_0.conda sha256: cd26f615140d0ed557f8927947ca62c181d55ddbe418eebd24bd06cd32fb3938 md5: ef5c1dedd943abfb0b80112ba46d4ab8 depends: - python - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - liblapack >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - python_abi 3.11.* *_cp311 - libblas >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=compressed-mapping size: 7807344 timestamp: 1779169235300 - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.4.6-py312ha3f287d_0.conda sha256: 9abf760418f2497f87715fa35d1c9cea5416be9edd1b166a218dfabe3b16e5be md5: 1dd6f497cc8369359f024463426e323c depends: - python - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - liblapack >=3.9.0,<4.0a0 - python_abi 3.12.* *_cp312 - libcblas >=3.9.0,<4.0a0 - libblas >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=compressed-mapping size: 7169449 timestamp: 1779169226122 - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.4.6-py314h02f10f6_0.conda sha256: de0eee21d902fb45a58454e3739e04ede7d02bf7575ca0ae9f959f20fa15c76b md5: df95e6c7325bbae2571e5cef5f9c8096 depends: - python - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - libcblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 - libblas >=3.9.0,<4.0a0 - python_abi 3.14.* *_cp314 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping size: 7318163 timestamp: 1779169232086 - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.4.6-py314hffb9209_0.conda sha256: 1a5b5e4bfecbc5de8cbf2ae04a2a31f45ceeac454be96eb931abc9a78a06b24c md5: 42e4fcaa6afcb0d68a9b73c3eff251da depends: - python - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - python_abi 3.14.* *_cp314t - liblapack >=3.9.0,<4.0a0 - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping size: 7482657 timestamp: 1779169231603 - conda: https://conda.anaconda.org/conda-forge/win-64/onemkl-license-2026.0.0-h57928b3_908.conda sha256: 42ad15cbb3bf31830efa04d4b86dd2d5c0dd590c86f98adcd3c8c1f75acf5dd5 md5: 9c9303e08b50e09f5c23e1dac99d0936 license: LicenseRef-IntelSimplifiedSoftwareOct2022 license_family: Proprietary purls: [] size: 41580 timestamp: 1779292867015 - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.2-hf411b9b_0.conda sha256: feb5815125c60f2be4a411e532db1ed1cd2d7261a6a43c54cb6ae90724e2e154 md5: 05c7d624cff49dbd8db1ad5ba537a8a3 depends: - ca-certificates - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache purls: [] size: 9410183 timestamp: 1775589779763 - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-2.0.3-py310h1c4a608_1.conda sha256: 2bd9ed92347d53d7ce99b3b45b832ecd6c3dd9ae68ca7153841dcab943ba6a85 md5: 9fb6d4a7414daf2aa66ddfa14d568fd0 depends: - numpy >=1.21.6,<2.0a0 - python >=3.10,<3.11.0a0 - python-dateutil >=2.8.1 - python-tzdata >=2022a - python_abi 3.10.* *_cp310 - pytz >=2020.1 - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pandas?source=hash-mapping size: 11000365 timestamp: 1688741630200 - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-2.3.3-py314h0757d37_2.conda sha256: cd7a6eefe7c6f1c39ee9d699c680afc479653ff1237c8d19a48c8ed1b8d57d11 md5: ca1a83479b3f83552ab703033810d79c depends: - numpy >=1.22.4 - numpy >=1.23,<3 - python >=3.14,<3.15.0a0 - python-dateutil >=2.8.2 - python-tzdata >=2022.7 - python_abi 3.14.* *_cp314t - pytz >=2020.1 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - xlsxwriter >=3.0.5 - beautifulsoup4 >=4.11.2 - sqlalchemy >=2.0.0 - matplotlib >=3.6.3 - numexpr >=2.8.4 - pytables >=3.8.0 - pandas-gbq >=0.19.0 - fsspec >=2022.11.0 - psycopg2 >=2.9.6 - html5lib >=1.1 - qtpy >=2.3.0 - python-calamine >=0.1.7 - openpyxl >=3.1.0 - pyxlsb >=1.0.10 - odfpy >=1.4.1 - tabulate >=0.9.0 - blosc >=1.21.3 - fastparquet >=2022.12.0 - s3fs >=2022.11.0 - tzdata >=2022.7 - xarray >=2022.12.0 - bottleneck >=1.3.6 - pyarrow >=10.0.1 - gcsfs >=2022.11.0 - numba >=0.56.4 - lxml >=4.9.2 - pyqt5 >=5.15.9 - pyreadstat >=1.2.0 - scipy >=1.10.0 - zstandard >=0.19.0 - xlrd >=2.0.1 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pandas?source=hash-mapping size: 14671884 timestamp: 1764615410770 - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-2.3.3-py314hd8fd7ce_2.conda sha256: a1c87d34f72d6ae3f78203c60cf1b1adfb8d5cf55a3fc90f47e9f9ed50eb8b91 md5: 95cf7fc22f898b6faeb1d62ce2f5b82c depends: - numpy >=1.22.4 - numpy >=1.23,<3 - python >=3.14,<3.15.0a0 - python-dateutil >=2.8.2 - python-tzdata >=2022.7 - python_abi 3.14.* *_cp314 - pytz >=2020.1 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - scipy >=1.10.0 - sqlalchemy >=2.0.0 - fsspec >=2022.11.0 - pyreadstat >=1.2.0 - gcsfs >=2022.11.0 - tabulate >=0.9.0 - openpyxl >=3.1.0 - pytables >=3.8.0 - qtpy >=2.3.0 - matplotlib >=3.6.3 - bottleneck >=1.3.6 - python-calamine >=0.1.7 - numba >=0.56.4 - beautifulsoup4 >=4.11.2 - tzdata >=2022.7 - xarray >=2022.12.0 - pyqt5 >=5.15.9 - odfpy >=1.4.1 - xlrd >=2.0.1 - pyarrow >=10.0.1 - s3fs >=2022.11.0 - psycopg2 >=2.9.6 - pandas-gbq >=0.19.0 - xlsxwriter >=3.0.5 - fastparquet >=2022.12.0 - numexpr >=2.8.4 - zstandard >=0.19.0 - lxml >=4.9.2 - pyxlsb >=1.0.10 - html5lib >=1.1 - blosc >=1.21.3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pandas?source=hash-mapping size: 14046781 timestamp: 1764615388271 - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.10.20-hc20f281_0_cpython.conda sha256: e71595dd281a9902d7b84f545f16d7d4c0fb62cc6816431301f8f4870c94dc8c md5: 6c18c24d33a7ac8a4f81c68b8eb8581b depends: - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.4,<3.0a0 - libffi >=3.4,<4.0a0 - liblzma >=5.8.2,<6.0a0 - libsqlite >=3.51.2,<4.0a0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.5,<4.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - python_abi 3.10.* *_cp310 license: Python-2.0 purls: [] size: 16028082 timestamp: 1772728853200 - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.11.15-h0159041_0_cpython.conda sha256: a1f1031088ce69bc99c82b95980c1f54e16cbd5c21f042e9c1ea25745a8fc813 md5: d09dbf470b41bca48cbe6a78ba1e009b depends: - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.4,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.2,<6.0a0 - libsqlite >=3.51.2,<4.0a0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.5,<4.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - python_abi 3.11.* *_cp311 license: Python-2.0 purls: [] size: 18416208 timestamp: 1772728847666 - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.12.13-h0159041_0_cpython.conda sha256: a02b446d8b7b167b61733a3de3be5de1342250403e72a63b18dac89e99e6180e md5: 2956dff38eb9f8332ad4caeba941cfe7 depends: - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.4,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.2,<6.0a0 - libsqlite >=3.51.2,<4.0a0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.5,<4.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - python_abi 3.12.* *_cp312 license: Python-2.0 purls: [] size: 15840187 timestamp: 1772728877265 - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.5-h4b44e0e_100_cp314.conda build_number: 100 sha256: c561d171e5d1f1bb1a83ca6fa6aa49577a2956a245c5040dfaf8ca20c10a096e md5: 3f76bc298eebc1ec1497852f4d7f09d9 depends: - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.8.0,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.3,<6.0a0 - libmpdec >=4.0.0,<5.0a0 - libsqlite >=3.53.1,<4.0a0 - libzlib >=1.3.2,<2.0a0 - openssl >=3.5.6,<4.0a0 - python_abi 3.14.* *_cp314 - tk >=8.6.13,<8.7.0a0 - tzdata - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - zstd >=1.5.7,<1.6.0a0 license: Python-2.0 purls: [] size: 18375338 timestamp: 1779237800732 python_site_packages_path: Lib/site-packages - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.5-h7c1dbca_0_cp314t.conda sha256: 7f5447a46dd8e79e41231c9c085de080371e1bc89163667c109e25fc21dc899e md5: 0d1f03b9416e6ffe55cc446cdab331e4 depends: - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.8.0,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.3,<6.0a0 - libmpdec >=4.0.0,<5.0a0 - libsqlite >=3.53.1,<4.0a0 - libzlib >=1.3.2,<2.0a0 - openssl >=3.5.6,<4.0a0 - python_abi 3.14.* *_cp314t - tk >=8.6.13,<8.7.0a0 - tzdata - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - zstd >=1.5.7,<1.6.0a0 license: Python-2.0 purls: [] size: 18250882 timestamp: 1779238566897 python_site_packages_path: Lib/site-packages - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py311h3f79411_1.conda sha256: 301c3ba100d25cd5ae37895988ee3ab986210d4d972aa58efed948fbe857773d md5: a0153c033dc55203e11d1cac8f6a9cf2 depends: - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT purls: - pkg:pypi/pyyaml?source=hash-mapping size: 187108 timestamp: 1770223467913 - conda: https://conda.anaconda.org/conda-forge/win-64/ruff-0.14.2-h3e3edff_0.conda noarch: python sha256: 325d09623420156ec7384ba56c20cd3482609d95d2c33ac913b96fc898b3a14c md5: c946d9d4e16b2aa505695f610c1ecb78 depends: - python - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 license: MIT license_family: MIT purls: - pkg:pypi/ruff?source=hash-mapping size: 11568203 timestamp: 1761253422638 - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.4.2-py310hf2a6c47_1.conda sha256: 24e9f3db0a2f477cbe20d1c98b48edd0d768af21dd7e6c3553e286f01deabfe5 md5: 9142e7e901c0f6e76541b523d480043e depends: - joblib >=1.2.0 - numpy >=1.19,<3 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - scipy - threadpoolctl >=2.0.0 - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 7798267 timestamp: 1715870160624 - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.4.2-py311hdcb8d17_1.conda sha256: e38cac2faa50b04ae06da6a7c9690ad8f893f2b3318b052ac15710221f32e231 md5: 4179839852432a4e95b5ff86dd5faa9c depends: - joblib >=1.2.0 - numpy >=1.19,<3 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - scipy - threadpoolctl >=2.0.0 - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9070251 timestamp: 1715870319512 - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.5.2-py311hdcb8d17_1.conda sha256: 3f23a54f327af0227115b1ac3a8d6b32926e87bfe0097e3c906bd205bb9340b7 md5: c3e550b20baa56f911022f6304c8f547 depends: - joblib >=1.2.0 - numpy >=1.19,<3 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - scipy - threadpoolctl >=3.1.0 - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9491082 timestamp: 1726083711620 - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.6.1-py312h816cc57_0.conda sha256: a35e90775f8eb213fe300747a5d9f242830fdde768871e6d194e27bbc0af0fff md5: 7d3fcb33b1b3ce559d8e83699504d9ee depends: - joblib >=1.2.0 - numpy >=1.19,<3 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - scipy - threadpoolctl >=3.1.0 - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9503776 timestamp: 1736497647297 - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py311hd01f973_0.conda sha256: 3858645f73a65e1fff1cb76dde2ac4a04876015ae4176a345b373d255ffa0d01 md5: e4ccdf47b6d2070ae414d42e4c9903d7 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.4.0 - threadpoolctl >=3.5.0 - narwhals >=2.0.1 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - numpy >=1.23,<3 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=compressed-mapping size: 9402823 timestamp: 1780401098634 - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314h1b5b07a_0.conda sha256: 3e30cc784bd5af6aa035807e5c2f12a1ecbc298d755561f6ce968b3b598b940a md5: 74bafde39f688cb95c111e74bfad6669 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.4.0 - threadpoolctl >=3.5.0 - narwhals >=2.0.1 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - numpy >=1.23,<3 - python_abi 3.14.* *_cp314 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=compressed-mapping size: 9411356 timestamp: 1780401101875 - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.9.0-np2py314hb10d3ac_0.conda sha256: 2b951586915c305b579e507329b362fab4aac13d5d119f5902b10f6ee7b84a7d md5: 5bb2d677e6f026027940eade361a1b69 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.4.0 - threadpoolctl >=3.5.0 - narwhals >=2.0.1 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - python_abi 3.14.* *_cp314t - numpy >=1.23,<3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9914296 timestamp: 1780401102174 - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.11.4-py310hf667824_0.conda sha256: 34db915d0815a9368ea950e9d31a30b01e1ba395953295cdd1224a59a7cb5a3e md5: b8b12cba10231b68dd802a9b6e262c54 depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 - numpy >=1.22.4,<1.28 - numpy >=1.22.4,<2.0a0 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 constrains: - libopenblas <0.3.26 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 14186301 timestamp: 1700814640256 - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.15.2-py311h99d06ae_0.conda sha256: 62ae1a1e02c919513213351474d1c72480fb70388a345fa81f1c95fa822d98bf md5: c7ec15b5ea6a27bb71af2ea5f7c97cbb depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 - numpy <2.5 - numpy >=1.19,<3 - numpy >=1.23.5 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 15487645 timestamp: 1739793313482 - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.15.2-py312h451d5c4_0.conda sha256: a154a6b6f4efefc65366437f611fa89c8178059e2ee7350515fe4a4c3da55c1d md5: 50632c72cc92ae3ebb615cb496bbf946 depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 - numpy <2.5 - numpy >=1.19,<3 - numpy >=1.23.5 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=hash-mapping size: 15350553 timestamp: 1739793319263 - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.17.1-py311h9c22a71_1.conda sha256: 668cfbfb7960df5fff0e2db2677eb00d9e02ee1ce63cc9b1c985d782dacab2fe md5: 0635502eadb751abecd2c68af249f50f depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 - numpy <2.7 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=compressed-mapping size: 15241561 timestamp: 1779876161272 - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.17.1-py314h221f224_1.conda sha256: f807e97b237b8528118557ef05073a9f4586c845f2431b25466aa88d268e7274 md5: 4e015e3de1f22a035a29ceba386f91aa depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 - numpy <2.7 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scipy?source=compressed-mapping size: 15229740 timestamp: 1779876154782 - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.17.1-py314h9c5632b_1.conda sha256: 20577acc20105a7281f807f906242394149d73976583e42e2a8f501489315695 md5: 4e3bf6fcbb827ea0e69193c1a10bbdb1 depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 - numpy <2.7 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314t - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: BSD-3-Clause license_family: BSD purls: [] size: 15506983 timestamp: 1779875914035 - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2023.0.0-hd3d4ead_2.conda sha256: 8a4053839b8e997a5965e2dff7d6cf3c77be62d82c0e48c8a04a5ed2d2e73035 md5: 8ee01a693aecff5432069eaaf1183c45 depends: - libhwloc >=2.13.0,<2.13.1.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE purls: [] size: 156515 timestamp: 1778673901757 - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda sha256: 0e79810fae28f3b69fe7391b0d43f5474d6bd91d451d5f2bde02f55ae481d5e3 md5: 0481bfd9814bf525bd4b3ee4b51494c4 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: TCL license_family: BSD purls: [] size: 3526350 timestamp: 1769460339384 - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda sha256: 3005729dce6f3d3f5ec91dfc49fc75a0095f9cd23bab49efb899657297ac91a5 md5: 71b24316859acd00bdb8b38f5e2ce328 constrains: - vc14_runtime >=14.29.30037 - vs2015_runtime >=14.29.30037 license: LicenseRef-MicrosoftWindowsSDK10 purls: [] size: 694692 timestamp: 1756385147981 - conda: https://conda.anaconda.org/conda-forge/win-64/ukkonen-1.1.0-py311h3fd045d_0.conda sha256: f30fdd8cdc5aa9800d6f05ed772d8e5b6427202c4bf2c86513ac62070215f19d md5: fb1cf06b4259739eaa61d94bc219c0e8 depends: - cffi - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT license_family: MIT purls: - pkg:pypi/ukkonen?source=hash-mapping size: 18452 timestamp: 1769438859987 - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.5-h1b7c187_38.conda sha256: 61b68e5a4fc71a17f8d64b12e013a2f971ad980bd08e9c389d5e68efe1a67de0 md5: 774568633f3b26d7a4a6dd4f9ea6d3e1 depends: - vc14_runtime >=14.51.36231 track_features: - vc14 license: BSD-3-Clause license_family: BSD purls: [] size: 20187 timestamp: 1780005880049 - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.51.36231-h1b9f54f_38.conda sha256: 957c7c65583c7107a5e76f39756c6361fcb7b0dc101ac7c0aea86e7ca09fe49c md5: 2cdcd8ea1010920911bb2eacb4c61227 depends: - ucrt >=10.0.20348.0 - vcomp14 14.51.36231 h1b9f54f_38 constrains: - vs2015_runtime 14.51.36231.* *_38 license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime license_family: Proprietary purls: [] size: 740997 timestamp: 1780005875753 - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.51.36231-h1b9f54f_38.conda sha256: c645fdc1f0f47718431d973386e946754a10200e7ba2c32032560913a970cacd md5: 63ee70d69d7540e821940dac5d4d9ba2 depends: - ucrt >=10.0.20348.0 constrains: - vs2015_runtime 14.51.36231.* *_38 license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime license_family: Proprietary purls: [] size: 123561 timestamp: 1780005858779 - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda sha256: 80ee68c1e7683a35295232ea79bcc87279d31ffeda04a1665efdb43cbd50a309 md5: 433699cba6602098ae8957a323da2664 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 license: MIT license_family: MIT purls: [] size: 63944 timestamp: 1753484092156 - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda sha256: 368d8628424966fd8f9c8018326a9c779e06913dd39e646cf331226acc90e5b2 md5: 053b84beec00b71ea8ff7a4f84b55207 depends: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 388453 timestamp: 1764777142545 - pypi: . name: imbalanced-learn requires_dist: - numpy>=1.25.2,<3 - scipy>=1.11.4,<2 - scikit-learn>=1.4.2,<2 - sklearn-compat>=0.1.6,<0.2 - joblib>=1.2.0,<2 - threadpoolctl>=2.0.0,<4 - ipykernel ; extra == 'dev' - ipython ; extra == 'dev' - jupyterlab ; extra == 'dev' - pandas>=2.0.3,<3 ; extra == 'docs' - tensorflow>=2.16.1,<3 ; extra == 'docs' - matplotlib>=3.7.3,<4 ; extra == 'docs' - seaborn>=0.12.2,<1 ; extra == 'docs' - memory-profiler>=0.61.0,<1 ; extra == 'docs' - numpydoc>=1.5.0,<2 ; extra == 'docs' - sphinx>=8.0.2,<9 ; extra == 'docs' - sphinx-gallery>=0.13.0,<1 ; extra == 'docs' - sphinxcontrib-bibtex>=2.6.3,<3 ; extra == 'docs' - sphinx-copybutton>=0.5.2,<1 ; extra == 'docs' - pydata-sphinx-theme>=0.15.4,<1 ; extra == 'docs' - sphinx-design>=0.6.1,<1 ; extra == 'docs' - black==23.3.0 ; extra == 'linters' - ruff==0.14.2 ; extra == 'linters' - pre-commit ; extra == 'linters' - pandas>=2.0.3,<3 ; extra == 'optional' - tensorflow>=2.16.1,<3 ; extra == 'tensorflow' - keras>=3.3.3,<4 ; extra == 'keras' - packaging>=23.2,<25 ; extra == 'tests' - pytest>=7.2.2,<9 ; extra == 'tests' - pytest-cov>=4.1.0,<6 ; extra == 'tests' - pytest-xdist>=3.5.0,<4 ; extra == 'tests' requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/0d/fe/6bea5c9162869c5beba5d9c8abbed835ec85bf1ec1fba05a3822325c45f3/build-1.5.0-py3-none-any.whl name: build version: 1.5.0 sha256: 13f3eecb844759ab66efec90ca17639bbf14dc06cb2fdf37a9010322d9c50a6f requires_dist: - packaging>=24.0 - pyproject-hooks - colorama ; os_name == 'nt' - importlib-metadata>=4.6 ; python_full_version < '3.10.2' - tomli>=1.1.0 ; python_full_version < '3.11' - keyring ; extra == 'keyring' - uv>=0.1.18 ; extra == 'uv' - virtualenv>=20.17 ; python_full_version >= '3.10' and python_full_version < '3.14' and extra == 'virtualenv' - virtualenv>=20.31 ; python_full_version >= '3.14' and extra == 'virtualenv' requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl name: scipy version: 1.17.1 sha256: eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484 requires_dist: - numpy>=1.26.4,<2.7 - pytest>=8.0.0 ; extra == 'test' - pytest-cov ; extra == 'test' - pytest-timeout ; extra == 'test' - pytest-xdist ; extra == 'test' - asv ; extra == 'test' - mpmath ; extra == 'test' - gmpy2 ; extra == 'test' - threadpoolctl ; extra == 'test' - scikit-umfpack ; extra == 'test' - pooch ; extra == 'test' - hypothesis>=6.30 ; extra == 'test' - array-api-strict>=2.3.1 ; extra == 'test' - cython ; extra == 'test' - meson ; extra == 'test' - ninja ; sys_platform != 'emscripten' and extra == 'test' - sphinx>=5.0.0,<8.2.0 ; extra == 'doc' - intersphinx-registry ; extra == 'doc' - pydata-sphinx-theme>=0.15.2 ; extra == 'doc' - sphinx-copybutton ; extra == 'doc' - sphinx-design>=0.4.0 ; extra == 'doc' - matplotlib>=3.5 ; extra == 'doc' - numpydoc ; extra == 'doc' - jupytext ; extra == 'doc' - myst-nb>=1.2.0 ; extra == 'doc' - pooch ; extra == 'doc' - jupyterlite-sphinx>=0.19.1 ; extra == 'doc' - jupyterlite-pyodide-kernel ; extra == 'doc' - linkify-it-py ; extra == 'doc' - tabulate ; extra == 'doc' - click<8.3.0 ; extra == 'dev' - spin ; extra == 'dev' - mypy==1.10.0 ; extra == 'dev' - typing-extensions ; extra == 'dev' - types-psutil ; extra == 'dev' - pycodestyle ; extra == 'dev' - ruff>=0.12.0 ; extra == 'dev' - cython-lint>=0.12.2 ; extra == 'dev' requires_python: '>=3.11' - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl name: pyproject-hooks version: 1.2.0 sha256: 9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913 requires_python: '>=3.7' scikit-learn-contrib-imbalanced-learn-8504e95/pyproject.toml000066400000000000000000000173771521136113700241310ustar00rootroot00000000000000[build-system] requires = ["setuptools>=71", "setuptools_scm[toml]>=8"] build-backend = "setuptools.build_meta" [project] name = "imbalanced-learn" dynamic = ["version", "readme"] description = "Toolbox for imbalanced dataset in machine learning" authors = [ { name="G. Lemaitre", email="g.lemaitre58@gmail.com"}, { name="C. Aridas", email="ichkoar@gmail.com"}, ] classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Intended Audience :: Science/Research", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Topic :: Scientific/Engineering", "Topic :: Software Development :: Libraries", ] requires-python = ">=3.10" dependencies = [ "numpy>=1.25.2,<3", "scipy>=1.11.4,<2", "scikit-learn>=1.4.2,<2", "sklearn-compat>=0.1.6,<0.2", "joblib>=1.2.0,<2", "threadpoolctl>=2.0.0,<4", ] [tool.setuptools.dynamic] version = { file = "imblearn/VERSION.txt" } readme = { file = "README.rst" } [project.optional-dependencies] dev = [ "ipykernel", "ipython", "jupyterlab", ] docs = [ "pandas>=2.0.3,<3", "tensorflow>=2.16.1,<3", "matplotlib>=3.7.3,<4", "seaborn>=0.12.2,<1", "memory_profiler>=0.61.0,<1", "numpydoc>=1.5.0,<2", "sphinx>=8.0.2,<9", "sphinx-gallery>=0.13.0,<1", "sphinxcontrib-bibtex>=2.6.3,<3", "sphinx-copybutton>=0.5.2,<1", "pydata-sphinx-theme>=0.15.4,<1", "sphinx-design>=0.6.1,<1", ] linters = [ "black==23.3.0", "ruff==0.14.2", "pre-commit", ] optional = [ "pandas>=2.0.3,<3", ] tensorflow = [ "tensorflow>=2.16.1,<3", ] keras = [ "keras>=3.3.3,<4", ] tests = [ "packaging>=23.2,<25", "pytest>=7.2.2,<9", "pytest-cov>=4.1.0,<6", "pytest-xdist>=3.5.0,<4", ] [project.urls] Homepage = "https://imbalanced-learn.org/" Source = "https://github.com/scikit-learn-contrib/imbalanced-learn" Issues = "https://github.com/scikit-learn-contrib/imbalanced-learn/issues" [tool.setuptools] packages = ["imblearn"] [tool.pixi.workspace] channels = ["conda-forge"] platforms = ["linux-64", "osx-arm64", "osx-64", "win-64"] [tool.pixi.dependencies] numpy = ">=1.25.2,<3" scipy = ">=1.11.4,<2" scikit-learn = ">=1.4.2,<2" sklearn-compat = ">=0.1.6,<0.2" joblib = ">=1.2.0,<2" threadpoolctl = ">=2.0.0,<4" [tool.pixi.feature.dev.dependencies] ipykernel = "*" ipython = "*" jupyterlab = "*" pip = "*" twine = "*" [tool.pixi.feature.dev.pypi-dependencies] "build" = "*" [tool.pixi.feature.docs.dependencies] matplotlib = ">=3.7.3,<4" seaborn = ">=0.12.2,<1" memory_profiler = ">=0.61.0,<1" numpydoc = ">=1.5.0,<2" sphinx = ">=8.0.2,<9" sphinx-gallery = ">=0.13.0,<1" sphinxcontrib-bibtex = ">=2.4.1,<3" sphinx-copybutton = ">=0.5.2,<1" pydata-sphinx-theme = ">=0.15.4,<1" sphinx-design = ">=0.6.1,<1" [tool.pixi.feature.linters.dependencies] black = "==23.3.0" ruff = "==0.14.2" pre-commit = "*" [tool.pixi.feature.optional.dependencies] pandas = ">=2.0.3,<3" [tool.pixi.feature.keras] platforms = ["linux-64", "osx-arm64", "osx-64"] [tool.pixi.feature.keras.dependencies] keras = ">=3.3.3,<4" [tool.pixi.feature.tensorflow] platforms = ["linux-64", "osx-arm64", "osx-64"] [tool.pixi.feature.tensorflow.dependencies] tensorflow = ">=2.16.1,<3" keras = ">=3.3.3,<3.9" [tool.pixi.feature.min-dependencies.dependencies] numpy = "==1.25.2" scipy = "==1.11.4" scikit-learn = "==1.4.2" joblib = "==1.2.0" threadpoolctl = "==2.0.0" [tool.pixi.feature.min-optional-dependencies.dependencies] pandas = "==2.0.3" [tool.pixi.feature.min-keras] platforms = ["linux-64", "osx-arm64", "osx-64"] [tool.pixi.feature.min-keras.dependencies] keras = "==3.3.3" [tool.pixi.feature.min-tensorflow] platforms = ["linux-64", "osx-arm64", "osx-64"] [tool.pixi.feature.min-tensorflow.dependencies] tensorflow = "==2.16.1" keras = "==3.3.3" [tool.pixi.feature.sklearn-1-4.dependencies] scikit-learn = "~=1.4.0" [tool.pixi.feature.sklearn-1-5.dependencies] scikit-learn = "~=1.5.0" [tool.pixi.feature.sklearn-1-6.dependencies] scikit-learn = "~=1.6.0" [tool.pixi.feature.scipy-1-15.dependencies] # for scikit-learn < 1.7, scipy > 1.15 is raising a deprecation warning scipy = "~=1.15.0" [tool.pixi.feature.py310.dependencies] python = "~=3.10.0" [tool.pixi.feature.py311.dependencies] python = "~=3.11.0" [tool.pixi.feature.py312.dependencies] python = "~=3.12.0" [tool.pixi.feature.py313.dependencies] python = "~=3.13.0" [tool.pixi.feature.py314.dependencies] python = "~=3.14.0" [tool.pixi.feature.tests.dependencies] packaging = ">=23.2,<25" pytest = ">=7.2.2,<9" pytest-cov = ">=4.1.0,<6" pytest-xdist = ">=3.5.0,<4" [tool.pixi.pypi-dependencies] imbalanced-learn = { path = ".", editable = true } [tool.pixi.feature.docs.tasks] build-docs = { cmd = "make html", cwd = "doc" } clean-docs = { cmd = "rm -rf _build/ && rm -rf auto_examples/ && rm -rf reference/generated/", cwd = "doc" } [tool.pixi.feature.linters.tasks] linters = { cmd = "pre-commit install && pre-commit run -v --all-files --show-diff-on-failure" } [tool.pixi.feature.tests.tasks] tests = { cmd = "pytest -vsl --cov=imblearn --cov-report=xml imblearn" } [tool.pixi.environments] linters = ["linters"] docs = ["optional", "docs", "tensorflow"] optional = ["optional"] tests = ["tests", "tensorflow"] dev = ["dev", "optional", "docs", "linters", "tests", "tensorflow"] ci-py310-min-dependencies = ["py310", "min-dependencies", "tests"] ci-py310-min-optional-dependencies = ["py310", "min-dependencies", "min-optional-dependencies", "tests"] ci-py310-min-keras = ["py310", "min-keras", "tests"] ci-py310-min-tensorflow = ["py310", "min-tensorflow", "tests"] ci-py311-sklearn-1-4 = ["py311", "sklearn-1-4", "scipy-1-15", "tests"] ci-py311-sklearn-1-5 = ["py311", "sklearn-1-5", "scipy-1-15", "tests"] ci-py312-sklearn-1-6 = ["py312", "sklearn-1-6", "scipy-1-15", "tests"] ci-py311-latest-tensorflow = ["py311", "tensorflow", "tests"] ci-py311-latest-keras = ["py311", "keras", "tests"] ci-py314-latest-dependencies = ["py314", "tests"] ci-py314-latest-optional-dependencies = ["py314", "optional", "tests"] [tool.black] line-length = 88 target_version = ['py310', 'py311'] preview = true # Exclude irrelevant directories for formatting exclude = ''' /( \.eggs | \.git | \.mypy_cache | \.vscode | \.pytest_cache | \.idea | build | dist )/ ''' [tool.ruff] # max line length for black line-length = 88 target-version = "py310" exclude=[ ".git", "__pycache__", "dist", "doc/_build", "doc/auto_examples", "build", "pixi.lock", ] [tool.ruff.lint] # all rules can be found here: https://beta.ruff.rs/docs/rules/ select = ["E", "F", "W", "C4", "I", "UP"] ignore = [ # use `is` and `is not` for type comparisons "E721", # do not assign a lambda expression, use a def "E731", # do not use variables named 'l', 'O', or 'I' "E741", # unnecessary list comprehension (rewrite as a set comprehension) "C403", # unnecessary tuple literal (rewrite as a set literal) "C405", # unnecessary `dict()` call (rewrite as a literal) "C408", # unnecessary list literal passed to `tuple()` (rewrite as a tuple literal) "C409", ] [tool.ruff.lint.per-file-ignores] # It's fine not to put the import at the top of the file in the examples # folder. "examples/*"=["E402"] "doc/conf.py"=["E402"] [tool.pytest.ini_options] filterwarnings = [ # Turn deprecation warnings into errors "error::FutureWarning", "error::DeprecationWarning", # raised by `joblib` in old versions "ignore:.*distutils Version classes are deprecated.*:DeprecationWarning", ] addopts = "--doctest-modules --color=yes -rs" doctest_optionflags = "NORMALIZE_WHITESPACE ELLIPSIS" scikit-learn-contrib-imbalanced-learn-8504e95/references.bib000066400000000000000000000155461521136113700240100ustar00rootroot00000000000000 @InProceedings{ batista2003, title = {Balancing training data for automated annotation of keywords: A case study}, author = {Batista, Gustavo E. A. P. A. and Bazzan, Ana L. C. and Monard, Maria Carolina}, booktitle = {Proceedings of the 2nd Brazilian Workshop on Bioinformatics}, pages = {10--18}, year = {2003}, month = {Dec.}, address = {Rio de Janeiro, Brazil} } @Article{ batista2004, title = {A study of the behavior of several methods for balancing machine learning training data}, author = {Batista, Gustavo E. A. P. A. and Prati, Ronaldo C. and Monard, Maria Carolina}, journal = {ACM Sigkdd Explorations Newsletter}, volume = {6}, number = {1}, pages = {20--29}, year = {2004}, publisher = {ACM} } @Article{ chawla2002, title = {SMOTE: Synthetic minority over-sampling technique}, author = {Chawla, Nitesh V. and Bowyer, Kevin W. and Hall, Lawrence O. and Kegelmeyer, W. Philip}, journal = {Journal of Artificial Intelligence Research}, volume = {16}, pages = {321--357}, year = {2002} } @InProceedings{ han2005, title = {Borderline-SMOTE: A new over-sampling method in imbalanced data sets learning}, author = {Han, Hui and Wang, Wen-Yuan and Mao, Bing-Huan}, journal = {Advances in intelligent computing}, pages = {878--887}, year = {2005}, booktitle = {Proceedings of the 1st International Conference on Intelligent Computing}, month = {Aug.}, address = {Hefei, China} } @Article{ hart1968, title = {The condensed nearest neighbor rule}, author = {Hart, Peter E.}, journal = {IEEE Transactions on Information Theory}, volume = {14}, number = {3}, pages = {515--516}, year = {1968}, publisher = {IEEE} } @InProceedings{ he2008, title = {ADASYN: Adaptive synthetic sampling approach for imbalanced learning}, author = {He, Haibo and Bai, Yang and Garcia, Edwardo A. and Li, Shutao}, booktitle = {Proceedings of the 5th IEEE International Joint Conference on Neural Networks}, pages = {1322--1328}, year = {2008}, organization = {IEEE}, month = {Jun.}, address = {Hong Kong, China} } @InProceedings{ kubat1997, title = {Addressing the curse of imbalanced training sets: One-sided selection}, author = {Kubat, Miroslav and Matwin, Stan}, booktitle = {Proceedings of the 14th International Conference on Machine Learning}, volume = {97}, pages = {179--186}, year = {1997}, address = {Nashville, Tennessee, USA}, month = {July} } @InProceedings{ laurikkala2001, title = {Improving identification of difficult small classes by balancing class distribution}, author = {Laurikkala, Jorma}, journal = {Proceedings of the 8th Conference on Artificial Intelligence in Medicine in Europe}, pages = {63--66}, address = {Cascais, Portugal}, month = {Jul.}, year = {2001}, publisher = {Springer} } @Article{ liu2009, title = {Exploratory undersampling for class-imbalance learning}, author = {Liu, Xu-Ying and Wu, Jianxin and Zhou, Zhi-Hua}, journal = {IEEE Transactions on Systems, Man, and Cybernetics}, volume = {39}, number = {2}, pages = {539--550}, year = {2009}, publisher = {IEEE} } @InProceedings{ mani2003, title = {kNN approach to unbalanced data distributions: A case study involving information extraction}, author = {Mani, Inderjeet and Zhang, Jianping}, booktitle = {Proceedings of the Workshop on Learning from Imbalanced Data Sets}, volume = {126}, year = {2003}, month = {Aug.}, pages = {1--7}, address = {Washington, DC, USA} } @InProceedings{ nguyen2009, title = {Borderline over-sampling for imbalanced data classification}, author = {Nguyen, Hien M. and Cooper, Eric W. and Kamei, Katsuari}, journal = {Proceedings of the 5th International Workshop on computational Intelligence and Applications}, pages = {24--29}, year = {2009} } @Article{ smith2014, title = {An instance level analysis of data complexity}, author = {Smith, Michael R. and Martinez, Tony and Giraud-Carrier, Christophe}, journal = {Machine learning}, volume = {95}, number = {2}, pages = {225--256}, year = {2014}, publisher = {Springer} } @Article{ tomek1976a, title = {Two modifications of CNN}, author = {Tomek, Ivan}, journal = {IEEE Trans. Systems, Man and Cybernetics}, volume = {6}, issue = {6}, pages = {769--772}, year = {1976} } @Article{ tomek1976b, title = {An experiment with the edited nearest-neighbor rule}, author = {Tomek, Ivan}, journal = {IEEE Transactions on Systems, Man, and Cybernetics}, number = {6}, issue = {6}, pages = {448--452}, year = {1976} } @Article{ wilson1972, title = {Asymptotic properties of nearest neighbor rules using edited data}, author = {Wilson, Dennis L.}, journal = {IEEE Transactions on Systems, Man, and Cybernetics}, volume = {2}, number = {3}, pages = {408--421}, year = {1972}, publisher = {IEEE} } @article{chen2004using, title={Using random forest to learn imbalanced data}, author={Chen, Chao and Liaw, Andy and Breiman, Leo}, journal={University of California, Berkeley}, volume={110}, pages={1--12}, year={2004} } @article{torelli2014rose, author = {Menardi, Giovanna and Torelli, Nicola}, title={Training and assessing classification rules with imbalanced data}, journal={Data Mining and Knowledge Discovery}, volume={28}, pages={92-122}, year={2014}, publisher={Springer}, issue = {1}, issn = {1573-756X}, url = {https://doi.org/10.1007/s10618-012-0295-5}, doi = {10.1007/s10618-012-0295-5} } @article{stanfill1986toward, title={Toward memory-based reasoning}, author={Stanfill, Craig and Waltz, David}, journal={Communications of the ACM}, volume={29}, number={12}, pages={1213--1228}, year={1986}, publisher={ACM New York, NY, USA} } @article{wilson1997improved, title={Improved heterogeneous distance functions}, author={Wilson, D Randall and Martinez, Tony R}, journal={Journal of artificial intelligence research}, volume={6}, pages={1--34}, year={1997} } @inproceedings{wang2009diversity, title={Diversity analysis on imbalanced data sets by using ensemble models}, author={Wang, Shuo and Yao, Xin}, booktitle={2009 IEEE symposium on computational intelligence and data mining}, pages={324--331}, year={2009}, organization={IEEE} } @article{hido2009roughly, title={Roughly balanced bagging for imbalanced data}, author={Hido, Shohei and Kashima, Hisashi and Takahashi, Yutaka}, journal={Statistical Analysis and Data Mining: The ASA Data Science Journal}, volume={2}, number={5-6}, pages={412--426}, year={2009}, publisher={Wiley Online Library} } @article{maclin1997empirical, title={An empirical evaluation of bagging and boosting}, author={Maclin, Richard and Opitz, David}, journal={AAAI/IAAI}, volume={1997}, pages={546--551}, year={1997} }