pax_global_header00006660000000000000000000000064151220663030014510gustar00rootroot0000000000000052 comment=fc39a8313e2ec91e144f535f30732025d37560b8 scikit-learn-contrib-imbalanced-learn-fc39a83/000077500000000000000000000000001512206630300213315ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/.circleci/000077500000000000000000000000001512206630300231645ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/.circleci/config.yml000066400000000000000000000024141512206630300251550ustar00rootroot00000000000000version: 2.1 jobs: python3: docker: - image: cimg/python:3.9 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.9 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-fc39a83/.coveragerc000066400000000000000000000002561512206630300234550ustar00rootroot00000000000000[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-fc39a83/.github/000077500000000000000000000000001512206630300226715ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/.github/ISSUE_TEMPLATE.md000066400000000000000000000031711512206630300254000ustar00rootroot00000000000000 #### Description #### Steps/Code to Reproduce #### Expected Results #### Actual Results #### Versions scikit-learn-contrib-imbalanced-learn-fc39a83/.github/ISSUE_TEMPLATE/000077500000000000000000000000001512206630300250545ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000030641512206630300275510ustar00rootroot00000000000000--- 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-fc39a83/.github/ISSUE_TEMPLATE/documentation-improvement.md000066400000000000000000000006231512206630300326130ustar00rootroot00000000000000--- 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-fc39a83/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000010271512206630300306010ustar00rootroot00000000000000--- 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-fc39a83/.github/ISSUE_TEMPLATE/other--blank-template-.md000066400000000000000000000001771512206630300315540ustar00rootroot00000000000000--- name: Other (blank template) about: For all other issues to reach the community... title: '' labels: '' assignees: '' --- scikit-learn-contrib-imbalanced-learn-fc39a83/.github/ISSUE_TEMPLATE/question.md000066400000000000000000000003701512206630300272450ustar00rootroot00000000000000--- 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-fc39a83/.github/ISSUE_TEMPLATE/usage-question.md000066400000000000000000000007371512206630300303560ustar00rootroot00000000000000--- 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-fc39a83/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000014631512206630300264760ustar00rootroot00000000000000 #### Reference Issue #### What does this implement/fix? Explain your changes. #### Any other comments? scikit-learn-contrib-imbalanced-learn-fc39a83/.github/check-changelog.yml000066400000000000000000000057461512206630300264320ustar00rootroot00000000000000name: 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-fc39a83/.github/dependabot.yml000066400000000000000000000010761512206630300255250ustar00rootroot00000000000000version: 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-fc39a83/.github/workflows/000077500000000000000000000000001512206630300247265ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/.github/workflows/circleci-artifacts-redirector.yml000066400000000000000000000016521512206630300333500ustar00rootroot00000000000000name: 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@v1 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-fc39a83/.github/workflows/linters.yml000066400000000000000000000006541512206630300271360ustar00rootroot00000000000000name: 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@v5 - uses: prefix-dev/setup-pixi@v0.9.0 with: pixi-version: v0.51.0 frozen: true - name: Run tests run: pixi run -e linters linters scikit-learn-contrib-imbalanced-learn-fc39a83/.github/workflows/tests.yml000066400000000000000000000032001512206630300266060ustar00rootroot00000000000000name: '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@v5 - uses: prefix-dev/setup-pixi@v0.9.0 with: pixi-version: v0.51.0 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@v5.5.0 with: token: ${{ secrets.CODECOV_TOKEN }} slug: scikit-learn-contrib/imbalanced-learn scikit-learn-contrib-imbalanced-learn-fc39a83/.gitignore000066400000000000000000000026351512206630300233270ustar00rootroot00000000000000# 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-fc39a83/.pre-commit-config.yaml000066400000000000000000000006531512206630300256160ustar00rootroot00000000000000repos: - 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-fc39a83/AUTHORS.rst000066400000000000000000000010631512206630300232100ustar00rootroot00000000000000History ------- 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-fc39a83/CONTRIBUTING.md000066400000000000000000000155201512206630300235650ustar00rootroot00000000000000Contributing 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-fc39a83/LICENSE000066400000000000000000000021451512206630300223400ustar00rootroot00000000000000The 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-fc39a83/MANIFEST.in000066400000000000000000000002051512206630300230640ustar00rootroot00000000000000 recursive-include doc * recursive-include examples * include AUTHORS.rst include CONTRIBUTING.md include LICENSE include README.rst scikit-learn-contrib-imbalanced-learn-fc39a83/README.rst000066400000000000000000000136641512206630300230320ustar00rootroot00000000000000.. -*- 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-fc39a83/build_tools/000077500000000000000000000000001512206630300236505ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/build_tools/circle/000077500000000000000000000000001512206630300251115ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/build_tools/circle/build_doc.sh000077500000000000000000000006251512206630300273770ustar00rootroot00000000000000#!/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 curl -fsSL https://pixi.sh/install.sh | 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-fc39a83/build_tools/circle/checkout_merge_commit.sh000077500000000000000000000016411512206630300320060ustar00rootroot00000000000000#!/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-fc39a83/build_tools/circle/linting.sh000077500000000000000000000142621512206630300271210ustar00rootroot00000000000000#!/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-fc39a83/build_tools/circle/push_doc.sh000077500000000000000000000024621512206630300272600ustar00rootroot00000000000000#!/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-fc39a83/conftest.py000066400000000000000000000020361512206630300235310ustar00rootroot00000000000000# 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-fc39a83/doc/000077500000000000000000000000001512206630300220765ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/doc/Makefile000066400000000000000000000156421512206630300235460ustar00rootroot00000000000000# 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-fc39a83/doc/_static/000077500000000000000000000000001512206630300235245ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/doc/_static/css/000077500000000000000000000000001512206630300243145ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/doc/_static/css/imbalanced-learn.css000066400000000000000000000022131512206630300302020ustar00rootroot00000000000000@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-fc39a83/doc/_static/img/000077500000000000000000000000001512206630300243005ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/doc/_static/img/favicon.ico000066400000000000000000000021761512206630300264270ustar00rootroot00000000000000 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-fc39a83/doc/_static/img/logo.png000066400000000000000000001046061512206630300257550ustar00rootroot00000000000000PNG  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-fc39a83/doc/_static/img/logo.xcf000066400000000000000000001542471512206630300257570ustar00rootroot00000000000000gimp 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-fc39a83/doc/_static/img/logo_wide.png000066400000000000000000000566431512206630300267740ustar00rootroot00000000000000PNG  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-fc39a83/doc/_static/img/logo_wide_dark.png000066400000000000000000000625331512206630300277700ustar00rootroot00000000000000PNG  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-fc39a83/doc/_static/index_examples.svg000066400000000000000000000047401512206630300272570ustar00rootroot00000000000000 image/svg+xml scikit-learn-contrib-imbalanced-learn-fc39a83/doc/_static/index_getting_started.svg000066400000000000000000000076111512206630300306300ustar00rootroot00000000000000 image/svg+xml scikit-learn-contrib-imbalanced-learn-fc39a83/doc/_static/index_user_guide.svg000066400000000000000000000144351512206630300275760ustar00rootroot00000000000000 image/svg+xml scikit-learn-contrib-imbalanced-learn-fc39a83/doc/_static/js/000077500000000000000000000000001512206630300241405ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/doc/_static/js/copybutton.js000066400000000000000000000053631512206630300267130ustar00rootroot00000000000000$(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-fc39a83/doc/_templates/000077500000000000000000000000001512206630300242335ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/doc/_templates/class.rst000066400000000000000000000007111512206630300260710ustar00rootroot00000000000000{{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-fc39a83/doc/_templates/function.rst000066400000000000000000000003231512206630300266100ustar00rootroot00000000000000{{objname}} {{ underline }}==================== .. currentmodule:: {{ module }} .. autofunction:: {{ objname }} .. include:: {{module}}.{{objname}}.examples .. raw:: html
scikit-learn-contrib-imbalanced-learn-fc39a83/doc/_templates/numpydoc_docstring.rst000066400000000000000000000003261512206630300307000ustar00rootroot00000000000000{{index}} {{summary}} {{extended_summary}} {{parameters}} {{returns}} {{yields}} {{other_parameters}} {{attributes}} {{raises}} {{warns}} {{warnings}} {{see_also}} {{notes}} {{references}} {{examples}} {{methods}} scikit-learn-contrib-imbalanced-learn-fc39a83/doc/_templates/sidebar-search-bs.html000066400000000000000000000005631512206630300304030ustar00rootroot00000000000000 scikit-learn-contrib-imbalanced-learn-fc39a83/doc/about.rst000066400000000000000000000012131512206630300237370ustar00rootroot00000000000000About 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-fc39a83/doc/bibtex/000077500000000000000000000000001512206630300233535ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/doc/bibtex/refs.bib000066400000000000000000000201731512206630300247730ustar00rootroot00000000000000@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-fc39a83/doc/combine.rst000066400000000000000000000044411512206630300242470ustar00rootroot00000000000000.. _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-fc39a83/doc/common_pitfalls.rst000066400000000000000000000170421512206630300260220ustar00rootroot00000000000000.. _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-fc39a83/doc/conf.py000066400000000000000000000240461512206630300234030ustar00rootroot00000000000000# # 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-fc39a83/doc/datasets/000077500000000000000000000000001512206630300237065ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/doc/datasets/index.rst000066400000000000000000000176061512206630300255610ustar00rootroot00000000000000.. _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-fc39a83/doc/developers_utils.rst000066400000000000000000000162711512206630300262270ustar00rootroot00000000000000.. _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-fc39a83/doc/ensemble.rst000066400000000000000000000113361512206630300244260ustar00rootroot00000000000000.. _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-fc39a83/doc/index.rst000066400000000000000000000060011512206630300237340ustar00rootroot00000000000000.. 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-fc39a83/doc/install.rst000066400000000000000000000064261512206630300243060ustar00rootroot00000000000000.. _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-fc39a83/doc/introduction.rst000066400000000000000000000061741512206630300253610ustar00rootroot00000000000000.. _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-fc39a83/doc/make.bat000066400000000000000000000151011512206630300235010ustar00rootroot00000000000000@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-fc39a83/doc/metrics.rst000066400000000000000000000132101512206630300242730ustar00rootroot00000000000000.. _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-fc39a83/doc/miscellaneous.rst000066400000000000000000000165211512206630300255000ustar00rootroot00000000000000.. _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-fc39a83/doc/model_selection.rst000066400000000000000000000141701512206630300260000ustar00rootroot00000000000000.. _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-fc39a83/doc/over_sampling.rst000066400000000000000000000343241512206630300255030ustar00rootroot00000000000000.. _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-fc39a83/doc/references/000077500000000000000000000000001512206630300242175ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/doc/references/combine.rst000066400000000000000000000005001512206630300263600ustar00rootroot00000000000000.. _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-fc39a83/doc/references/datasets.rst000066400000000000000000000004041512206630300265570ustar00rootroot00000000000000.. _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-fc39a83/doc/references/ensemble.rst000066400000000000000000000007571512206630300265540ustar00rootroot00000000000000.. _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-fc39a83/doc/references/index.rst000066400000000000000000000004731512206630300260640ustar00rootroot00000000000000.. _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-fc39a83/doc/references/keras.rst000066400000000000000000000005701512206630300260600ustar00rootroot00000000000000.. _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-fc39a83/doc/references/metrics.rst000066400000000000000000000015561512206630300264260ustar00rootroot00000000000000.. _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-fc39a83/doc/references/miscellaneous.rst000066400000000000000000000003251512206630300276140ustar00rootroot00000000000000.. _misc_ref: Miscellaneous ============= Imbalance-learn provides some fast-prototyping tools. .. currentmodule:: imblearn .. autosummary:: :toctree: generated/ :template: class.rst FunctionSampler scikit-learn-contrib-imbalanced-learn-fc39a83/doc/references/model_selection.rst000066400000000000000000000006701512206630300301210ustar00rootroot00000000000000.. _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-fc39a83/doc/references/over_sampling.rst000066400000000000000000000010001512206630300276050ustar00rootroot00000000000000.. _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-fc39a83/doc/references/pipeline.rst000066400000000000000000000005001512206630300265510ustar00rootroot00000000000000.. _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-fc39a83/doc/references/tensorflow.rst000066400000000000000000000004561512206630300271600ustar00rootroot00000000000000.. _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-fc39a83/doc/references/under_sampling.rst000066400000000000000000000016271512206630300277660ustar00rootroot00000000000000.. _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-fc39a83/doc/references/utils.rst000066400000000000000000000013151512206630300261110ustar00rootroot00000000000000Utilities ========= .. 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-fc39a83/doc/sphinxext/000077500000000000000000000000001512206630300241305ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/doc/sphinxext/LICENSE.txt000066400000000000000000000136221512206630300257570ustar00rootroot00000000000000------------------------------------------------------------------------------- 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-fc39a83/doc/sphinxext/MANIFEST.in000066400000000000000000000000531512206630300256640ustar00rootroot00000000000000recursive-include tests *.py include *.txt scikit-learn-contrib-imbalanced-learn-fc39a83/doc/sphinxext/README.txt000066400000000000000000000032401512206630300256250ustar00rootroot00000000000000===================================== 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-fc39a83/doc/sphinxext/github_link.py000066400000000000000000000051251512206630300270040ustar00rootroot00000000000000import 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-fc39a83/doc/sphinxext/sphinx_issues.py000066400000000000000000000175341512206630300274200ustar00rootroot00000000000000"""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-fc39a83/doc/under_sampling.rst000066400000000000000000000537071512206630300256530ustar00rootroot00000000000000.. _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-fc39a83/doc/user_guide.rst000066400000000000000000000010151512206630300247600ustar00rootroot00000000000000.. 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-fc39a83/doc/whats_new.rst000066400000000000000000000010401512206630300246220ustar00rootroot00000000000000.. currentmodule:: imblearn =============== Release history =============== .. 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-fc39a83/doc/whats_new/000077500000000000000000000000001512206630300240755ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/doc/whats_new/v0.1.rst000066400000000000000000000022221512206630300253110ustar00rootroot00000000000000.. _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-fc39a83/doc/whats_new/v0.10.rst000066400000000000000000000040611512206630300253740ustar00rootroot00000000000000.. _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-fc39a83/doc/whats_new/v0.11.rst000066400000000000000000000055121512206630300253770ustar00rootroot00000000000000.. _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-fc39a83/doc/whats_new/v0.12.rst000066400000000000000000000105311512206630300253750ustar00rootroot00000000000000.. _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-fc39a83/doc/whats_new/v0.13.rst000066400000000000000000000016231512206630300254000ustar00rootroot00000000000000.. _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-fc39a83/doc/whats_new/v0.14.rst000066400000000000000000000012701512206630300253770ustar00rootroot00000000000000.. _changes_0_14: Version 0.14.1 ============== **December 21, 2025** Changelog --------- Maintenance ........... - Compatibility with scikit-learn 1.8 :pr:`1158` by :user:`Guillaume Lemaitre `. 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-fc39a83/doc/whats_new/v0.2.rst000066400000000000000000000136641512206630300253260ustar00rootroot00000000000000.. _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-fc39a83/doc/whats_new/v0.3.rst000066400000000000000000000064741512206630300253300ustar00rootroot00000000000000.. _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-fc39a83/doc/whats_new/v0.4.rst000066400000000000000000000206611512206630300253230ustar00rootroot00000000000000.. _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-fc39a83/doc/whats_new/v0.6.rst000066400000000000000000000115471512206630300253300ustar00rootroot00000000000000.. _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-fc39a83/doc/whats_new/v0.7.rst000066400000000000000000000046041512206630300253250ustar00rootroot00000000000000.. _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-fc39a83/doc/whats_new/v0.8.rst000066400000000000000000000051641512206630300253300ustar00rootroot00000000000000.. _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-fc39a83/doc/whats_new/v0.9.rst000066400000000000000000000006221512206630300253230ustar00rootroot00000000000000.. _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-fc39a83/doc/zzz_references.rst000066400000000000000000000001041512206630300256610ustar00rootroot00000000000000========== References ========== .. bibliography:: bibtex/refs.bib scikit-learn-contrib-imbalanced-learn-fc39a83/examples/000077500000000000000000000000001512206630300231475ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/examples/README.txt000066400000000000000000000001701512206630300246430ustar00rootroot00000000000000.. _general_examples: Examples -------- General-purpose and introductory examples for the `imbalanced-learn` toolbox. scikit-learn-contrib-imbalanced-learn-fc39a83/examples/api/000077500000000000000000000000001512206630300237205ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/examples/api/README.txt000066400000000000000000000002601512206630300254140ustar00rootroot00000000000000.. _api_usage: Examples showing API imbalanced-learn usage ------------------------------------------- Examples that show some details regarding the API of imbalanced-learn. scikit-learn-contrib-imbalanced-learn-fc39a83/examples/api/plot_sampling_strategy_usage.py000066400000000000000000000137241512206630300322570ustar00rootroot00000000000000""" ==================================================== 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-fc39a83/examples/applications/000077500000000000000000000000001512206630300256355ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/examples/applications/README.txt000066400000000000000000000002131512206630300273270ustar00rootroot00000000000000.. _realword_examples: Examples based on real world datasets ------------------------------------- Examples which use real-word dataset. plot_impact_imbalanced_classes.py000066400000000000000000000301301512206630300343140ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000027261512206630300346030ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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-fc39a83/examples/applications/plot_outlier_rejections.py000066400000000000000000000102711512206630300331560ustar00rootroot00000000000000""" =============================================================== 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.py000066400000000000000000000112261512206630300347170ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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-fc39a83/examples/applications/plot_topic_classication.py000066400000000000000000000064361512206630300331300ustar00rootroot00000000000000""" ================================================= 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.py000066400000000000000000000210611512206630300351330ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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-fc39a83/examples/combine/000077500000000000000000000000001512206630300245635ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/examples/combine/README.txt000066400000000000000000000004261512206630300262630ustar00rootroot00000000000000.. _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-fc39a83/examples/combine/plot_comparison_combine.py000066400000000000000000000073541512206630300320520ustar00rootroot00000000000000""" ================================================== 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-fc39a83/examples/datasets/000077500000000000000000000000001512206630300247575ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/examples/datasets/README.txt000066400000000000000000000001721512206630300264550ustar00rootroot00000000000000.. _dataset_examples: Dataset examples ----------------------- Examples concerning the :mod:`imblearn.datasets` module. scikit-learn-contrib-imbalanced-learn-fc39a83/examples/datasets/plot_make_imbalance.py000066400000000000000000000046521512206630300313060ustar00rootroot00000000000000""" ============================ 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-fc39a83/examples/ensemble/000077500000000000000000000000001512206630300247415ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/examples/ensemble/README.txt000066400000000000000000000005511512206630300264400ustar00rootroot00000000000000.. _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-fc39a83/examples/ensemble/plot_bagging_classifier.py000066400000000000000000000136061512206630300321610ustar00rootroot00000000000000""" ================================= 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.py000066400000000000000000000162521512206630300345300ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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-fc39a83/examples/evaluation/000077500000000000000000000000001512206630300253165ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/examples/evaluation/README.txt000066400000000000000000000002221512206630300270100ustar00rootroot00000000000000.. _evaluation_examples: Evaluation examples ------------------- Examples illustrating how classification using imbalanced dataset can be done. scikit-learn-contrib-imbalanced-learn-fc39a83/examples/evaluation/plot_classification_report.py000066400000000000000000000030601512206630300333130ustar00rootroot00000000000000""" ============================================= 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-fc39a83/examples/evaluation/plot_metrics.py000066400000000000000000000055241512206630300304020ustar00rootroot00000000000000""" ======================================= 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-fc39a83/examples/model_selection/000077500000000000000000000000001512206630300263145ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/examples/model_selection/README.txt000066400000000000000000000001701512206630300300100ustar00rootroot00000000000000.. _model_selection_examples: Model Selection --------------- Examples related to the selection of balancing methods. scikit-learn-contrib-imbalanced-learn-fc39a83/examples/model_selection/plot_instance_hardness_cv.py000066400000000000000000000071701512206630300341140ustar00rootroot00000000000000""" ==================================================== 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-fc39a83/examples/model_selection/plot_validation_curve.py000066400000000000000000000061251512206630300332660ustar00rootroot00000000000000""" ========================== 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-fc39a83/examples/over-sampling/000077500000000000000000000000001512206630300257325ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/examples/over-sampling/README.txt000066400000000000000000000003771512206630300274370ustar00rootroot00000000000000.. _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.py000066400000000000000000000253531512206630300343720ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000037321512206630300355750ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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-fc39a83/examples/over-sampling/plot_shrinkage_effect.py000066400000000000000000000075641512206630300326450ustar00rootroot00000000000000""" ====================================================== 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-fc39a83/examples/pipeline/000077500000000000000000000000001512206630300247545ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/examples/pipeline/README.txt000066400000000000000000000002341512206630300264510ustar00rootroot00000000000000.. _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-fc39a83/examples/pipeline/plot_pipeline_classification.py000066400000000000000000000037261512206630300332540ustar00rootroot00000000000000""" ==================================== 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-fc39a83/examples/under-sampling/000077500000000000000000000000001512206630300260745ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/examples/under-sampling/README.txt000066400000000000000000000005121512206630300275700ustar00rootroot00000000000000.. _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.py000066400000000000000000000227531512206630300346770ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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-fc39a83/examples/under-sampling/plot_illustration_nearmiss.py000066400000000000000000000132071512206630300341410ustar00rootroot00000000000000""" ============================ 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.py000066400000000000000000000061541512206630300345630ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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-fc39a83/imblearn/000077500000000000000000000000001512206630300231225ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/VERSION.txt000066400000000000000000000000071512206630300250050ustar00rootroot000000000000000.14.1 scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/__init__.py000066400000000000000000000100041512206630300252260ustar00rootroot00000000000000"""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-fc39a83/imblearn/_version.py000066400000000000000000000013171512206630300253220ustar00rootroot00000000000000""" ``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-fc39a83/imblearn/base.py000066400000000000000000000324251512206630300244140ustar00rootroot00000000000000"""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-fc39a83/imblearn/combine/000077500000000000000000000000001512206630300245365ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/combine/__init__.py000066400000000000000000000003611512206630300266470ustar00rootroot00000000000000"""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-fc39a83/imblearn/combine/_smote_enn.py000066400000000000000000000117501512206630300272420ustar00rootroot00000000000000"""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-fc39a83/imblearn/combine/_smote_tomek.py000066400000000000000000000115321512206630300275770ustar00rootroot00000000000000"""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-fc39a83/imblearn/combine/tests/000077500000000000000000000000001512206630300257005ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/combine/tests/__init__.py000066400000000000000000000000001512206630300277770ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/combine/tests/test_smote_enn.py000066400000000000000000000111711512206630300313010ustar00rootroot00000000000000"""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-fc39a83/imblearn/combine/tests/test_smote_tomek.py000066400000000000000000000126011512206630300316370ustar00rootroot00000000000000"""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-fc39a83/imblearn/datasets/000077500000000000000000000000001512206630300247325ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/datasets/__init__.py000066400000000000000000000003611512206630300270430ustar00rootroot00000000000000""" 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-fc39a83/imblearn/datasets/_imbalance.py000066400000000000000000000100641512206630300273570ustar00rootroot00000000000000"""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-fc39a83/imblearn/datasets/_zenodo.py000066400000000000000000000315701512206630300267470ustar00rootroot00000000000000"""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-fc39a83/imblearn/datasets/tests/000077500000000000000000000000001512206630300260745ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/datasets/tests/__init__.py000066400000000000000000000000001512206630300301730ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/datasets/tests/test_imbalance.py000066400000000000000000000047261512206630300314310ustar00rootroot00000000000000"""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-fc39a83/imblearn/datasets/tests/test_zenodo.py000066400000000000000000000053251512206630300310100ustar00rootroot00000000000000"""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-fc39a83/imblearn/ensemble/000077500000000000000000000000001512206630300247145ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/ensemble/__init__.py000066400000000000000000000010251512206630300270230ustar00rootroot00000000000000""" 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-fc39a83/imblearn/ensemble/_bagging.py000066400000000000000000000313321512206630300270250ustar00rootroot00000000000000"""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-fc39a83/imblearn/ensemble/_common.py000066400000000000000000000062611512206630300267220ustar00rootroot00000000000000from 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-fc39a83/imblearn/ensemble/_easy_ensemble.py000066400000000000000000000232761512206630300302520ustar00rootroot00000000000000"""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-fc39a83/imblearn/ensemble/_forest.py000066400000000000000000000764511512206630300267440ustar00rootroot00000000000000"""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] 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 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] n_samples_bootstrap = _get_n_samples_bootstrap( n_sample_subset, self.max_samples ) 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-fc39a83/imblearn/ensemble/_weight_boosting.py000066400000000000000000000356201512206630300306260ustar00rootroot00000000000000import 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-fc39a83/imblearn/ensemble/tests/000077500000000000000000000000001512206630300260565ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/ensemble/tests/__init__.py000066400000000000000000000000001512206630300301550ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/ensemble/tests/test_bagging.py000066400000000000000000000445041512206630300310740ustar00rootroot00000000000000"""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-fc39a83/imblearn/ensemble/tests/test_easy_ensemble.py000066400000000000000000000155711512206630300323130ustar00rootroot00000000000000"""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-fc39a83/imblearn/ensemble/tests/test_forest.py000066400000000000000000000234711512206630300310000ustar00rootroot00000000000000import 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-fc39a83/imblearn/ensemble/tests/test_weight_boosting.py000066400000000000000000000061731512206630300326710ustar00rootroot00000000000000import 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-fc39a83/imblearn/exceptions.py000066400000000000000000000014211512206630300256530ustar00rootroot00000000000000""" 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-fc39a83/imblearn/keras/000077500000000000000000000000001512206630300242275ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/keras/__init__.py000066400000000000000000000003671512206630300263460ustar00rootroot00000000000000"""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-fc39a83/imblearn/keras/_generator.py000066400000000000000000000240541512206630300267330ustar00rootroot00000000000000"""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-fc39a83/imblearn/keras/tests/000077500000000000000000000000001512206630300253715ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/keras/tests/__init__.py000066400000000000000000000000001512206630300274700ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/keras/tests/test_generator.py000066400000000000000000000103221512206630300307660ustar00rootroot00000000000000import 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-fc39a83/imblearn/metrics/000077500000000000000000000000001512206630300245705ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/metrics/__init__.py000066400000000000000000000012221512206630300266760ustar00rootroot00000000000000""" 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-fc39a83/imblearn/metrics/_classification.py000066400000000000000000001163401512206630300303010ustar00rootroot00000000000000"""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-fc39a83/imblearn/metrics/pairwise.py000066400000000000000000000214171512206630300267720ustar00rootroot00000000000000"""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-fc39a83/imblearn/metrics/tests/000077500000000000000000000000001512206630300257325ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/metrics/tests/__init__.py000066400000000000000000000000001512206630300300310ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/metrics/tests/test_classification.py000066400000000000000000000425771512206630300323550ustar00rootroot00000000000000"""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, svm 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 SVC 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 = svm.SVC(kernel="linear", probability=True, random_state=0) 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.41), (None, [0.85, 0.29, 0.7]), ("macro", 0.68), ("weighted", 0.65), ], ) 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.83 0.79 0.92 " "0.81 0.85 0.72 24 versicolor 0.33 0.10 0.86 0.15 " "0.29 0.08 31 virginica 0.42 0.90 0.55 0.57 0.70 " "0.51 20 avg / total 0.51 0.53 0.80 0.47 0.58 0.40 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.83 0.79 0.92 0.81 " "0.85 0.72 24 1 0.33 0.10 0.86 0.15 0.29 0.08 31 " "2 0.42 0.90 0.55 0.57 0.70 0.51 20 avg / total " "0.51 0.53 0.80 0.47 0.58 0.40 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.82609 0.79167 " "0.92157 0.80851 0.85415 0.72010 24 versicolor " "0.33333 0.09677 0.86364 0.15000 0.28910 0.07717 " "31 virginica 0.41860 0.90000 0.54545 0.57143 0.70065 " "0.50831 20 avg / total 0.51375 0.53333 0.79733 " "0.47310 0.57966 0.39788 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.83 0.79 0.92 0.81 " "0.85 0.72 24 1 0.33 0.10 0.86 0.15 0.29 0.08 31 " "2 0.42 0.90 0.55 0.57 0.70 0.51 20 avg / total 0.51 " "0.53 0.80 0.47 0.58 0.40 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.83 0.79 0.92 0.81 " "0.85 0.72 24 green 0.33 0.10 0.86 0.15 0.29 0.08 31 " "red 0.42 0.90 0.55 0.57 0.70 0.51 20 avg / total " "0.51 0.53 0.80 0.47 0.58 0.40 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.83 0.79 0.92 0.81 0.85 " "0.72 24 b 0.33 0.10 0.86 0.15 0.29 0.08 31 c 0.42 " "0.90 0.55 0.57 0.70 0.51 20 avg / total 0.51 0.53 " "0.80 0.47 0.58 0.40 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.83 0.79 0.92 0.81 " "0.85 0.72 24 green¢ 0.33 0.10 0.86 0.15 0.29 0.08 31 " "red¢ 0.42 0.90 0.55 0.57 0.70 0.51 20 avg / total " "0.51 0.53 0.80 0.47 0.58 0.40 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.83 0.79 0.92 0.81 " "0.85 0.72 24 greengreengreengreengreen 0.33 0.10 " "0.86 0.15 0.29 0.08 31 red 0.42 0.90 0.55 0.57 0.70 " "0.51 20 avg / total 0.51 0.53 0.80 0.47 0.58 0.40 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-fc39a83/imblearn/metrics/tests/test_pairwise.py000066400000000000000000000143731512206630300311760ustar00rootroot00000000000000"""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.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-fc39a83/imblearn/metrics/tests/test_score_objects.py000066400000000000000000000040531512206630300321710ustar00rootroot00000000000000"""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-fc39a83/imblearn/model_selection/000077500000000000000000000000001512206630300262675ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/model_selection/__init__.py000066400000000000000000000003211512206630300303740ustar00rootroot00000000000000""" 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-fc39a83/imblearn/model_selection/_split.py000066400000000000000000000104621512206630300301360ustar00rootroot00000000000000import 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-fc39a83/imblearn/model_selection/tests/000077500000000000000000000000001512206630300274315ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/model_selection/tests/__init__.py000066400000000000000000000000001512206630300315300ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/model_selection/tests/test_split.py000066400000000000000000000064201512206630300321770ustar00rootroot00000000000000import 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-fc39a83/imblearn/over_sampling/000077500000000000000000000000001512206630300257675ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/over_sampling/__init__.py000066400000000000000000000007721512206630300301060ustar00rootroot00000000000000""" 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-fc39a83/imblearn/over_sampling/_adasyn.py000066400000000000000000000172531512206630300277670ustar00rootroot00000000000000"""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-fc39a83/imblearn/over_sampling/_random_over_sampler.py000066400000000000000000000231771512206630300325500ustar00rootroot00000000000000"""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-fc39a83/imblearn/over_sampling/_smote/000077500000000000000000000000001512206630300272555ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/over_sampling/_smote/__init__.py000066400000000000000000000005021512206630300313630ustar00rootroot00000000000000from 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-fc39a83/imblearn/over_sampling/_smote/base.py000066400000000000000000001077161512206630300305550ustar00rootroot00000000000000"""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) 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-fc39a83/imblearn/over_sampling/_smote/cluster.py000066400000000000000000000256221512206630300313170ustar00rootroot00000000000000"""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-fc39a83/imblearn/over_sampling/_smote/filter.py000066400000000000000000000440451512206630300311230ustar00rootroot00000000000000"""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-fc39a83/imblearn/over_sampling/_smote/tests/000077500000000000000000000000001512206630300304175ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/over_sampling/_smote/tests/__init__.py000066400000000000000000000000001512206630300325160ustar00rootroot00000000000000test_borderline_smote.py000066400000000000000000000066421512206630300353150ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000070601512206630300344410ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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-fc39a83/imblearn/over_sampling/_smote/tests/test_smote.py000066400000000000000000000116661512206630300331710ustar00rootroot00000000000000"""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-fc39a83/imblearn/over_sampling/_smote/tests/test_smote_nc.py000066400000000000000000000321621512206630300336430ustar00rootroot00000000000000"""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 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-fc39a83/imblearn/over_sampling/_smote/tests/test_smoten.py000066400000000000000000000062511512206630300333410ustar00rootroot00000000000000import numpy as np import pytest from sklearn.exceptions import DataConversionWarning from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder from sklearn.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-fc39a83/imblearn/over_sampling/_smote/tests/test_svm_smote.py000066400000000000000000000054541512206630300340540ustar00rootroot00000000000000import 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-fc39a83/imblearn/over_sampling/base.py000066400000000000000000000047651512206630300272670ustar00rootroot00000000000000""" 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-fc39a83/imblearn/over_sampling/tests/000077500000000000000000000000001512206630300271315ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/over_sampling/tests/__init__.py000066400000000000000000000000001512206630300312300ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/over_sampling/tests/test_adasyn.py000066400000000000000000000076011512206630300320250ustar00rootroot00000000000000"""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-fc39a83/imblearn/over_sampling/tests/test_common.py000066400000000000000000000071161512206630300320370ustar00rootroot00000000000000from 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.py000066400000000000000000000234321512206630300345250ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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 ( _convert_container, assert_allclose, assert_array_equal, ) 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 == "timedelta64[ns]" scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/pipeline.py000066400000000000000000001624251512206630300253130ustar00rootroot00000000000000""" 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-fc39a83/imblearn/tensorflow/000077500000000000000000000000001512206630300253245ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/tensorflow/__init__.py000066400000000000000000000003241512206630300274340ustar00rootroot00000000000000"""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-fc39a83/imblearn/tensorflow/_generator.py000066400000000000000000000063631512206630300300330ustar00rootroot00000000000000"""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-fc39a83/imblearn/tensorflow/tests/000077500000000000000000000000001512206630300264665ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/tensorflow/tests/__init__.py000066400000000000000000000000001512206630300305650ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/tensorflow/tests/test_generator.py000066400000000000000000000124641512206630300320740ustar00rootroot00000000000000import 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-fc39a83/imblearn/tests/000077500000000000000000000000001512206630300242645ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/tests/__init__.py000066400000000000000000000000001512206630300263630ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/tests/test_base.py000066400000000000000000000065051512206630300266150ustar00rootroot00000000000000"""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-fc39a83/imblearn/tests/test_common.py000066400000000000000000000065431512206630300271750ustar00rootroot00000000000000"""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-fc39a83/imblearn/tests/test_docstring_parameters.py000066400000000000000000000201341512206630300321140ustar00rootroot00000000000000# 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-fc39a83/imblearn/tests/test_exceptions.py000066400000000000000000000005671512206630300300660ustar00rootroot00000000000000"""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-fc39a83/imblearn/tests/test_pipeline.py000066400000000000000000001372441512206630300275150ustar00rootroot00000000000000""" 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 + SVC clf = SVC(gamma="scale", probability=True, random_state=0) pca = PCA(svd_solver="full", n_components="mle", whiten=True) pipe = Pipeline([("pca", pca), ("svc", 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 = SVC( gamma="scale", probability=True, random_state=0, decision_function_shape="ovr", ) for preprocessing in [scaler, pca]: pipe = Pipeline([("preprocess", preprocessing), ("svc", 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 + SVC clf = SVC(gamma="scale", probability=True, random_state=0) transf = DummyTransf() pipe = Pipeline([("transf", clone(transf)), ("svc", clf)]) cached_pipe = Pipeline([("transf", transf), ("svc", 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 = SVC(gamma="scale", probability=True, random_state=0) transf_2 = DummyTransf() cached_pipe_2 = Pipeline( [("transf_2", transf_2), ("svc", 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 Transformer + SVC clf = SVC(gamma="scale", probability=True, random_state=0) transf = DummySampler() pipe = Pipeline([("transf", clone(transf)), ("svc", clf)]) cached_pipe = Pipeline([("transf", transf), ("svc", 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 = SVC(gamma="scale", probability=True, random_state=0) transf_2 = DummySampler() cached_pipe_2 = Pipeline( [("transf_2", transf_2), ("svc", 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 + SVC clf = SVC(gamma="scale", probability=True, random_state=0) pca = PCA() rus = RandomUnderSampler(random_state=0) pipe = Pipeline([("pca", pca), ("rus", rus), ("svc", 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 + SVC clf = SVC(gamma="scale", probability=True, random_state=0) pca = PCA() rus = RandomUnderSampler(random_state=0) pipe = Pipeline([("rus", rus), ("pca", pca), ("svc", 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-fc39a83/imblearn/tests/test_public_functions.py000066400000000000000000000077041512206630300312530ustar00rootroot00000000000000"""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-fc39a83/imblearn/under_sampling/000077500000000000000000000000001512206630300261315ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/under_sampling/__init__.py000066400000000000000000000014131512206630300302410ustar00rootroot00000000000000""" 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-fc39a83/imblearn/under_sampling/_prototype_generation/000077500000000000000000000000001512206630300325505ustar00rootroot00000000000000__init__.py000066400000000000000000000004361512206630300346050ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000170371512206630300367450ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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-fc39a83/imblearn/under_sampling/_prototype_generation/tests/000077500000000000000000000000001512206630300337125ustar00rootroot00000000000000__init__.py000066400000000000000000000000001512206630300357320ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/under_sampling/_prototype_generation/teststest_cluster_centroids.py000066400000000000000000000122511512206630300410000ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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-fc39a83/imblearn/under_sampling/_prototype_selection/000077500000000000000000000000001512206630300324025ustar00rootroot00000000000000__init__.py000066400000000000000000000024551512206630300344420ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000216211512206630300405630ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000527211512206630300402470ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000150121512206630300406020ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000262411512206630300346620ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000207371512206630300405700ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000171561512206630300370440ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000114521512206630300372370ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000122541512206630300353570ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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-fc39a83/imblearn/under_sampling/_prototype_selection/tests/000077500000000000000000000000001512206630300335445ustar00rootroot00000000000000__init__.py000066400000000000000000000000001512206630300355640ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/under_sampling/_prototype_selection/teststest_allknn.py000066400000000000000000000211051512206630300363540ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000073761512206630300426400ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000101641512206630300423040ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000072561512206630300426570ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000155151512206630300367260ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000043551512206630300426300ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000073441512206630300411040ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000127061512206630300413040ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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 == "timedelta64[ns]" test_repeated_edited_nearest_neighbours.py000066400000000000000000000210271512206630300441550ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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.py000066400000000000000000000052121512206630300374150ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/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-fc39a83/imblearn/under_sampling/base.py000066400000000000000000000075361512206630300274300ustar00rootroot00000000000000""" 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-fc39a83/imblearn/utils/000077500000000000000000000000001512206630300242625ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/utils/__init__.py000066400000000000000000000005551512206630300264000ustar00rootroot00000000000000""" 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-fc39a83/imblearn/utils/_docstring.py000066400000000000000000000027501512206630300267730ustar00rootroot00000000000000"""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-fc39a83/imblearn/utils/_show_versions.py000066400000000000000000000042061512206630300277050ustar00rootroot00000000000000""" 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-fc39a83/imblearn/utils/_tags.py000066400000000000000000000204031512206630300257300ustar00rootroot00000000000000from 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-fc39a83/imblearn/utils/_test_common/000077500000000000000000000000001512206630300267505ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/utils/_test_common/__init__.py000066400000000000000000000000001512206630300310470ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/utils/_test_common/instance_generator.py000066400000000000000000000176471512206630300332130ustar00rootroot00000000000000# 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-fc39a83/imblearn/utils/_validation.py000066400000000000000000000565511512206630300271410ustar00rootroot00000000000000"""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 == "datetime64[ns]" and props["dtypes"][col] == "timedelta64[ns]" ): 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-fc39a83/imblearn/utils/deprecation.py000066400000000000000000000032771512206630300271420ustar00rootroot00000000000000"""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-fc39a83/imblearn/utils/estimator_checks.py000066400000000000000000001010741512206630300301660ustar00rootroot00000000000000"""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-fc39a83/imblearn/utils/testing.py000066400000000000000000000122451512206630300263150ustar00rootroot00000000000000"""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-fc39a83/imblearn/utils/tests/000077500000000000000000000000001512206630300254245ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/utils/tests/__init__.py000066400000000000000000000000001512206630300275230ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/imblearn/utils/tests/test_deprecation.py000066400000000000000000000010521512206630300313300ustar00rootroot00000000000000"""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-fc39a83/imblearn/utils/tests/test_docstring.py000066400000000000000000000037151512206630300310370ustar00rootroot00000000000000"""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-fc39a83/imblearn/utils/tests/test_estimator_checks.py000066400000000000000000000070171512206630300323710ustar00rootroot00000000000000import 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-fc39a83/imblearn/utils/tests/test_min_dependencies.py000066400000000000000000000052711512206630300323330ustar00rootroot00000000000000"""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-fc39a83/imblearn/utils/tests/test_show_versions.py000066400000000000000000000034321512206630300317470ustar00rootroot00000000000000"""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-fc39a83/imblearn/utils/tests/test_testing.py000066400000000000000000000032341512206630300305140ustar00rootroot00000000000000"""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-fc39a83/imblearn/utils/tests/test_validation.py000066400000000000000000000324611512206630300311750ustar00rootroot00000000000000"""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-fc39a83/maint_tools/000077500000000000000000000000001512206630300236615ustar00rootroot00000000000000scikit-learn-contrib-imbalanced-learn-fc39a83/maint_tools/test_docstring.py000066400000000000000000000212161512206630300272700ustar00rootroot00000000000000import 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-fc39a83/pixi.lock000066400000000000000000034076001512206630300231660ustar00rootroot00000000000000version: 6 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/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/linux-64/coverage-7.13.0-py310h3406613_0.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/linux-64/icu-78.1-h33c6efd_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/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.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.7.3-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.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.1-hb9d3cd8_2.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.51.1-hf4e2dac_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-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.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.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.0-h26f9b46_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.19.2-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/linux-64/python-3.10.19-h3c07f61_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/linux-64/tk-8.6.13-noxft_ha0e22de_103.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.3.0-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-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: ./ osx-64: - 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_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/osx-64/coverage-7.13.0-py310hd951482_0.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/osx-64/icu-78.1-h14c5de8_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/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-21.1.8-h3d58e20_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.3-heffb93a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.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.1-hd471939_2.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.51.1-hd09e2f1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.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.0-h230baf5_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.19.2-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/osx-64/python-3.10.19-h988dfef_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-64/tk-8.6.13-hf689a15_3.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.3.0-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-h8577fbf_0.conda - pypi: ./ osx-arm64: - 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_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/osx-arm64/coverage-7.13.0-py310hf4fd40f_0.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/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-21.1.8-hf598326_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.3-haf25636_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.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.1-h39f12f2_2.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.51.1-h1b79a29_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.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.0-h5503f6c_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.19.2-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/osx-arm64/python-3.10.19-hcd7f573_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-arm64/tk-8.6.13-h892fb3f_3.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.3.0-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-h8577fbf_0.conda - pypi: ./ win-64: - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/win-64/coverage-7.13.0-py310hdb0e946_0.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/win-64/icu-78.1-h637d24d_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/win-64/libblas-3.11.0-5_hf2e6a31_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-5_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h4379cf1_1003.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-5_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.1-hf5d6505_1.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.1-h3cfd58e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-h779ef1b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-21.1.8-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.0-hac47afa_454.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/openssl-3.6.0-h725018a_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.19.2-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/win-64/python-3.10.19-hc20f281_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-hd094cb3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-2.0.0-pyh5ca1d4c_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.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.3.0-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-h8577fbf_0.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.3-h2b53caa_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_33.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/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.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/noarch/ca-certificates-2025.11.12-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/linux-64/coverage-7.13.0-py310h3406613_0.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/linux-64/h5py-3.15.1-nompi_py310h4aa865e_101.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h1b119a7_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.1-h33c6efd_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/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.4-h3f801dc_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.17.0-h4e3cde8_1.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.7.3-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_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.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-hf4e2dac_1.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_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-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.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.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/linux-64/ml_dtypes-0.5.4-np2py310h3d4ba91_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.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.0-h26f9b46_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.18.0-py310h03d9f68_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.19.2-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/linux-64/python-3.10.19-h3c07f61_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/linux-64/tk-8.6.13-noxft_ha0e22de_103.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.3.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/tzdata-2025c-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: ./ osx-64: - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_8.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/noarch/ca-certificates-2025.11.12-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/osx-64/coverage-7.13.0-py310hd951482_0.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/osx-64/h5py-3.15.1-nompi_py310h30b6785_101.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.6-nompi_hc1508a4_104.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.1-h14c5de8_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/osx-64/krb5-1.21.3-h37d8d59_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libaec-1.1.4-ha6bc127_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-5_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.17.0-h7dd4100_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-21.1.8-h3d58e20_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.7.3-heffb93a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-5_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.67.0-h3338091_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.51.1-hd09e2f1_1.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.1-hd23fc13_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.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/osx-64/ml_dtypes-0.5.4-np2py310hc20d22f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.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.0-h230baf5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/optree-0.18.0-py310h8cf47bc_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.19.2-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/osx-64/python-3.10.19-h988dfef_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-64/tk-8.6.13-hf689a15_3.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.3.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/tzdata-2025c-h8577fbf_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/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.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/noarch/ca-certificates-2025.11.12-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/osx-arm64/coverage-7.13.0-py310hf4fd40f_0.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/osx-arm64/h5py-3.15.1-nompi_py310hc571fcd_101.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.6-nompi_hd3baa01_104.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/osx-arm64/krb5-1.21.3-h237132a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.4-h51d1e36_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.17.0-hdece5d2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_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.7.3-haf25636_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.67.0-hc438710_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.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.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.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/osx-arm64/ml_dtypes-0.5.4-np2py310h9cec423_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.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.0-h5503f6c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.18.0-py310h0e897d2_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.19.2-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/osx-arm64/python-3.10.19-hcd7f573_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.10-8_cp310.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-arm64/tk-8.6.13-h892fb3f_3.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.3.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/tzdata-2025c-h8577fbf_0.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/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/linux-64/coverage-7.13.0-py310h3406613_0.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/linux-64/icu-78.1-h33c6efd_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/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.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.7.3-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.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.1-hb9d3cd8_2.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.51.1-hf4e2dac_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-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.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.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.0-h26f9b46_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.0.3-py310h7cbd5c2_1.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.19.2-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/linux-64/python-3.10.19-h3c07f61_2_cpython.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-2025.3-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-2025.2-pyhd8ed1ab_0.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/noarch/setuptools-80.9.0-pyhff2d567_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.5-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/linux-64/tk-8.6.13-noxft_ha0e22de_103.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.3.0-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-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: ./ osx-64: - 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_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/osx-64/coverage-7.13.0-py310hd951482_0.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/osx-64/icu-78.1-h14c5de8_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/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-21.1.8-h3d58e20_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.3-heffb93a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.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.1-hd471939_2.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.51.1-hd09e2f1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.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.0-h230baf5_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pandas-2.0.3-py310h5e4fcda_1.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.19.2-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/osx-64/python-3.10.19-h988dfef_2_cpython.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-2025.3-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-2025.2-pyhd8ed1ab_0.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/noarch/setuptools-80.9.0-pyhff2d567_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.5-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/osx-64/tk-8.6.13-hf689a15_3.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.3.0-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-h8577fbf_0.conda - pypi: ./ osx-arm64: - 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_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/osx-arm64/coverage-7.13.0-py310hf4fd40f_0.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/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-21.1.8-hf598326_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.3-haf25636_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.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.1-h39f12f2_2.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.51.1-h1b79a29_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.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.0-h5503f6c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.0.3-py310h1cdf563_1.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.19.2-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/osx-arm64/python-3.10.19-hcd7f573_2_cpython.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-2025.3-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-2025.2-pyhd8ed1ab_0.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/noarch/setuptools-80.9.0-pyhff2d567_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.5-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/osx-arm64/tk-8.6.13-h892fb3f_3.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.3.0-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-h8577fbf_0.conda - pypi: ./ win-64: - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/win-64/coverage-7.13.0-py310hdb0e946_0.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/win-64/icu-78.1-h637d24d_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/win-64/libblas-3.11.0-5_hf2e6a31_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-5_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h4379cf1_1003.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-5_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.1-hf5d6505_1.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.1-h3cfd58e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-h779ef1b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-21.1.8-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.0-hac47afa_454.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/openssl-3.6.0-h725018a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-2.0.3-py310h1c4a608_1.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.19.2-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/win-64/python-3.10.19-hc20f281_2_cpython.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-2025.3-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-2025.2-pyhd8ed1ab_0.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/noarch/setuptools-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-hd094cb3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-2.0.0-pyh5ca1d4c_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.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.3.0-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-h8577fbf_0.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.3-h2b53caa_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_33.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/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.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/linux-64/backports.zstd-1.2.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_8.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/noarch/ca-certificates-2025.11.12-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-2025.11.12-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-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/linux-64/coverage-7.13.0-py310h3406613_0.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/linux-64/flatbuffers-24.3.25-h59595ed_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_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/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.62.2-py310h1b8f574_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.15.1-nompi_py310h4aa865e_101.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h1b119a7_104.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/linux-64/icu-73.2-h59595ed_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.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/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.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.4-h3f801dc_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.17.0-h4e3cde8_1.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.7.3-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.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.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_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.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.53-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.51.1-h0c1763c_1.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_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-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.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py310h3406613_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_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/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.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.0-h26f9b46_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/linux-64/optree-0.18.0-py310h03d9f68_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/linux-64/protobuf-4.25.3-py310h0e2eeba_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-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/linux-64/python-3.10.19-h3c07f61_2_cpython.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/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/noarch/requests-2.32.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_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/noarch/setuptools-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_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/noarch/tensorboard-2.16.2-pyhd8ed1ab_0.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/noarch/termcolor-3.2.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/linux-64/tk-8.6.13-noxft_ha0e22de_103.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.3.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/tzdata-2025c-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.0.1-py310h7c4b9e2_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: ./ osx-64: - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.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/osx-64/backports.zstd-1.2.0-py310h6194938_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_8.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/noarch/ca-certificates-2025.11.12-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-2025.11.12-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-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/osx-64/coverage-7.13.0-py310hd951482_0.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/osx-64/flatbuffers-24.3.25-h73e2aa4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_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/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/grpcio-1.62.2-py310h271164d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.15.1-nompi_py310h30b6785_101.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.6-nompi_hc1508a4_104.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/osx-64/icu-73.2-hf5e326d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.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/osx-64/krb5-1.21.3-h37d8d59_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.4-ha6bc127_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-5_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.17.0-h7dd4100_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-21.1.8-h3d58e20_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.7.3-heffb93a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.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.2-h8616949_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-5_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.67.0-h3338091_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.53-h380d223_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.51.1-hb99441e_1.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.1-hd23fc13_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py310hd951482_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_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/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.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.0-h230baf5_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/osx-64/optree-0.18.0-py310h8cf47bc_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/osx-64/protobuf-4.25.3-py310h533c97b_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-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/osx-64/python-3.10.19-h988dfef_2_cpython.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/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/noarch/requests-2.32.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_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/noarch/setuptools-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_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/noarch/tensorboard-2.16.2-pyhd8ed1ab_0.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/noarch/termcolor-3.2.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/osx-64/tk-8.6.13-hf689a15_3.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.3.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/tzdata-2025c-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.0.1-py310hd2d5e8d_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_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/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.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/osx-arm64/backports.zstd-1.2.0-py310hdc7f11d_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_8.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/noarch/ca-certificates-2025.11.12-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-2025.11.12-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-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/osx-arm64/coverage-7.13.0-py310hf4fd40f_0.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/osx-arm64/flatbuffers-24.3.25-hebf3989_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_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/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/grpcio-1.62.2-py310hf7687f1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.15.1-nompi_py310hc571fcd_101.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.6-nompi_hd3baa01_104.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/osx-arm64/icu-73.2-hc8870d7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.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/osx-arm64/krb5-1.21.3-h237132a_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.4-h51d1e36_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.17.0-hdece5d2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_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.7.3-haf25636_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.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.2-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.67.0-hc438710_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.53-hfab5511_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.51.1-h1b79a29_1.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.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py310hf4fd40f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_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/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.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.0-h5503f6c_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/osx-arm64/optree-0.18.0-py310h0e897d2_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/osx-arm64/protobuf-4.25.3-py310ha1b16c5_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-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/osx-arm64/python-3.10.19-hcd7f573_2_cpython.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/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/noarch/requests-2.32.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_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/noarch/setuptools-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_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/noarch/tensorboard-2.16.2-pyhd8ed1ab_0.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/noarch/termcolor-3.2.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/osx-arm64/tk-8.6.13-h892fb3f_3.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.3.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/tzdata-2025c-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.0.1-py310hfe3a0ae_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_1.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/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.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/noarch/ca-certificates-2025.11.12-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/linux-64/coverage-7.13.0-py311h3778330_0.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/linux-64/h5py-3.15.1-nompi_py311h0b2f468_101.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h1b119a7_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.1-h33c6efd_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.12.0-pyh753f3f9_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.21.3-h659f571_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.4-h3f801dc_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.17.0-h4e3cde8_1.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.7.3-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_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.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-hf4e2dac_1.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_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-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.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.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/linux-64/ml_dtypes-0.5.4-np2py311h912ec1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py311h2e04523_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.18.0-py311hdf67eae_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.19.2-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/linux-64/python-3.11.14-hd63d673_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.8.0-np2py311ha15b03d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.3-py311hbe70eeb_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/linux-64/tk-8.6.13-noxft_ha0e22de_103.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.3.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/tzdata-2025c-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: ./ osx-64: - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_8.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/noarch/ca-certificates-2025.11.12-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/osx-64/coverage-7.13.0-py311he13f9b5_0.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/osx-64/h5py-3.15.1-nompi_py311hc8b82f0_101.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.6-nompi_hc1508a4_104.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.1-h14c5de8_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.12.0-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.21.3-h37d8d59_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libaec-1.1.4-ha6bc127_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-5_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.17.0-h7dd4100_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-21.1.8-h3d58e20_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.7.3-heffb93a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-5_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.67.0-h3338091_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.51.1-hd09e2f1_1.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.1-hd23fc13_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.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/osx-64/ml_dtypes-0.5.4-np2py311hd5df55d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.3.5-py311hf157cb9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/optree-0.18.0-py311haec20ae_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.19.2-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/osx-64/python-3.11.14-h74c2667_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.3-h68b038d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.8.0-np2py311hd641367_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.16.3-py311hd77d3c2_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-64/tk-8.6.13-hf689a15_3.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.3.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/tzdata-2025c-h8577fbf_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/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.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/noarch/ca-certificates-2025.11.12-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/osx-arm64/coverage-7.13.0-py311ha9b3269_0.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/osx-arm64/h5py-3.15.1-nompi_py311hd5a25a3_101.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.6-nompi_hd3baa01_104.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.12.0-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.4-h51d1e36_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.17.0-hdece5d2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_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.7.3-haf25636_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.67.0-hc438710_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.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.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.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/osx-arm64/ml_dtypes-0.5.4-np2py311h76ff34e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py311h8685306_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.18.0-py311h5a5e7c7_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.19.2-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/osx-arm64/python-3.11.14-h18782d2_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.8.0-np2py311hece8d6f_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.16.3-py311ha71c161_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-arm64/tk-8.6.13-h892fb3f_3.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.3.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/tzdata-2025c-h8577fbf_0.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/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - 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.3.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/linux-64/backports.zstd-1.2.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_8.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/noarch/ca-certificates-2025.11.12-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-2025.11.12-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-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/linux-64/coverage-7.13.0-py311h3778330_0.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/linux-64/flatbuffers-25.2.10-hb7832b1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_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/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.73.1-py311h46a3a17_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.15.1-nompi_py311h0b2f468_101.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h1b119a7_104.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/linux-64/icu-75.1-he02047a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.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/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.4-h3f801dc_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.17.0-h4e3cde8_1.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.7.3-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.73.1-h3288cfb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.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/linux-64/libnghttp2-1.67.0-had1ee68_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.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.53-h421ea60_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.31.1-h49aed37_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h7b12aa8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-h0c1763c_1.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_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_cc-2.19.1-cpu_hf7abd0a_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_framework-2.19.1-cpu_h12ecb4a_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-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.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py311h3778330_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ml_dtypes-0.5.4-np2py311h912ec1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py311h2e04523_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_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/linux-64/optree-0.18.0-py311hdf67eae_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/linux-64/protobuf-6.31.1-py311h425ed32_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-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/linux-64/python-3.11.14-hd63d673_2_cpython.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/linux-64/re2-2025.11.05-h5301d42_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.8.0-np2py311ha15b03d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.3-py311hbe70eeb_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_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/noarch/tensorboard-2.19.0-pyhd8ed1ab_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_py311h6ac8430_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-base-2.19.1-cpu_py311h690c71f_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-estimator-2.19.1-cpu_py311ha1e2c70_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.2.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/linux-64/tk-8.6.13-noxft_ha0e22de_103.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.3.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/tzdata-2025c-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.0.1-py311h49ec1c0_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: ./ osx-64: - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.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/osx-64/backports.zstd-1.2.0-py311hd694d0d_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_8.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/noarch/ca-certificates-2025.11.12-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-2025.11.12-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-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/osx-64/coverage-7.13.0-py311he13f9b5_0.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/osx-64/flatbuffers-24.12.23-hd70532e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_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/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/grpcio-1.67.1-py311ha489736_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.15.1-nompi_py311hc8b82f0_101.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.6-nompi_hc1508a4_104.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/osx-64/icu-75.1-h120a0e1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.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/osx-64/krb5-1.21.3-h37d8d59_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.4-ha6bc127_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-5_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.17.0-h7dd4100_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-21.1.8-h3d58e20_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.7.3-heffb93a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.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.2-h8616949_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-5_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.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/osx-64/libnghttp2-1.67.0-h3338091_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.53-h380d223_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.51.1-hb99441e_1.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.1-hd23fc13_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py311he13f9b5_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_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/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.3.5-py311hf157cb9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_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/osx-64/optree-0.18.0-py311haec20ae_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/osx-64/protobuf-5.28.3-py311hc356e98_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-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/osx-64/python-3.11.14-h74c2667_2_cpython.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/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/noarch/requests-2.32.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.8.0-np2py311hd641367_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.16.3-py311hd77d3c2_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_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/noarch/tensorboard-2.18.0-pyhd8ed1ab_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/noarch/termcolor-3.2.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/osx-64/tk-8.6.13-hf689a15_3.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.3.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/tzdata-2025c-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.0.1-py311hf197a57_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_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/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.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/osx-arm64/backports.zstd-1.2.0-py311h97d6d38_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_8.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/noarch/ca-certificates-2025.11.12-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-2025.11.12-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-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/osx-arm64/coverage-7.13.0-py311ha9b3269_0.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/osx-arm64/flatbuffers-24.12.23-h28594ff_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_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/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/grpcio-1.67.1-py311ha413f7a_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.15.1-nompi_py311hd5a25a3_101.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.6-nompi_hd3baa01_104.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/osx-arm64/icu-75.1-hfee45f7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.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/osx-arm64/krb5-1.21.3-h237132a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_h07bc746_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.4-h51d1e36_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.17.0-hdece5d2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_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.7.3-haf25636_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-h0a426d6_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.2-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.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/osx-arm64/libnghttp2-1.67.0-hc438710_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.53-hfab5511_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.3-h3bd63a1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h07bc746_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.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.18.0-cpu_hf321e49_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtensorflow_framework-2.18.0-cpu_h2398287_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py311ha9b3269_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ml_dtypes-0.4.0-py311h9cb3ce9_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py311h8685306_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_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/osx-arm64/optree-0.18.0-py311h5a5e7c7_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/osx-arm64/protobuf-5.28.3-py311h155a34a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-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/osx-arm64/python-3.11.14-h18782d2_2_cpython.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/osx-arm64/re2-2024.07.02-h6589ca4_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.8.0-np2py311hece8d6f_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.16.3-py311ha71c161_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_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/noarch/tensorboard-2.18.0-pyhd8ed1ab_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.18.0-cpu_py311h9d3d1e9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-base-2.18.0-cpu_py311h5147c6a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-estimator-2.18.0-cpu_py311h61c9c21_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.2.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/osx-arm64/tk-8.6.13-h892fb3f_3.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.3.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/tzdata-2025c-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.0.1-py311h9408147_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_1.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/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/linux-64/coverage-7.13.0-py311h3778330_0.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/linux-64/icu-78.1-h33c6efd_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/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.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.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-hf4e2dac_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-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.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py311h2e04523_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_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.19.2-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/linux-64/python-3.11.14-hd63d673_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/linux-64/tk-8.6.13-noxft_ha0e22de_103.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.3.0-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-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: ./ osx-64: - 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_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/osx-64/coverage-7.13.0-py311he13f9b5_0.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/osx-64/icu-78.1-h14c5de8_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/osx-64/libblas-3.11.0-5_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-21.1.8-h3d58e20_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.3-heffb93a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-5_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.51.1-hd09e2f1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.3.5-py311hf157cb9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_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.19.2-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/osx-64/python-3.11.14-h74c2667_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-64/tk-8.6.13-hf689a15_3.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.3.0-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-h8577fbf_0.conda - pypi: ./ osx-arm64: - 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_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/osx-arm64/coverage-7.13.0-py311ha9b3269_0.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/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.3-haf25636_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py311h8685306_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_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.19.2-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/osx-arm64/python-3.11.14-h18782d2_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-arm64/tk-8.6.13-h892fb3f_3.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.3.0-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-h8577fbf_0.conda - pypi: ./ win-64: - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/win-64/coverage-7.13.0-py311h3f79411_0.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/win-64/icu-78.1-h637d24d_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/win-64/libblas-3.11.0-5_hf2e6a31_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-5_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h4379cf1_1003.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-5_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.1-hf5d6505_1.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.1-h3cfd58e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-h779ef1b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-21.1.8-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.0-hac47afa_454.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.3.5-py311h80b3fa1_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_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.19.2-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/win-64/python-3.11.14-h0159041_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-hd094cb3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.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.3.0-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-h8577fbf_0.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.3-h2b53caa_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_33.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/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/linux-64/coverage-7.13.0-py311h3778330_0.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/linux-64/icu-78.1-h33c6efd_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/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.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.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-hf4e2dac_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-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.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py311h2e04523_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_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.19.2-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/linux-64/python-3.11.14-hd63d673_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/linux-64/tk-8.6.13-noxft_ha0e22de_103.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.3.0-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-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: ./ osx-64: - 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_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/osx-64/coverage-7.13.0-py311he13f9b5_0.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/osx-64/icu-78.1-h14c5de8_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/osx-64/libblas-3.11.0-5_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-21.1.8-h3d58e20_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.3-heffb93a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-5_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.51.1-hd09e2f1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.3.5-py311hf157cb9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_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.19.2-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/osx-64/python-3.11.14-h74c2667_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-64/tk-8.6.13-hf689a15_3.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.3.0-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-h8577fbf_0.conda - pypi: ./ osx-arm64: - 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_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/osx-arm64/coverage-7.13.0-py311ha9b3269_0.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/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.3-haf25636_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py311h8685306_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_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.19.2-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/osx-arm64/python-3.11.14-h18782d2_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-arm64/tk-8.6.13-h892fb3f_3.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.3.0-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-h8577fbf_0.conda - pypi: ./ win-64: - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/win-64/coverage-7.13.0-py311h3f79411_0.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/win-64/icu-78.1-h637d24d_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/win-64/libblas-3.11.0-5_hf2e6a31_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-5_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h4379cf1_1003.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-5_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.1-hf5d6505_1.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.1-h3cfd58e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-h779ef1b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-21.1.8-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.0-hac47afa_454.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.3.5-py311h80b3fa1_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_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.19.2-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/win-64/python-3.11.14-h0159041_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-hd094cb3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.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.3.0-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-h8577fbf_0.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.3-h2b53caa_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_33.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/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/linux-64/coverage-7.13.0-py312h8a5da7c_0.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/linux-64/icu-78.1-h33c6efd_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/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.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.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-hf4e2dac_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-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.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_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.19.2-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/linux-64/python-3.12.12-hd63d673_1_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/linux-64/tk-8.6.13-noxft_ha0e22de_103.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.3.0-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-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: ./ osx-64: - 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_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/osx-64/coverage-7.13.0-py312hacf3034_0.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/osx-64/icu-78.1-h14c5de8_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/osx-64/libblas-3.11.0-5_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-21.1.8-h3d58e20_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.3-heffb93a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-5_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.51.1-hd09e2f1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.3.5-py312ha3982b3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_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.19.2-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/osx-64/python-3.12.12-h74c2667_1_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-64/tk-8.6.13-hf689a15_3.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.3.0-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-h8577fbf_0.conda - pypi: ./ osx-arm64: - 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_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/osx-arm64/coverage-7.13.0-py312h5748b74_0.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/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.3-haf25636_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py312h85ea64e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_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.19.2-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/osx-arm64/python-3.12.12-h18782d2_1_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-arm64/tk-8.6.13-h892fb3f_3.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.3.0-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-h8577fbf_0.conda - pypi: ./ win-64: - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/win-64/coverage-7.13.0-py312h05f76fc_0.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/win-64/icu-78.1-h637d24d_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/win-64/libblas-3.11.0-5_hf2e6a31_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-5_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h4379cf1_1003.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-5_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.1-hf5d6505_1.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.1-h3cfd58e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-h779ef1b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-21.1.8-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.0-hac47afa_454.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.3.5-py312ha72d056_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_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.19.2-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/win-64/python-3.12.12-h0159041_1_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.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/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-hd094cb3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.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.3.0-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-h8577fbf_0.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.3-h2b53caa_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_33.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/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/linux-64/coverage-7.13.0-py314h67df5f8_0.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/linux-64/icu-78.1-h33c6efd_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/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-hf4e2dac_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py314h2b28147_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_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.19.2-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/linux-64/python-3.14.2-h32b2ec7_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_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.8.0-np2py314hf09ca88_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.3-py314hf07bd8e_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/linux-64/tk-8.6.13-noxft_ha0e22de_103.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.3.0-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-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: ./ osx-64: - 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_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/osx-64/coverage-7.13.0-py314hb9c7d66_0.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/osx-64/icu-78.1-h14c5de8_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/osx-64/libblas-3.11.0-5_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-21.1.8-h3d58e20_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.3-heffb93a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-5_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-h6e16a3a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.51.1-hd09e2f1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.3.5-py314hf08249b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_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.19.2-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/osx-64/python-3.14.2-hf88997e_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_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.8.0-np2py314h17d75c0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.16.3-py314hbb40827_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-64/tk-8.6.13-hf689a15_3.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.3.0-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-h8577fbf_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/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/osx-arm64/coverage-7.13.0-py314hb7e19f3_0.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/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.3-haf25636_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h5505292_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py314h5b5928d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_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.19.2-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/osx-arm64/python-3.14.2-h40d2674_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_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.8.0-np2py314h3d7c909_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.16.3-py314h725efaa_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-arm64/tk-8.6.13-h892fb3f_3.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.3.0-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-h8577fbf_0.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/win-64/bzip2-1.0.8-h0ad9c76_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/win-64/coverage-7.13.0-py314h2359020_0.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/win-64/icu-78.1-h637d24d_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/win-64/libblas-3.11.0-5_hf2e6a31_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-5_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h4379cf1_1003.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-5_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-h2466b09_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.1-hf5d6505_1.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.1-h3cfd58e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-h779ef1b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-21.1.8-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.0-hac47afa_454.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.3.5-py314h06c3c77_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_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.19.2-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/win-64/python-3.14.2-h4b44e0e_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.8.0-np2py314h1b5b07a_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.16.3-py314h221f224_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-hd094cb3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.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.3.0-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-h8577fbf_0.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.3-h2b53caa_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_33.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/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/linux-64/coverage-7.13.0-py314h67df5f8_0.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/linux-64/icu-78.1-h33c6efd_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/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-hf4e2dac_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py314h2b28147_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py314ha0b5721_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.19.2-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/linux-64/python-3.14.2-h32b2ec7_100_cp314.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-2025.3-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-2025.2-pyhd8ed1ab_0.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.8.0-np2py314hf09ca88_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.3-py314hf07bd8e_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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.5-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/linux-64/tk-8.6.13-noxft_ha0e22de_103.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.3.0-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-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: ./ osx-64: - 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_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/osx-64/coverage-7.13.0-py314hb9c7d66_0.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/osx-64/icu-78.1-h14c5de8_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/osx-64/libblas-3.11.0-5_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-21.1.8-h3d58e20_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.3-heffb93a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-5_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-h6e16a3a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.51.1-hd09e2f1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.3.5-py314hf08249b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pandas-2.3.3-py314hc4308db_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.19.2-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/osx-64/python-3.14.2-hf88997e_100_cp314.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-2025.3-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-2025.2-pyhd8ed1ab_0.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.8.0-np2py314h17d75c0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.16.3-py314hbb40827_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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.5-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/osx-64/tk-8.6.13-hf689a15_3.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.3.0-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-h8577fbf_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/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/osx-arm64/coverage-7.13.0-py314hb7e19f3_0.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/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.3-haf25636_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h5505292_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py314h5b5928d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.3.3-py314ha3d490a_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.19.2-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/osx-arm64/python-3.14.2-h40d2674_100_cp314.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-2025.3-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-2025.2-pyhd8ed1ab_0.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.8.0-np2py314h3d7c909_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.16.3-py314h725efaa_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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.5-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/osx-arm64/tk-8.6.13-h892fb3f_3.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.3.0-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-h8577fbf_0.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/win-64/bzip2-1.0.8-h0ad9c76_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/win-64/coverage-7.13.0-py314h2359020_0.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/win-64/icu-78.1-h637d24d_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/win-64/libblas-3.11.0-5_hf2e6a31_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-5_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h4379cf1_1003.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-5_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-h2466b09_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.1-hf5d6505_1.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.1-h3cfd58e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-h779ef1b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-21.1.8-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.0-hac47afa_454.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.3.5-py314h06c3c77_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-24.2-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pandas-2.3.3-py314hd8fd7ce_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.19.2-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/win-64/python-3.14.2-h4b44e0e_100_cp314.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-2025.3-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-2025.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.8.0-np2py314h1b5b07a_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.16.3-py314h221f224_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-hd094cb3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.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.3.0-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-h8577fbf_0.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.3-h2b53caa_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_33.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/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.1-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-hf4e2dac_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py314h2b28147_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.2-h32b2ec7_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_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.8.0-np2py314hf09ca88_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.3-py314hf07bd8e_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: ./ osx-64: - 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_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.1-h14c5de8_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-5_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-21.1.8-h3d58e20_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.3-heffb93a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-5_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-h6e16a3a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.51.1-hd09e2f1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.3.5-py314hf08249b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.14.2-hf88997e_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_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.8.0-np2py314h17d75c0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.16.3-py314hbb40827_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-64/tk-8.6.13-hf689a15_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-h8577fbf_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/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.3-haf25636_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h5505292_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py314h5b5928d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.2-h40d2674_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_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.8.0-np2py314h3d7c909_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.16.3-py314h725efaa_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-arm64/tk-8.6.13-h892fb3f_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-h8577fbf_0.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/win-64/bzip2-1.0.8-h0ad9c76_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.1-h637d24d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-5_hf2e6a31_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-5_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h4379cf1_1003.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-5_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-h2466b09_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.1-hf5d6505_1.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.1-h3cfd58e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-h779ef1b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-21.1.8-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.0-hac47afa_454.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.3.5-py314h06c3c77_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.2-h4b44e0e_100_cp314.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.8.0-np2py314h1b5b07a_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.16.3-py314h221f224_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-hd094cb3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-h8577fbf_0.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.3-h2b53caa_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_33.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/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - 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.3.1-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/linux-64/alsa-lib-1.2.15.1-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.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/linux-64/argon2-cffi-bindings-25.1.0-py311h49ec1c0_2.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.0.5-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.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-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.2.0-py311h6b1f9c4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.3-pyha770c72_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/noarch/bleach-6.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.3.0-h5f6438b_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-py311h66f275b_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.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/noarch/ca-certificates-2025.11.12-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/linux-64/cairo-1.18.4-h3394656_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.11.12-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py311h03d9500_1.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.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_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/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/linux-64/contourpy-1.3.3-py311hdf67eae_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.13.0-py311h3778330_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.11.14-py311hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-46.0.3-py311h2005dd1_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.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.18-py311hc665b79_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.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.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_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/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.20.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/flatbuffers-25.2.10-hb7832b1_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/linux-64/fontconfig-2.15.0-h7e30c49_1.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/linux-64/fonttools-4.61.1-py311h3778330_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.1-ha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_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/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.73.1-py311h46a3a17_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.15.1-nompi_py311h0b2f468_101.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.2.0-h15599e2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h1b119a7_104.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/linux-64/icu-75.1-he02047a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/id-1.5.0-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.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/ipykernel-7.1.0-pyha191276_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.8.0-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-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.context-6.0.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.functools-4.4.0-pyhd8ed1ab_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.12.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.0.0-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.25.1-pyhe01879c_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.25.1-he01879c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.7.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.0-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.17.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.5.1-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/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.9-py311h724c32c_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_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/linux-64/lcms2-2.17-h717163a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.4-h3f801dc_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_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-5_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.7-default_h99862b1_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.7-default_h746c552_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.17.0-h4e3cde8_1.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.125-hb03c661_1.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_2.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.7.3-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.1-h73754d4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.3-h6548e54_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.73.1-h3288cfb_1.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.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.8-hf7376ad_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.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/linux-64/libnghttp2-1.67.0-had1ee68_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.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.53-h421ea60_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.1-h5c52fec_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.31.1-h49aed37_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h7b12aa8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-h0c1763c_1.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_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_cc-2.19.1-cpu_hf7abd0a_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_framework-2.19.1-cpu_h12ecb4a_2.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.41.3-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.328.1-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.1-hca5e8e5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_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.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py311h3778330_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.8-py311h38be061_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.8-py311h0f3be63_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.1-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.1.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ml_dtypes-0.5.4-np2py311h912ec1f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-10.8.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/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/nbclient-0.10.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/nh3-0.3.2-py310h1570de5_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/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py311h2e04523_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.10.0-pyhcf101f3_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.10-he970967_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_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/linux-64/optree-0.18.0-py311hdf67eae_0.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/linux-64/pandas-2.3.3-py311hed34c8f_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.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.47-haa7fec5_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pexpect-4.9.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.0.0-py311hf88fc01_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.3-pyh8b19718_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/noarch/platformdirs-4.5.1-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.5.1-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.23.1-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/linux-64/protobuf-6.31.1-py311h425ed32_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.3-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/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.25.1-pyhd8ed1ab_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-2.22-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.16.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.10.1-py311he4c1a5a_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/linux-64/python-3.11.14-hd63d673_2_cpython.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-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.14-hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.3-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-2025.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py311h3778330_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py311h2315fbb_0.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.10.1-h6f76662_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_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.32.5-pyhd8ed1ab_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-14.2.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/linux-64/rpds-py-0.30.0-py311h902ca64_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.8.0-np2py311ha15b03d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.3-py311hbe70eeb_2.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/linux-64/secretstorage-3.4.1-py311h38be061_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_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/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.0.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.1-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.6.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.20.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.6.5-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/linux-64/statsmodels-0.14.6-py311h0372a8f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.19.0-pyhd8ed1ab_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_py311h6ac8430_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-base-2.19.1-cpu_py311h690c71f_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-estimator-2.19.1-cpu_py311ha1e2c70_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_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/tinycss2-1.5.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_ha0e22de_103.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.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.3-py311h49ec1c0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.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-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py311hdf67eae_6.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.0-py311h49ec1c0_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.6.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.35.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.14-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.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.0.1-py311h49ec1c0_1.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.46-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.12-h4f16b4b_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.6-hb9d3cd8_2.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.6-hb9d3cd8_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.2-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_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.6-hb9d3cd8_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-h387f397_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.2-hceb46e0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - pypi: ./ osx-64: - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.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/absl-py-2.3.1-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.12.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/osx-64/argon2-cffi-bindings-25.1.0-py311hf197a57_2.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.0.5-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.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-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/backports.zstd-1.2.0-py311hd694d0d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.3-pyha770c72_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/noarch/bleach-6.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.3.0-h5f6438b_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-py311h7e844b6_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_8.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/noarch/ca-certificates-2025.11.12-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-2025.11.12-pyhd8ed1ab_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/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_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/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/osx-64/contourpy-1.3.3-py311haec20ae_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.13.0-py311he13f9b5_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.11.14-py311hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/debugpy-1.8.19-py311hd4eb7a1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.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.0-pyhd8ed1ab_0.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.20.1-pyhd8ed1ab_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.61.1-py311he13f9b5_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/freetype-2.14.1-h694c41f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_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/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/grpcio-1.67.1-py311ha489736_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.15.1-nompi_py311hc8b82f0_101.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.6-nompi_hc1508a4_104.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/osx-64/icu-75.1-h120a0e1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/id-1.5.0-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.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/ipykernel-7.1.0-pyh5552912_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.8.0-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-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.context-6.0.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.functools-4.4.0-pyhd8ed1ab_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.12.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.0.0-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.25.1-pyhe01879c_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.25.1-he01879c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.7.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.0-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.17.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.5.1-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/osx-64/kiwisolver-1.4.9-py311h591569d_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.21.3-h37d8d59_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/osx-64/lcms2-2.17-h72f5680_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/lerc-4.0.0-hcca01a6_1.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.4-ha6bc127_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-5_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-5_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.17.0-h7dd4100_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-21.1.8-h3d58e20_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.7.3-heffb93a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype-2.14.1-h694c41f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype6-2.14.1-h6912278_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.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.2-h8616949_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-5_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.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/osx-64/libnghttp2-1.67.0-h3338091_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.53-h380d223_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.20-hfdf4475_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.51.1-hb99441e_1.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.1-hd23fc13_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py311he13f9b5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.10.8-py311h6eed73b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.10.8-py311h48d7e91_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.1-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.1.4-pyhcf101f3_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/noarch/more-itertools-10.8.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/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/nbclient-0.10.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/nh3-0.3.2-py310h6cf5e2e_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/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.3.5-py311hf157cb9_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openjpeg-2.5.4-h87e8dc5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_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/osx-64/optree-0.18.0-py311haec20ae_0.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/osx-64/pandas-2.3.3-py311hca9a5ca_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.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.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/osx-64/pillow-12.0.0-py311h56465c8_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.3-pyh8b19718_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.1-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.5.1-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.23.1-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/osx-64/protobuf-5.28.3-py311hc356e98_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/psutil-7.1.3-py311h62e9434_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/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.25.1-pyhd8ed1ab_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-2.22-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.16.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.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/noarch/pyparsing-3.2.5-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/osx-64/python-3.11.14-h74c2667_2_cpython.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-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.14-hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.3-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-2025.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py311he13f9b5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyzmq-27.1.0-py311h0ab6910_0.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/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.32.5-pyhd8ed1ab_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-14.2.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/osx-64/rpds-py-0.30.0-py311hd2a4513_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.8.0-np2py311hd641367_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.16.3-py311hd77d3c2_2.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-1.8.3-pyh31c8845_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_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/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.0.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.1-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.6.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.20.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.6.5-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/osx-64/statsmodels-0.14.6-py311ha837bc1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.18.0-pyhd8ed1ab_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/noarch/termcolor-3.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh31c8845_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/tinycss2-1.5.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-hf689a15_3.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.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tornado-6.5.4-py311ha2bb86f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.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-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ukkonen-1.0.1-py311hd4d69bb_6.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/unicodedata2-17.0.0-py311hf197a57_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.6.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.35.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.14-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.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.0.1-py311hf197a57_1.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-h6c33b1e_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zlib-ng-2.3.2-h8bce59a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zstd-1.5.7-h3eecb57_6.conda - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - pypi: ./ osx-arm64: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.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/absl-py-2.3.1-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.12.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/osx-arm64/argon2-cffi-bindings-25.1.0-py311h9408147_2.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.0.5-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.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-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.2.0-py311h97d6d38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.3-pyha770c72_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/noarch/bleach-6.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.3.0-h5f6438b_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-py311hdc60ec4_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.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/noarch/ca-certificates-2025.11.12-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-2025.11.12-pyhd8ed1ab_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/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_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/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/osx-arm64/contourpy-1.3.3-py311h5a5e7c7_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.13.0-py311ha9b3269_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.11.14-py311hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.19-py311hc58e375_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.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.0-pyhd8ed1ab_0.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.20.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/flatbuffers-24.12.23-h28594ff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.61.1-py311ha9b3269_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/fqdn-1.5.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.1-hce30654_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_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/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/grpcio-1.67.1-py311ha413f7a_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.15.1-nompi_py311hd5a25a3_101.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.6-nompi_hd3baa01_104.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/osx-arm64/icu-75.1-hfee45f7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/id-1.5.0-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.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/ipykernel-7.1.0-pyh5552912_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.8.0-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-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.context-6.0.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.functools-4.4.0-pyhd8ed1ab_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.12.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.0.0-pyhcf101f3_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.25.1-pyhe01879c_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.25.1-he01879c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.7.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.0-pyh29332c3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.17.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.5.1-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/osx-arm64/kiwisolver-1.4.9-py311h26d6576_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_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/osx-arm64/lcms2-2.17-h7eeda09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-hd64df32_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_h07bc746_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.4-h51d1e36_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-5_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-5_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.17.0-hdece5d2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_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.7.3-haf25636_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.1-hce30654_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.1-h6da58f4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-h0a426d6_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.2-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.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/osx-arm64/libnghttp2-1.67.0-hc438710_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.53-hfab5511_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.3-h3bd63a1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h07bc746_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.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.18.0-cpu_hf321e49_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtensorflow_framework-2.18.0-cpu_h2398287_1.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.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py311ha9b3269_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.8-py311ha1ab1f8_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.8-py311h29553df_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.1-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.1.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ml_dtypes-0.4.0-py311h9cb3ce9_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-10.8.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/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/nbclient-0.10.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nest-asyncio-1.6.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nh3-0.3.2-py310h06fc29a_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/notebook-shim-0.2.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py311h8685306_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hbfb3c88_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_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/osx-arm64/optree-0.18.0-py311h5a5e7c7_0.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/osx-arm64/pandas-2.3.3-py311hdb8e4fa_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.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.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/osx-arm64/pillow-12.0.0-py311hb52a7e5_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.3-pyh8b19718_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.1-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.5.1-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.23.1-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/osx-arm64/protobuf-5.28.3-py311h155a34a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.1.3-py311h5bb9006_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/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.25.1-pyhd8ed1ab_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-2.22-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.16.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.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/noarch/pyparsing-3.2.5-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/osx-arm64/python-3.11.14-h18782d2_2_cpython.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-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.14-hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.3-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-2025.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py311ha9b3269_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.1.0-py311h13abfa4_0.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-2024.07.02-h6589ca4_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_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.32.5-pyhd8ed1ab_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-14.2.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/osx-arm64/rpds-py-0.30.0-py311h71babbd_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.8.0-np2py311hece8d6f_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.16.3-py311ha71c161_2.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-1.8.3-pyh31c8845_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_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/noarch/sniffio-1.3.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.0.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.1-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.6.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.20.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.6.5-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/osx-arm64/statsmodels-0.14.6-py311h09efe57_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.18.0-pyhd8ed1ab_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.18.0-cpu_py311h9d3d1e9_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-base-2.18.0-cpu_py311h5147c6a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-estimator-2.18.0-cpu_py311h61c9c21_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.2.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh31c8845_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/tinycss2-1.5.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_3.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.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.4-py311h9408147_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.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-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ukkonen-1.0.1-py311h57a9ea7_6.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.0-py311h9408147_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.6.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.35.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.14-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.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.0.1-py311h9408147_1.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-h888dc83_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.2-hed4e4f5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl - pypi: ./ 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/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - 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.3.1-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/linux-64/alsa-lib-1.2.15.1-hb03c661_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/babel-2.17.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.2.0-py312h90b7ffd_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.3-pyha770c72_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_8.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/noarch/ca-certificates-2025.11.12-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/linux-64/cairo-1.18.4-h3394656_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.11.12-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-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/linux-64/contourpy-1.3.3-py312hd9148b4_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cycler-0.12.1-pyhcf101f3_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/dbus-1.16.2-h24cb091_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/docutils-0.21.2-pyhd8ed1ab_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.2.10-hb7832b1_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/linux-64/fontconfig-2.15.0-h7e30c49_1.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/linux-64/fonttools-4.61.1-py312h8a5da7c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/freetype-2.14.1-ha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_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/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.14-hecca717_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.73.1-py312h6f3464c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.15.1-nompi_py312ha4f8f14_101.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.2.0-h15599e2_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h1b119a7_104.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/linux-64/icu-75.1-he02047a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_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/keras-3.8.0-pyh753f3f9_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.4.9-py312h0a2e395_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_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/linux-64/lcms2-2.17-h717163a_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.4-h3f801dc_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_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-5_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.7-default_h99862b1_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.7-default_h746c552_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.17.0-h4e3cde8_1.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.125-hb03c661_1.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_2.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.7.3-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.1-h73754d4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.3-h6548e54_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.73.1-h3288cfb_1.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.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.8-hf7376ad_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.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/linux-64/libnghttp2-1.67.0-had1ee68_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.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.53-h421ea60_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.1-h5c52fec_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.31.1-h49aed37_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h7b12aa8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-h0c1763c_1.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_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_cc-2.19.1-cpu_hf7abd0a_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_framework-2.19.1-cpu_h12ecb4a_2.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.41.3-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.328.1-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.1-hca5e8e5_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_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.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.8-py312h7900ff3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.8-py312he3d6523_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/linux-64/ml_dtypes-0.5.4-np2py312h0f77346_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/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.10.0-pyhcf101f3_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.10-he970967_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_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/linux-64/optree-0.18.0-py312hd9148b4_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pandas-2.3.3-py312hf79963d_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.2-pyhcf101f3_0.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.0.0-py312h50c33e8_2.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.31.1-py312hb8af0ac_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.3-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/noarch/pybtex-0.25.1-pyhd8ed1ab_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.16.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.5-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.10.1-py312h9da60e5_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.12-hd63d673_1_cpython.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-2025.3-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-2025.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_0.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.10.1-h6f76662_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/re2-2025.11.05-h5301d42_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.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/linux-64/scikit-learn-1.8.0-np2py312h3226591_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.3-py312h54fa4ab_2.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-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_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/noarch/snowballstemmer-3.0.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.1-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.6.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.20.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.6.5-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/linux-64/statsmodels-0.14.6-py312h4f23490_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.19.0-pyhd8ed1ab_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_py312h69ecde4_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-base-2.19.1-cpu_py312h0afb428_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-estimator-2.19.1-cpu_py312h6e6b5e2_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.2.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/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.3-py312h4c3975b_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-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.0-py312h4c3975b_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.0.1-py312h4c3975b_1.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.46-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.12-h4f16b4b_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.6-hb9d3cd8_2.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.6-hb9d3cd8_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.2-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_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.6-hb9d3cd8_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/noarch/zipp-3.23.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.2-hceb46e0_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: ./ osx-64: - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.1-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.17.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/backports.zstd-1.2.0-py312hcb931b7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.3-pyha770c72_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_8.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/noarch/ca-certificates-2025.11.12-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-2025.11.12-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-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/osx-64/contourpy-1.3.3-py312hd099df3_3.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/osx-64/flatbuffers-24.12.23-hd70532e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.61.1-py312hacf3034_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/freetype-2.14.1-h694c41f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_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/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/grpcio-1.67.1-py312h145213c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.15.1-nompi_py312hcf08926_101.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.6-nompi_hc1508a4_104.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/osx-64/icu-75.1-h120a0e1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_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/keras-3.8.0-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/kiwisolver-1.4.9-py312h90e26e8_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.21.3-h37d8d59_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/osx-64/lcms2-2.17-h72f5680_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/lerc-4.0.0-hcca01a6_1.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.4-ha6bc127_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-5_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-5_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.17.0-h7dd4100_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-21.1.8-h3d58e20_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.7.3-heffb93a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype-2.14.1-h694c41f_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype6-2.14.1-h6912278_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.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.2-h8616949_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-5_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.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/osx-64/libnghttp2-1.67.0-h3338091_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.53-h380d223_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.51.1-hb99441e_1.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.1-hd23fc13_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py312hacf3034_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.10.8-py312hb401068_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.10.8-py312h7894933_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/osx-64/ml_dtypes-0.4.0-py312h98e817e_2.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/osx-64/ncurses-6.5-h0622a9a_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.3.5-py312ha3982b3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openjpeg-2.5.4-h87e8dc5_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_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/osx-64/optree-0.18.0-py312hd099df3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pandas-2.3.3-py312h86abcb1_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pillow-12.0.0-py312hea0c9db_2.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.1.3-py312h01f6755_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/noarch/pybtex-0.25.1-pyhd8ed1ab_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.16.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.5-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/osx-64/python-3.12.12-h74c2667_1_cpython.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-2025.3-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-2025.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py312hacf3034_0.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/noarch/requests-2.32.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.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/osx-64/scikit-learn-1.8.0-np2py312hc921ccd_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.16.3-py312h79cf0a0_2.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-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_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/noarch/snowballstemmer-3.0.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.1-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.6.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.20.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.6.5-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/osx-64/statsmodels-0.14.6-py312h391ab28_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.18.0-pyhd8ed1ab_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/noarch/termcolor-3.2.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/osx-64/tk-8.6.13-hf689a15_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tornado-6.5.4-py312h404bc50_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-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/unicodedata2-17.0.0-py312h80b0991_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.0.1-py312h80b0991_1.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/noarch/zipp-3.23.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/zlib-ng-2.3.2-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/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.1-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.17.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.2.0-py312h84d6f5f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.3-pyha770c72_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_8.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/noarch/ca-certificates-2025.11.12-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-2025.11.12-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-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/osx-arm64/contourpy-1.3.3-py312h84eede6_3.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/osx-arm64/flatbuffers-24.12.23-h28594ff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.61.1-py312h5748b74_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.1-hce30654_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_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/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/grpcio-1.67.1-py312he4e58e5_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.15.1-nompi_py312h4eecd6b_101.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.6-nompi_hd3baa01_104.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/osx-arm64/icu-75.1-hfee45f7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_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/keras-3.8.0-pyh753f3f9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.4.9-py312hd8c8125_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_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/osx-arm64/lcms2-2.17-h7eeda09_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-hd64df32_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_h07bc746_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.4-h51d1e36_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-5_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-5_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.17.0-hdece5d2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_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.7.3-haf25636_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.1-hce30654_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.1-h6da58f4_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-h0a426d6_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.2-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.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/osx-arm64/libnghttp2-1.67.0-hc438710_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.53-hfab5511_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.3-h3bd63a1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h07bc746_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.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.18.0-cpu_hf321e49_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtensorflow_framework-2.18.0-cpu_h2398287_1.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.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h5748b74_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.8-py312h1f38498_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.8-py312h605b88b_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/osx-arm64/ml_dtypes-0.4.0-py312hcd31e36_2.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/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py312h85ea64e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/numpydoc-1.10.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hbfb3c88_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_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/osx-arm64/optree-0.18.0-py312h84eede6_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pandas-2.3.3-py312h5978115_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/patsy-1.0.2-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.0.0-py312h95c711c_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-5.28.3-py312hd8f9ff3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.1.3-py312h37e1c23_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/noarch/pybtex-0.25.1-pyhd8ed1ab_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.16.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.2.5-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/osx-arm64/python-3.12.12-h18782d2_1_cpython.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-2025.3-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-2025.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py312h5748b74_0.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-2024.07.02-h6589ca4_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.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/osx-arm64/scikit-learn-1.8.0-np2py312he7bfc6a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.16.3-py312h39258fd_2.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-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_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/noarch/snowballstemmer-3.0.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.1-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.6.1-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.20.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.6.5-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/osx-arm64/statsmodels-0.14.6-py312ha11c99a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tensorboard-2.18.0-pyhd8ed1ab_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.18.0-cpu_py312hb6b62e0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-base-2.18.0-cpu_py312h81ab8d0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-estimator-2.18.0-cpu_py312h5e86b3d_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.2.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/osx-arm64/tk-8.6.13-h892fb3f_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.4-py312h4409184_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-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.0-py312h4409184_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.0.1-py312h4409184_1.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/noarch/zipp-3.23.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.2-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/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - 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_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py311h03d9500_1.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.3.1-pyh8f84b5b_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.20.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.1-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-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/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.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.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-hf4e2dac_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-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.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mypy_extensions-1.1.0-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py311h2e04523_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.5.1-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.14-hd63d673_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py311h3778330_0.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.8.0-np2py311ha15b03d_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.3-py311hbe70eeb_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/linux-64/tk-8.6.13-noxft_ha0e22de_103.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-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py311hdf67eae_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.35.4-pyhd8ed1ab_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 - pypi: ./ osx-64: - 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_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_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/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.20.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.1-h14c5de8_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-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/osx-64/libblas-3.11.0-5_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-21.1.8-h3d58e20_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.3-heffb93a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-5_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.51.1-hd09e2f1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_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/osx-64/ncurses-6.5-h0622a9a_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.3.5-py311hf157cb9_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.5.1-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.11.14-h74c2667_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py311he13f9b5_0.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.8.0-np2py311hd641367_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.16.3-py311hd77d3c2_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-64/tk-8.6.13-hf689a15_3.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-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ukkonen-1.0.1-py311hd4d69bb_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.35.4-pyhd8ed1ab_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/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_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_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/noarch/cfgv-3.5.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/distlib-0.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.20.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-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/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.3-haf25636_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_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/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py311h8685306_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.5.1-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.14-h18782d2_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py311ha9b3269_0.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.8.0-np2py311hece8d6f_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.16.3-py311ha71c161_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-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/osx-arm64/tk-8.6.13-h892fb3f_3.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-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ukkonen-1.0.1-py311h57a9ea7_6.conda - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.35.4-pyhd8ed1ab_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/win-64/black-23.3.0-py311h1ea47a8_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py311h3485c13_1.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.3.1-pyha7b4d00_1.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.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/filelock-3.20.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.1-h637d24d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-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/win-64/libblas-3.11.0-5_hf2e6a31_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-5_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h4379cf1_1003.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-5_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.1-hf5d6505_1.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.1-h3cfd58e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-h779ef1b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-21.1.8-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.0-hac47afa_454.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/nodeenv-1.10.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.3.5-py311h80b3fa1_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-25.0-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.5.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.5.1-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.11.14-h0159041_2_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py311h3f79411_0.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.8.0-np2py311hd01f973_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.16.3-py311h9c22a71_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/sklearn-compat-0.1.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-hd094cb3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.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-h8577fbf_0.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.0.1-py311h3fd045d_6.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h2b53caa_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_33.conda - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.35.4-pyhd8ed1ab_0.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/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.1-h33c6efd_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.3-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-hf4e2dac_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h934c35e_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-h5347b49_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py314h2b28147_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_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.2-h32b2ec7_100_cp314.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-2025.3-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-2025.2-pyhd8ed1ab_0.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.8.0-np2py314hf09ca88_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.3-py314hf07bd8e_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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.5-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/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: ./ osx-64: - 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_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/icu-78.1-h14c5de8_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-5_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-21.1.8-h3d58e20_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.3-heffb93a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-5_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-h6e16a3a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.51.1-hd09e2f1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.3.5-py314hf08249b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_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.2-hf88997e_100_cp314.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-2025.3-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-2025.2-pyhd8ed1ab_0.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.8.0-np2py314h17d75c0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.16.3-py314hbb40827_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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.5-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/osx-64/tk-8.6.13-hf689a15_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-h8577fbf_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/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-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/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.3-haf25636_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h5505292_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py314h5b5928d_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_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.2-h40d2674_100_cp314.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-2025.3-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-2025.2-pyhd8ed1ab_0.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.8.0-np2py314h3d7c909_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.16.3-py314h725efaa_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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.5-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/osx-arm64/tk-8.6.13-h892fb3f_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-h8577fbf_0.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/win-64/bzip2-1.0.8-h0ad9c76_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.1-h637d24d_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/joblib-1.5.3-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libblas-3.11.0-5_hf2e6a31_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libcblas-3.11.0-5_h2a3cdd5_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h4379cf1_1003.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-5_hf9ab0e9_mkl.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-h2466b09_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.1-hf5d6505_1.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.1-h3cfd58e_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-h779ef1b_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-21.1.8-h4fa8253_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.0-hac47afa_454.conda - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.3.5-py314hf08e5d2_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_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.2-h7c1dbca_0_cp314t.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-2025.3-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-2025.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.8.0-np2py314hb10d3ac_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.16.3-py314h9c5632b_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tbb-2022.3.0-hd094cb3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/threadpoolctl-3.6.0-pyhecae5ae_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025c-h8577fbf_0.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.3-h2b53caa_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_33.conda - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - pypi: ./ 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/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 - 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.3.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/linux-64/backports.zstd-1.2.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_8.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/noarch/ca-certificates-2025.11.12-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-2025.11.12-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-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/linux-64/coverage-7.13.0-py312h8a5da7c_0.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/linux-64/flatbuffers-25.2.10-hb7832b1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_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/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.73.1-py312h6f3464c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.15.1-nompi_py312ha4f8f14_101.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h1b119a7_104.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/linux-64/icu-75.1-he02047a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.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/linux-64/keyutils-1.6.3-hb9d3cd8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libabseil-20250512.1-cxx17_hba17884_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.4-h3f801dc_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.17.0-h4e3cde8_1.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.7.3-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libgrpc-1.73.1-h3288cfb_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libjpeg-turbo-3.1.2-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.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/linux-64/libnghttp2-1.67.0-had1ee68_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.30-pthreads_h94d23a6_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.53-h421ea60_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libprotobuf-6.31.1-h49aed37_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libre2-11-2025.11.05-h7b12aa8_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-h0c1763c_1.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_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_16.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_cc-2.19.1-cpu_hf7abd0a_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_framework-2.19.1-cpu_h12ecb4a_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.3-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.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ml_dtypes-0.5.4-np2py312h0f77346_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py312h33ff503_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_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/linux-64/optree-0.18.0-py312hd9148b4_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/linux-64/protobuf-6.31.1-py312hb8af0ac_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-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/linux-64/python-3.12.12-hd63d673_1_cpython.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/linux-64/re2-2025.11.05-h5301d42_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.8.0-np2py312h3226591_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.3-py312h54fa4ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_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/noarch/tensorboard-2.19.0-pyhd8ed1ab_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_py312h69ecde4_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-base-2.19.1-cpu_py312h0afb428_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-estimator-2.19.1-cpu_py312h6e6b5e2_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.2.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/linux-64/tk-8.6.13-noxft_ha0e22de_103.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.3.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/tzdata-2025c-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.0.1-py312h4c3975b_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: ./ osx-64: - conda: https://conda.anaconda.org/conda-forge/osx-64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.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/osx-64/backports.zstd-1.2.0-py312hcb931b7_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_8.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/noarch/ca-certificates-2025.11.12-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-2025.11.12-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-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/osx-64/coverage-7.13.0-py312hacf3034_0.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/osx-64/flatbuffers-24.12.23-hd70532e_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_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/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/grpcio-1.67.1-py312h145213c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.15.1-nompi_py312hcf08926_101.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.6-nompi_hc1508a4_104.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/osx-64/icu-75.1-h120a0e1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.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/osx-64/krb5-1.21.3-h37d8d59_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.4-ha6bc127_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libblas-3.11.0-5_he492b99_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.17.0-h7dd4100_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-21.1.8-h3d58e20_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.7.3-heffb93a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.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.2-h8616949_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblapack-3.11.0-5_h859234e_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.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/osx-64/libnghttp2-1.67.0-h3338091_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libopenblas-0.3.30-openmp_h6006d49_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.53-h380d223_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.51.1-hb99441e_1.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.1-hd23fc13_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py312hacf3034_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_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/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.3.5-py312ha3982b3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_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/osx-64/optree-0.18.0-py312hd099df3_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/osx-64/protobuf-5.28.3-py312haafddd8_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-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/osx-64/python-3.12.12-h74c2667_1_cpython.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/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/noarch/requests-2.32.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.8.0-np2py312hc921ccd_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.16.3-py312h79cf0a0_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_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/noarch/tensorboard-2.18.0-pyhd8ed1ab_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/noarch/termcolor-3.2.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/osx-64/tk-8.6.13-hf689a15_3.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.3.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/tzdata-2025c-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.0.1-py312h80b0991_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_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/osx-arm64/_openmp_mutex-4.5-7_kmp_llvm.conda - conda: https://conda.anaconda.org/conda-forge/noarch/absl-py-2.3.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/osx-arm64/backports.zstd-1.2.0-py312h84d6f5f_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_8.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/noarch/ca-certificates-2025.11.12-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-2025.11.12-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.4-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/osx-arm64/coverage-7.13.0-py312h5748b74_0.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/osx-arm64/flatbuffers-24.12.23-h28594ff_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/gast-0.7.0-pyhd8ed1ab_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/noarch/google-pasta-0.2.0-pyhd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/grpcio-1.67.1-py312he4e58e5_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/h2-4.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.15.1-nompi_py312h4eecd6b_101.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.6-nompi_hd3baa01_104.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/osx-arm64/icu-75.1-hfee45f7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.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/osx-arm64/krb5-1.21.3-h237132a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libabseil-20240722.0-cxx17_h07bc746_4.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.4-h51d1e36_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libblas-3.11.0-5_h51639a9_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.17.0-hdece5d2_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_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.7.3-haf25636_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgrpc-1.67.1-h0a426d6_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.2-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.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/osx-arm64/libnghttp2-1.67.0-hc438710_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libopenblas-0.3.30-openmp_ha158390_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.53-hfab5511_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libprotobuf-5.28.3-h3bd63a1_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libre2-11-2024.07.02-h07bc746_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.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.18.0-cpu_hf321e49_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libtensorflow_framework-2.18.0-cpu_h2398287_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h5748b74_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mdurl-0.1.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ml_dtypes-0.4.0-py312hcd31e36_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/namex-0.1.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py312h85ea64e_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_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/osx-arm64/optree-0.18.0-py312h84eede6_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/osx-arm64/protobuf-5.28.3-py312hd8f9ff3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-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/osx-arm64/python-3.12.12-h18782d2_1_cpython.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/osx-arm64/re2-2024.07.02-h6589ca4_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.32.5-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rich-14.2.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.8.0-np2py312he7bfc6a_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.16.3-py312h39258fd_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_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.5-pyhd8ed1ab_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/noarch/tensorboard-2.18.0-pyhd8ed1ab_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.18.0-cpu_py312hb6b62e0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-base-2.18.0-cpu_py312h81ab8d0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-estimator-2.18.0-cpu_py312h5e86b3d_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.2.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/osx-arm64/tk-8.6.13-h892fb3f_3.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.3.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/tzdata-2025c-h8577fbf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/urllib3-2.6.2-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/werkzeug-3.1.4-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.0.1-py312h4409184_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_1.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/_libgcc_mutex-0.1-conda_forge.tar.bz2 sha256: fe51de6107f9edc7aa4f786a70f4a883943bc9d39b3bb7307c04c41410990726 md5: d7c89558ba9fa0495403155b64376d81 license: None purls: [] size: 2562 timestamp: 1578324546067 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 build_number: 16 sha256: fbe2c5e56a653bebb982eda4876a9178aedfc2b545f25d0ce9c4c0b508253d22 md5: 73aaf86a425cc6e73fcf236a5a46396d depends: - _libgcc_mutex 0.1 conda_forge - libgomp >=7.5.0 constrains: - openmp_impl 9999 license: BSD-3-Clause license_family: BSD purls: [] size: 23621 timestamp: 1650670423406 - 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-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/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.3.1-pyhd8ed1ab_0.conda sha256: ec7a804be25350c310be7e0fffdbf4006fd22a650bf316513bdd71cb922944bf md5: 7d4f1ddc43d323c916b2c744835eb093 depends: - python >=3.9 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/absl-py?source=hash-mapping size: 109408 timestamp: 1751547635237 - 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/linux-64/alsa-lib-1.2.15.1-hb03c661_0.conda sha256: 224f1a55a9ba7e877bce980f14fc3e3c0f0fb6d3cbf3c5f1a8f5dd8391ce8bba md5: bba37fb066adb90e1d876dff0fd5d09d depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: LGPL-2.1-or-later license_family: GPL purls: [] size: 585491 timestamp: 1766155792553 - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.12.0-pyhcf101f3_0.conda sha256: 830fc81970cd9d19869909b9b16d241f4d557e4f201a1030aa6ed87c6aa8b930 md5: 9958d4a1ee7e9c768fe8f4fb51bd07ea depends: - exceptiongroup >=1.0.2 - idna >=2.8 - python >=3.10 - typing_extensions >=4.5 - python constrains: - trio >=0.32.0 - uvloop >=0.21 license: MIT license_family: MIT purls: - pkg:pypi/anyio?source=hash-mapping size: 144702 timestamp: 1764375386926 - 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/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/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-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/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.0.5-pyh29332c3_0.conda sha256: 3b7233041e462d9eeb93ea1dfe7b18aca9c358832517072054bb8761df0c324b md5: d9d0f99095a9bb7e3641bca8c6ad2ac7 depends: - python >=3.9 - typing_extensions >=4.0.0 - python license: MIT license_family: MIT purls: - pkg:pypi/async-lru?source=hash-mapping size: 17335 timestamp: 1742153708859 - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-25.4.0-pyhcf101f3_1.conda sha256: c13d5e42d187b1d0255f591b7ce91201d4ed8a5370f0d986707a802c20c9d32f md5: 537296d57ea995666c68c821b00e360b depends: - python >=3.10 - python license: MIT license_family: MIT purls: - pkg:pypi/attrs?source=compressed-mapping size: 64759 timestamp: 1764875182184 - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.17.0-pyhd8ed1ab_0.conda sha256: 1c656a35800b7f57f7371605bc6507c8d3ad60fbaaec65876fce7f73df1fc8ac md5: 0a01c169f0ab0f91b26e77a3301fbfe4 depends: - python >=3.9 - pytz >=2015.7 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/babel?source=hash-mapping size: 6938256 timestamp: 1738490268466 - 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-pyhd8ed1ab_1.conda sha256: a0f41db6d7580cec3c850e5d1b82cb03197dd49a3179b1cee59c62cd2c761b36 md5: df837d654933488220b454c6a3b0fad6 depends: - backports - python >=3.9 license: MIT license_family: MIT purls: - pkg:pypi/backports-tarfile?source=hash-mapping size: 32786 timestamp: 1733325872620 - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.2.0-py310h69bd2ac_0.conda sha256: 3c8e4259c59cee15de96544247a57d5b057b503383dfcd9f9054f672a5f8da1a md5: 22be0d7b12ae6ff92aa1fd73272c65d7 depends: - python - __glibc >=2.17,<3.0.a0 - libgcc >=14 - zstd >=1.5.7,<1.6.0a0 - python_abi 3.10.* *_cp310 license: BSD-3-Clause AND MIT AND EPL-2.0 purls: - pkg:pypi/backports-zstd?source=hash-mapping size: 191645 timestamp: 1765057663888 - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.2.0-py311h6b1f9c4_0.conda sha256: 922cf0e26929aa34a5ce3e6fbbb6d960be35a146a85a5d8f5e7e16c09e660827 md5: 596b9cc36b7af0640825b399e6b11ccc depends: - python - __glibc >=2.17,<3.0.a0 - libgcc >=14 - 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: 245173 timestamp: 1765057678423 - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.2.0-py312h90b7ffd_0.conda sha256: c0e375fd6a67a39b3d855d1cb53c2017faf436e745a780ca2bbb527f4cac25fd md5: 9fc7e65938c0e4b2658631b8bfd380e8 depends: - python - libgcc >=14 - __glibc >=2.17,<3.0.a0 - 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: 238087 timestamp: 1765057663263 - conda: https://conda.anaconda.org/conda-forge/osx-64/backports.zstd-1.2.0-py310h6194938_0.conda sha256: 9ebbc9525a0333c1d9947a1c131e1dec7400b3c5e6cdc913675418f9e84f5653 md5: 463da39cd9d09eee87fd8ec4b3283164 depends: - python - __osx >=10.13 - zstd >=1.5.7,<1.6.0a0 - python_abi 3.10.* *_cp310 license: BSD-3-Clause AND MIT AND EPL-2.0 purls: - pkg:pypi/backports-zstd?source=hash-mapping size: 191156 timestamp: 1765057701139 - conda: https://conda.anaconda.org/conda-forge/osx-64/backports.zstd-1.2.0-py311hd694d0d_0.conda sha256: 3d104f88728b869a0065e8b6119e643baa94eb25327ac1fe21cc9e4ec2bd4546 md5: 313b462b337ea9c0da9001227683699d depends: - python - __osx >=10.13 - 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: 243864 timestamp: 1765057688172 - conda: https://conda.anaconda.org/conda-forge/osx-64/backports.zstd-1.2.0-py312hcb931b7_0.conda sha256: 5fe811e1c582febda13afab3cf06badda62157bd851cdb6f67201da827fdbdde md5: 5b8b4a50dae13f2d8412388ae7fa996b depends: - python - __osx >=10.13 - 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: 238407 timestamp: 1765057706612 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.2.0-py310hdc7f11d_0.conda sha256: 57cc572c2c8d4410f1ee70e89ba9b7267c0295111991c6ab8bb0ffdf1547986b md5: 7366452a54e3bc6632fa461458aa5f54 depends: - python - python 3.10.* *_cpython - __osx >=11.0 - zstd >=1.5.7,<1.6.0a0 - python_abi 3.10.* *_cp310 license: BSD-3-Clause AND MIT AND EPL-2.0 purls: - pkg:pypi/backports-zstd?source=hash-mapping size: 194040 timestamp: 1765057757634 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.2.0-py311h97d6d38_0.conda sha256: 443f9799e1135919bde3bf1d1c60c73aa0518fc7beb031389437dfcddb8f9b0d md5: 9b46dfb4ecef344e7031c82b6d37db49 depends: - python - __osx >=11.0 - python 3.11.* *_cpython - 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: 247150 timestamp: 1765057758144 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.2.0-py312h84d6f5f_0.conda sha256: 833370729199ef55f3f9efd024e28bba87fcd8b5c397d8afecefde63851e6997 md5: c0ca697637ef6cf0ac768a50964e4af6 depends: - python - __osx >=11.0 - python 3.12.* *_cpython - 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: 241337 timestamp: 1765057702057 - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.3-pyha770c72_0.conda sha256: bf1e71c3c0a5b024e44ff928225a0874fc3c3356ec1a0b6fe719108e6d1288f6 md5: 5267bef8efea4127aacd1f4e1f149b6e depends: - python >=3.10 - soupsieve >=1.2 - typing-extensions license: MIT license_family: MIT purls: - pkg:pypi/beautifulsoup4?source=hash-mapping size: 90399 timestamp: 1764520638652 - 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/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-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/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/noarch/bleach-6.3.0-pyhcf101f3_0.conda sha256: e03ba1a2b93fe0383c57920a9dc6b4e0c2c7972a3f214d531ed3c21dc8f8c717 md5: b1a27250d70881943cca0dd6b4ba0956 depends: - python >=3.10 - webencodings - python constrains: - tinycss >=1.1.0,<1.5 license: Apache-2.0 AND MIT purls: - pkg:pypi/bleach?source=hash-mapping size: 141952 timestamp: 1763589981635 - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.3.0-h5f6438b_0.conda sha256: f85f6b2c7938d8c20c80ce5b7e6349fafbb49294641b5648273c5f892b150768 md5: 08a03378bc5293c6f97637323802f480 depends: - bleach ==6.3.0 pyhcf101f3_0 - tinycss2 license: Apache-2.0 AND MIT purls: [] size: 4386 timestamp: 1763589981639 - 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/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-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/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/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-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/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=compressed-mapping size: 368300 timestamp: 1764017300621 - 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-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 - pypi: https://files.pythonhosted.org/packages/cb/8c/2b30c12155ad8de0cf641d76a8b396a16d2c36bc6d50b621a62b7c4567c1/build-1.3.0-py3-none-any.whl name: build version: 1.3.0 sha256: 7145f0b5061ba90a1500d60bd1b13ca0a8a4cebdd0cc16ed8adf1c0e739f43b4 requires_dist: - packaging>=19.1 - 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' - uv>=0.1.18 ; extra == 'uv' - virtualenv>=20.11 ; python_full_version < '3.10' and extra == 'virtualenv' - 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.9' - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda sha256: c30daba32ddebbb7ded490f0e371eae90f51e72db620554089103b4a6934b0d5 md5: 51a19bba1b8ebfb60df25cde030b7ebc depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: bzip2-1.0.6 license_family: BSD purls: [] size: 260341 timestamp: 1757437258798 - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_8.conda sha256: 8f50b58efb29c710f3cecf2027a8d7325ba769ab10c746eff75cea3ac050b10c md5: 97c4b3bd8a90722104798175a1bdddbf depends: - __osx >=10.13 license: bzip2-1.0.6 license_family: BSD purls: [] size: 132607 timestamp: 1757437730085 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.conda sha256: b456200636bd5fecb2bec63f7e0985ad2097cf1b83d60ce0b6968dffa6d02aa1 md5: 58fd217444c2a5701a44244faf518206 depends: - __osx >=11.0 license: bzip2-1.0.6 license_family: BSD purls: [] size: 125061 timestamp: 1757437486465 - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda sha256: d882712855624641f48aa9dc3f5feea2ed6b4e6004585d3616386a18186fe692 md5: 1077e9333c41ff0be8edd1a5ec0ddace 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: 55977 timestamp: 1757437738856 - 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/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-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/noarch/ca-certificates-2025.11.12-h4c7d964_0.conda sha256: 686a13bd2d4024fc99a22c1e0e68a7356af3ed3304a8d3ff6bb56249ad4e82f0 md5: f98fb7db808b94bc1ec5b0e62f9f1069 depends: - __win license: ISC purls: [] size: 152827 timestamp: 1762967310929 - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.11.12-hbd8a1cb_0.conda sha256: b986ba796d42c9d3265602bc038f6f5264095702dd546c14bc684e60c385e773 md5: f0991f0f84902f6b6009b4d2350a83aa depends: - __unix license: ISC purls: [] size: 152432 timestamp: 1762967197890 - 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/linux-64/cairo-1.18.4-h3394656_0.conda sha256: 3bd6a391ad60e471de76c0e9db34986c4b5058587fbf2efa5a7f54645e28c2c7 md5: 09262e66b19567aff4f592fb53b28760 depends: - __glibc >=2.17,<3.0.a0 - fontconfig >=2.15.0,<3.0a0 - fonts-conda-ecosystem - freetype >=2.12.1,<3.0a0 - icu >=75.1,<76.0a0 - libexpat >=2.6.4,<3.0a0 - libgcc >=13 - libglib >=2.82.2,<3.0a0 - libpng >=1.6.47,<1.7.0a0 - libstdcxx >=13 - libxcb >=1.17.0,<2.0a0 - libzlib >=1.3.1,<2.0a0 - pixman >=0.44.2,<1.0a0 - xorg-libice >=1.1.2,<2.0a0 - xorg-libsm >=1.2.5,<2.0a0 - xorg-libx11 >=1.8.11,<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: 978114 timestamp: 1741554591855 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2025.11.12-pyhd8ed1ab_0.conda sha256: 083a2bdad892ccf02b352ecab38ee86c3e610ba9a4b11b073ea769d55a115d32 md5: 96a02a5c1a65470a7e4eedb644c872fd depends: - python >=3.10 license: ISC purls: - pkg:pypi/certifi?source=compressed-mapping size: 157131 timestamp: 1762976260320 - 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/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-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/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/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.4-pyhd8ed1ab_0.conda sha256: b32f8362e885f1b8417bac2b3da4db7323faa12d5db62b7fd6691c02d60d6f59 md5: a22d1fd9bf98827e280a02875d9a007a depends: - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/charset-normalizer?source=hash-mapping size: 50965 timestamp: 1760437331772 - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyh8f84b5b_1.conda sha256: 38cfe1ee75b21a8361c8824f5544c3866f303af1762693a178266d7f198e8715 md5: ea8a6c3256897cc31263de9f455e25d9 depends: - python >=3.10 - __unix - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/click?source=hash-mapping size: 97676 timestamp: 1764518652276 - conda: https://conda.anaconda.org/conda-forge/noarch/click-8.3.1-pyha7b4d00_1.conda sha256: c3bc9a49930fa1c3383a1485948b914823290efac859a2587ca57a270a652e08 md5: 6cd3ccc98bacfcc92b2bd7f236f01a7e depends: - python >=3.10 - colorama - __win - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/click?source=hash-mapping size: 96620 timestamp: 1764518654675 - 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/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-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/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/linux-64/contourpy-1.3.3-py311hdf67eae_3.conda sha256: fde69b5ab61225daca6c2f05a93f94c06af93003e4f871d61470df5c4cf9587b md5: c4e2f4d5193e55a70bb67a2aa07006ae depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - numpy >=1.25 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/contourpy?source=hash-mapping size: 296142 timestamp: 1762525422359 - conda: https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.3.3-py312hd9148b4_3.conda sha256: e173ea96fb135b233c7f57c35c0d07f7adc50ebacf814550f3daf1c7ba2ed51e md5: 86cf7a7d861b79d38e3f0e5097e4965b depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - numpy >=1.25 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/contourpy?source=compressed-mapping size: 295243 timestamp: 1762525427240 - conda: https://conda.anaconda.org/conda-forge/osx-64/contourpy-1.3.3-py311haec20ae_3.conda sha256: 2e575eec7b5b00231a8c78012a906f03aab47ab67a90a5dc715e14edf6ceb334 md5: 490cd30a30f32edbe8e642df7db8bbc2 depends: - __osx >=10.13 - libcxx >=19 - numpy >=1.25 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/contourpy?source=hash-mapping size: 268549 timestamp: 1762525619740 - conda: https://conda.anaconda.org/conda-forge/osx-64/contourpy-1.3.3-py312hd099df3_3.conda sha256: a317f6d5c8d574656665907fa5bf9ca1017ef132a988c6d126f2121d7817e4ec md5: 83036bb23aad87b7256d7ae13d1fdb89 depends: - __osx >=10.13 - libcxx >=19 - numpy >=1.25 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/contourpy?source=hash-mapping size: 269184 timestamp: 1762525977233 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py311h5a5e7c7_3.conda sha256: ec3b13298a2370ea01b3d51131a31ce5ec6ae868df4b7cf7b63583cbc88b9f40 md5: ee7f010da41a954ef6910fd31798305c depends: - __osx >=11.0 - libcxx >=19 - numpy >=1.25 - 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/contourpy?source=hash-mapping size: 259447 timestamp: 1762526287997 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/contourpy-1.3.3-py312h84eede6_3.conda sha256: ee6a2497f2d9aff6ec53b6998a37c546916b79118e386bb90a7cb1f389d35197 md5: e3fbe173dea7137a6d766cbacf697df2 depends: - __osx >=11.0 - libcxx >=19 - numpy >=1.25 - 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/contourpy?source=hash-mapping size: 258388 timestamp: 1762525877844 - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.13.0-py310h3406613_0.conda sha256: ff91a266d7f0c5d2492f2a64f8844b269c9af35a893461669556d9a223e77253 md5: 87a9020d32817a12115e5ddfce4693ac 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=hash-mapping size: 309961 timestamp: 1765203313211 - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.13.0-py311h3778330_0.conda sha256: e3d66a16a01d1729374ede4191736d99537b2115c7002a3abc65b2f29bcd1a68 md5: 95294f5480dae437d7c15d40238c9b1c 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: 394002 timestamp: 1765203452031 - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.13.0-py312h8a5da7c_0.conda sha256: 1624eaffb5ff622a48712114faf328b44e11d800dc85e891ee2412ffd38bd18b md5: da396284d1f498e20b4377478dbb830c 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: 383584 timestamp: 1765203584575 - conda: https://conda.anaconda.org/conda-forge/linux-64/coverage-7.13.0-py314h67df5f8_0.conda sha256: 9d471408605b406a77f3f3ddf8105b7a7cce64a06c45d1a26b2ded2c6bbca703 md5: 0260930722e378b677c67e1ac29668dd 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: 408240 timestamp: 1765203383473 - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.13.0-py310hd951482_0.conda sha256: 4a105dcd331777d22c087ee3bc9cd9cfa46819f6595a7ae1d79e923fda58e8ff md5: dfd55761f4c349babf12ee24bd2c3f4b depends: - __osx >=10.13 - 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: 308478 timestamp: 1765204111318 - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.13.0-py311he13f9b5_0.conda sha256: 0d9f2d0a48e0d3a471451ce0f38f6c07fc083cff6cd6db5c9cfe7b7032065e7e md5: 89194385f87374a1665bc0e73e794187 depends: - __osx >=10.13 - 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: 392068 timestamp: 1765203655402 - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.13.0-py312hacf3034_0.conda sha256: 1759766e4b834f3f3635866042124bc429cb6a55ebcc29c5c91a54241888e137 md5: 2eb53877ba2ec4cb8fc34bc77593ee68 depends: - __osx >=10.13 - 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: 382018 timestamp: 1765203683605 - conda: https://conda.anaconda.org/conda-forge/osx-64/coverage-7.13.0-py314hb9c7d66_0.conda sha256: d71651cc5e6edba7f8e63ee7bb05efc19471af950b80196a02e83319e218c2c7 md5: 373a014ff4c3a20ec2581c304e7bc455 depends: - __osx >=10.13 - 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: 406689 timestamp: 1765203511474 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.13.0-py310hf4fd40f_0.conda sha256: bbcb5daf9aeaa165760b5656727155cdeceb2f97a77ea5d87d5d9c292defd862 md5: 74f555f175f0efdfd03d831acfb3079a 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=hash-mapping size: 309516 timestamp: 1765203516048 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.13.0-py311ha9b3269_0.conda sha256: 6f5eda53e40192f6aed58b6767c82cdf5c1475710d403f1e285c62da3ffa254e md5: 7b7329b38fa1855f4099f59edf8debe1 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: 393519 timestamp: 1765203739121 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.13.0-py312h5748b74_0.conda sha256: fc898ebd3e8faeb56314ec4b7996c7d28cb37330a5e70e122b86f6152c8ea4f8 md5: aa4e0f5ae7200c4add1b1c9d4a586eba 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: 383059 timestamp: 1765203714727 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/coverage-7.13.0-py314hb7e19f3_0.conda sha256: 5ad1e05c9a1c155fad7731fbc9b07ffcd7ec7b5dae029144256f968f630a7e90 md5: 0827ceba1914911b963011540c214f96 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: 406412 timestamp: 1765203549502 - conda: https://conda.anaconda.org/conda-forge/win-64/coverage-7.13.0-py310hdb0e946_0.conda sha256: 155f721fa7b2b8c3eb444aaafe0489e3d893592e9735c748f0ff1f73dfbb9ebe md5: 027ac6d48fe5660f791c96334cb6d714 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: 334918 timestamp: 1765203527825 - conda: https://conda.anaconda.org/conda-forge/win-64/coverage-7.13.0-py311h3f79411_0.conda sha256: dcd5bce421243b57edcd8855d59a6ddb43f4137795ebeb5cfde72600cc8ac36d md5: 8424783b620f08ae0de5321c8ab02406 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: 418067 timestamp: 1765203452169 - conda: https://conda.anaconda.org/conda-forge/win-64/coverage-7.13.0-py312h05f76fc_0.conda sha256: 3ed2f6d5b2b988d9faeebd68c68411e74b6b0dd4d3d8f8aa25368c9bde142367 md5: 54a1ead847baeb406001161398657cd1 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: 408955 timestamp: 1765203501551 - conda: https://conda.anaconda.org/conda-forge/win-64/coverage-7.13.0-py314h2359020_0.conda sha256: 946decd7468b4b5a22b32dbec999f4516da92a706535c5ef40974b5091cc48ed md5: 066dbd52c0218c84ba03e3b6b4a544e2 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: 433902 timestamp: 1765203441253 - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.11.14-py311hd8ed1ab_2.conda noarch: generic sha256: c871fe68dcc6b79b322e4fcf9f2b131162094c32a68d48c87aa8582995948a01 md5: 43ed151bed1a0eb7181d305fed7cf051 depends: - python >=3.11,<3.12.0a0 - python_abi * *_cp311 license: Python-2.0 purls: [] size: 47257 timestamp: 1761172995774 - conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-46.0.3-py311h2005dd1_1.conda sha256: 3469975e4e836ae8c40b54b4a9c514b15565450b86503244c1d11ff4c9a91932 md5: 4621dd1f3c38b24472a5e7ea4b3d8f6c depends: - __glibc >=2.17,<3.0.a0 - cffi >=1.14 - 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 AND BSD-3-Clause AND PSF-2.0 AND MIT license_family: BSD purls: - pkg:pypi/cryptography?source=compressed-mapping size: 1718910 timestamp: 1764805458730 - 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=compressed-mapping size: 14778 timestamp: 1764466758386 - conda: https://conda.anaconda.org/conda-forge/linux-64/cyrus-sasl-2.1.28-hd9c7081_0.conda sha256: ee09ad7610c12c7008262d713416d0b58bf365bc38584dce48950025850bdf3f md5: cae723309a49399d2949362f4ab5c9e4 depends: - __glibc >=2.17,<3.0.a0 - krb5 >=1.21.3,<1.22.0a0 - libgcc >=13 - libntlm >=1.8,<2.0a0 - libstdcxx >=13 - libxcrypt >=4.4.36 - openssl >=3.5.0,<4.0a0 license: BSD-3-Clause-Attribution license_family: BSD purls: [] size: 209774 timestamp: 1750239039316 - 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.18-py311hc665b79_0.conda sha256: ba68335de570bc24f9bba813b8608a2822e619f4741efce194d073b48dfddcfc md5: 0ef6a6d6c08ff139453694184efcd3dc depends: - python - __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - libgcc >=14 - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/debugpy?source=hash-mapping size: 2732796 timestamp: 1765704055695 - conda: https://conda.anaconda.org/conda-forge/osx-64/debugpy-1.8.19-py311hd4eb7a1_0.conda sha256: d8a951e8f7ef38278b88cec97693fb3f772a2876e3ff18f90829dba922fc4c70 md5: 31ec27cf2254fe4c1299559294b1bb57 depends: - python - libcxx >=19 - __osx >=11.0 - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/debugpy?source=hash-mapping size: 2669754 timestamp: 1765840868762 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.19-py311hc58e375_0.conda sha256: d9f9492afd950f4cf2cd21220179afd6088a35fa7f5f4fe8e73d4cf79f986709 md5: afe5ca4a03b551d7748da1a559795e01 depends: - python - python 3.11.* *_cpython - libcxx >=19 - __osx >=11.0 - python_abi 3.11.* *_cp311 license: MIT license_family: MIT purls: - pkg:pypi/debugpy?source=hash-mapping size: 2669167 timestamp: 1765840838467 - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda sha256: c17c6b9937c08ad63cb20a26f403a3234088e57d4455600974a0ce865cb14017 md5: 9ce473d1d1be1cc3810856a48b3fab32 depends: - python >=3.9 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/decorator?source=hash-mapping size: 14129 timestamp: 1740385067843 - 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.0-pyhd8ed1ab_0.conda sha256: 6d977f0b2fc24fee21a9554389ab83070db341af6d6f09285360b2e09ef8b26e md5: 003b8ba0a94e2f1e117d0bd46aebc901 depends: - python >=3.9 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/distlib?source=hash-mapping size: 275642 timestamp: 1752823081585 - 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/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/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=compressed-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.20.1-pyhd8ed1ab_0.conda sha256: 8028582d956ab76424f6845fa1bdf5cb3e629477dd44157ca30d45e06d8a9c7c md5: 81a651287d3000eb12f0860ade0a1b41 depends: - python >=3.10 license: Unlicense purls: - pkg:pypi/filelock?source=hash-mapping size: 18609 timestamp: 1765846639623 - 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.2.10-hb7832b1_0.conda sha256: 0e58114d0e16bc89b94ef9068558e304d2eccae5dbaa55b955274ea60da81dfd md5: 279ba9719d1afc81538d8260f31e42a0 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - libstdcxx >=13 license: Apache-2.0 license_family: APACHE purls: [] size: 1539958 timestamp: 1747130572350 - 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-arm64/flatbuffers-24.12.23-h28594ff_0.conda sha256: 3eb3d471cda99c6b1de152078f784cac2f498620af1eeb2cc0f3407987810c55 md5: b7b7a7a8a69ec7f3a7fccfbdab2352c0 depends: - __osx >=11.0 - libcxx >=18 license: Apache-2.0 license_family: APACHE purls: [] size: 1281858 timestamp: 1736587423115 - 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/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/linux-64/fontconfig-2.15.0-h7e30c49_1.conda sha256: 7093aa19d6df5ccb6ca50329ef8510c6acb6b0d8001191909397368b65b02113 md5: 8f5b0b297b59e1ac160ad4beec99dbee depends: - __glibc >=2.17,<3.0.a0 - freetype >=2.12.1,<3.0a0 - libexpat >=2.6.3,<3.0a0 - libgcc >=13 - libuuid >=2.38.1,<3.0a0 - libzlib >=1.3.1,<2.0a0 license: MIT license_family: MIT purls: [] size: 265599 timestamp: 1730283881107 - 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/linux-64/fonttools-4.61.1-py311h3778330_0.conda sha256: 8f7eb3a66854785ae1867386f6c8d19791fac7a4d41b335d3117a6e896a154f1 md5: 2e8ccb31890a95d5cd90d74a11c7d5e2 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: 3004920 timestamp: 1765633180642 - conda: https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.61.1-py312h8a5da7c_0.conda sha256: c73cd238e0f6b2183c5168b64aa35a7eb66bb145192a9b26bb9041a4152844a3 md5: 3bf8fb959dc598c67dac0430b4aff57a 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=hash-mapping size: 2932702 timestamp: 1765632761555 - conda: https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.61.1-py311he13f9b5_0.conda sha256: 85bf9b4e506d6627c97ea35b884b7c322eb901e0a881499c287643e5125b76ec md5: 16170c1aade6f6b935c585ccedf49f58 depends: - __osx >=10.13 - 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: 2944202 timestamp: 1765632982133 - conda: https://conda.anaconda.org/conda-forge/osx-64/fonttools-4.61.1-py312hacf3034_0.conda sha256: f01c62330a693e05b6938ffbf3b930197c4e9ba73659c36bb8ee74c799ec840d md5: 277eb1146255b637cac845cc6bc8fb6b depends: - __osx >=10.13 - 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: 2879894 timestamp: 1765632981375 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.61.1-py311ha9b3269_0.conda sha256: dbde6d3ea8058747fb7800aafbb5a0bf650f6f25e4b2ee53206eedadbb73054e md5: 1cd0bcb3eee550d601c84b28b7df1ac3 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: 2900413 timestamp: 1765632975100 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/fonttools-4.61.1-py312h5748b74_0.conda sha256: d87752e84621f90e9350262200fef55f054472f7779323f51717b557208e2a16 md5: c14625bf00c41c00cea174f459287fc4 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: 2859891 timestamp: 1765633073562 - 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/linux-64/freetype-2.14.1-ha770c72_0.conda sha256: bf8e4dffe46f7d25dc06f31038cacb01672c47b9f45201f065b0f4d00ab0a83e md5: 4afc585cd97ba8a23809406cd8a9eda8 depends: - libfreetype 2.14.1 ha770c72_0 - libfreetype6 2.14.1 h73754d4_0 license: GPL-2.0-only OR FTL purls: [] size: 173114 timestamp: 1757945422243 - conda: https://conda.anaconda.org/conda-forge/osx-64/freetype-2.14.1-h694c41f_0.conda sha256: 9f8282510db291496e89618fc66a58a1124fe7a6276fbd57ed18c602ce2576e9 md5: ca641fdf8b7803f4b7212b6d66375930 depends: - libfreetype 2.14.1 h694c41f_0 - libfreetype6 2.14.1 h6912278_0 license: GPL-2.0-only OR FTL purls: [] size: 173969 timestamp: 1757945973505 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/freetype-2.14.1-hce30654_0.conda sha256: 14427aecd72e973a73d5f9dfd0e40b6bc3791d253de09b7bf233f6a9a190fd17 md5: 1ec9a1ee7a2c9339774ad9bb6fe6caec depends: - libfreetype 2.14.1 hce30654_0 - libfreetype6 2.14.1 h6da58f4_0 license: GPL-2.0-only OR FTL purls: [] size: 173399 timestamp: 1757947175403 - 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/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/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-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/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/linux-64/graphite2-1.3.14-hecca717_2.conda sha256: 25ba37da5c39697a77fce2c9a15e48cf0a84f1464ad2aafbe53d8357a9f6cc8c md5: 2cd94587f3a401ae05e03a6caf09539d depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 license: LGPL-2.0-or-later license_family: LGPL purls: [] size: 99596 timestamp: 1755102025473 - 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.73.1-py311h46a3a17_1.conda sha256: f40f55b62c88d8478f580b1e00f8853dcda47f773326d0eced6e2943b996de54 md5: 628c7da4f636ac2aa09207f9357be1bc depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libgrpc 1.73.1 h3288cfb_1 - libstdcxx >=14 - 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: 894164 timestamp: 1761058803529 - conda: https://conda.anaconda.org/conda-forge/linux-64/grpcio-1.73.1-py312h6f3464c_1.conda sha256: 9b6ef222599d63ca23a9e292c35f454756e321cce52af9f5142303230f0c2762 md5: dca50c100d8d67882ada32756810372f depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libgrpc 1.73.1 h3288cfb_1 - libstdcxx >=14 - 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: 885879 timestamp: 1761058885541 - 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-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.67.1-py311ha413f7a_2.conda sha256: 5f2a2bb36c0919661e5c6af022bb2af2d60e511bcda1fffb9acfbf47fcd5fa6b md5: 8175bf681a9fbc4f7ebfc1a23a02db3a depends: - __osx >=11.0 - libcxx >=18 - libgrpc 1.67.1 h0a426d6_2 - libzlib >=1.3.1,<2.0a0 - 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/grpcio?source=hash-mapping size: 824223 timestamp: 1740786731918 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/grpcio-1.67.1-py312he4e58e5_2.conda sha256: f7ffc67cae79114a63f3da9ae1ff97e0719b82b8334d984f1bbc485d3673f960 md5: 905296f2713c79017fc9ea11bd57ecfe depends: - __osx >=11.0 - libcxx >=18 - libgrpc 1.67.1 h0a426d6_2 - libzlib >=1.3.1,<2.0a0 - 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/grpcio?source=hash-mapping size: 814879 timestamp: 1740786902083 - conda: https://conda.anaconda.org/conda-forge/noarch/h11-0.16.0-pyhd8ed1ab_0.conda sha256: f64b68148c478c3bfc8f8d519541de7d2616bf59d44485a5271041d40c061887 md5: 4b69232755285701bc86a5afe4d9933a depends: - python >=3.9 - typing_extensions license: MIT license_family: MIT purls: - pkg:pypi/h11?source=hash-mapping size: 37697 timestamp: 1745526482242 - 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=compressed-mapping size: 95967 timestamp: 1756364871835 - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.15.1-nompi_py310h4aa865e_101.conda sha256: 427fc2540a4728dc80d9f0b464541aed61d35ae9ccafcd7f6bbce499eeaf8ce9 md5: 4fccf52eaeb2ae9d9e251623e2b66e63 depends: - __glibc >=2.17,<3.0.a0 - cached-property - hdf5 >=1.14.6,<1.14.7.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: 1217205 timestamp: 1764016763175 - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.15.1-nompi_py311h0b2f468_101.conda sha256: 6bf4f9a6ab5ccbfd8a2a6f130d5c14cb12f77ada367d3fa7724cd2f6515bddab md5: 1ce254e09ec4982ed0334e5e6f113e1c depends: - __glibc >=2.17,<3.0.a0 - cached-property - hdf5 >=1.14.6,<1.14.7.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: 1312134 timestamp: 1764016671110 - conda: https://conda.anaconda.org/conda-forge/linux-64/h5py-3.15.1-nompi_py312ha4f8f14_101.conda sha256: bb5cefbe5b54195a54f749189fc6797568d52e8790b2f542143c681b98a92b71 md5: 23965cb240cb534649dfe2327ecec4fa depends: - __glibc >=2.17,<3.0.a0 - cached-property - hdf5 >=1.14.6,<1.14.7.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: 1290741 timestamp: 1764016665782 - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.15.1-nompi_py310h30b6785_101.conda sha256: a759b62042256066f381ec48bf45851404b39e4e65a145ede3686e578ae5dba6 md5: fb7729213a66323c67d2e5cfe4924834 depends: - __osx >=10.13 - cached-property - hdf5 >=1.14.6,<1.14.7.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: 1067163 timestamp: 1764017373616 - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.15.1-nompi_py311hc8b82f0_101.conda sha256: a55b85c297362480b370cdfc8acb3817ea3f21e92feed3587cbb9de9dbdc8b64 md5: 64cf6c04db5617196149cb21c43ff873 depends: - __osx >=10.13 - cached-property - hdf5 >=1.14.6,<1.14.7.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: 1150682 timestamp: 1764017027307 - conda: https://conda.anaconda.org/conda-forge/osx-64/h5py-3.15.1-nompi_py312hcf08926_101.conda sha256: 04644ecf6b71e804d8487a5d1b094d60d0d0e38e6f3f7f49f8c7df527a6e394c md5: 8754d1f93fa0936d304d2ad2de09f7ba depends: - __osx >=10.13 - cached-property - hdf5 >=1.14.6,<1.14.7.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: 1146012 timestamp: 1764017396488 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.15.1-nompi_py310hc571fcd_101.conda sha256: 0e5ed0b31962c4da8e43e127c583ee6fc6a0f343d2d3eeb0f0b8baf0c4949291 md5: de8f90d425fb178ffba64422d0883834 depends: - __osx >=11.0 - cached-property - hdf5 >=1.14.6,<1.14.7.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: 1065358 timestamp: 1764017873561 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.15.1-nompi_py311hd5a25a3_101.conda sha256: 34d7f92b6de85528a0fc567f2dab6d85c0e54acc3b40a5e67a1f9a598c2b589e md5: 99f3fea6448a481c9a82fdaef1a0da1e depends: - __osx >=11.0 - cached-property - hdf5 >=1.14.6,<1.14.7.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: 1145724 timestamp: 1764017890824 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/h5py-3.15.1-nompi_py312h4eecd6b_101.conda sha256: 914d4f00a4d8cb86a70ce60241acc631a0e9d0cd939c0091b06de2d6cef51a3b md5: 1f19a033f9c3f388c8f3d3c1643d6611 depends: - __osx >=11.0 - cached-property - hdf5 >=1.14.6,<1.14.7.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: 1139768 timestamp: 1764017732485 - conda: https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-12.2.0-h15599e2_0.conda sha256: 6bd8b22beb7d40562b2889dc68232c589ff0d11a5ad3addd41a8570d11f039d9 md5: b8690f53007e9b5ee2c2178dd4ac778c depends: - __glibc >=2.17,<3.0.a0 - cairo >=1.18.4,<2.0a0 - graphite2 >=1.3.14,<2.0a0 - icu >=75.1,<76.0a0 - libexpat >=2.7.1,<3.0a0 - libfreetype >=2.14.1 - libfreetype6 >=2.14.1 - libgcc >=14 - libglib >=2.86.1,<3.0a0 - libstdcxx >=14 - libzlib >=1.3.1,<2.0a0 license: MIT license_family: MIT purls: [] size: 2411408 timestamp: 1762372726141 - conda: https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.14.6-nompi_h1b119a7_104.conda sha256: 454e9724b322cee277abd7acf4f8d688e9c4ded006b6d5bc9fcc2a1ff907d27a md5: 0857f4d157820dcd5625f61fdfefb780 depends: - __glibc >=2.17,<3.0.a0 - libaec >=1.1.4,<2.0a0 - libcurl >=8.17.0,<9.0a0 - libgcc >=14 - libgfortran - libgfortran5 >=14.3.0 - libstdcxx >=14 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.4,<4.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 3720961 timestamp: 1764771748126 - conda: https://conda.anaconda.org/conda-forge/osx-64/hdf5-1.14.6-nompi_hc1508a4_104.conda sha256: aed322f0e8936960332305fbc213831a3cd301db5ea22c06e1293d953ddec563 md5: 9425a5c53febdf71696aed291586d038 depends: - __osx >=10.13 - libaec >=1.1.4,<2.0a0 - libcurl >=8.17.0,<9.0a0 - libcxx >=19 - libgfortran - libgfortran5 >=14.3.0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.4,<4.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 3528765 timestamp: 1764773824647 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/hdf5-1.14.6-nompi_hd3baa01_104.conda sha256: 3cd591334a838b127dfe8a626f38241892063eac8873abb93255962c71155533 md5: 5a1cbaf2349dd2e6dd6cfaab378de51b depends: - __osx >=11.0 - libaec >=1.1.4,<2.0a0 - libcurl >=8.17.0,<9.0a0 - libcxx >=19 - libgfortran - libgfortran5 >=14.3.0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.4,<4.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 3292042 timestamp: 1764771887501 - 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/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-75.1-he02047a_0.conda sha256: 71e750d509f5fa3421087ba88ef9a7b9be11c53174af3aa4d06aff4c18b38e8e md5: 8b189310083baabfb622af68fd9d3ae3 depends: - __glibc >=2.17,<3.0.a0 - libgcc-ng >=12 - libstdcxx-ng >=12 license: MIT license_family: MIT purls: [] size: 12129203 timestamp: 1720853576813 - conda: https://conda.anaconda.org/conda-forge/linux-64/icu-78.1-h33c6efd_0.conda sha256: 7d6463d0be5092b2ae8f2fad34dc84de83eab8bd44cc0d4be8931881c973c48f md5: 518e9bbbc3e3486d6a4519192ba690f8 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 license: MIT purls: [] size: 12722920 timestamp: 1766299101259 - 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.1-h14c5de8_0.conda sha256: 256df2229f930d7c83d8e2d36fdfce1f78980272558095ce741a9fccc5ed8998 md5: 1e648e0c6657a29dc44102d6e3b10ebc depends: - __osx >=10.13 license: MIT purls: [] size: 12273114 timestamp: 1766299263503 - 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-75.1-hfee45f7_0.conda sha256: 9ba12c93406f3df5ab0a43db8a4b4ef67a5871dfd401010fbe29b218b2cbe620 md5: 5eb22c1d7b3fc4abb50d92d621583137 depends: - __osx >=11.0 license: MIT license_family: MIT purls: [] size: 11857802 timestamp: 1720853997952 - conda: https://conda.anaconda.org/conda-forge/win-64/icu-78.1-h637d24d_0.conda sha256: bee083d5a0f05c380fcec1f30a71ef5518b23563aeb0a21f6b60b792645f9689 md5: cb8048bed35ef01431184d6a88e46b3e depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT purls: [] size: 13849749 timestamp: 1766299627069 - conda: https://conda.anaconda.org/conda-forge/noarch/id-1.5.0-pyh29332c3_0.conda sha256: 161e3eb5aba887d0329bb4099f72cb92eed9072cf63f551d08540480116e69a2 md5: d37314c8f553e3b4b44d113a0ee10196 depends: - python >=3.9 - requests - python license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/id?source=hash-mapping size: 24444 timestamp: 1737528654512 - conda: https://conda.anaconda.org/conda-forge/noarch/identify-2.6.15-pyhd8ed1ab_0.conda sha256: 32d5007d12e5731867908cbf5345f5cd44a6c8755a2e8e63e15a184826a51f82 md5: 25f954b7dae6dd7b0dc004dab74f1ce9 depends: - python >=3.10 - ukkonen license: MIT license_family: MIT purls: - pkg:pypi/identify?source=hash-mapping size: 79151 timestamp: 1759437561529 - conda: https://conda.anaconda.org/conda-forge/noarch/idna-3.11-pyhd8ed1ab_0.conda sha256: ae89d0299ada2a3162c2614a9d26557a92aa6a77120ce142f8e0109bbf0342b0 md5: 53abe63df7e10a6ba605dc5f9f961d36 depends: - python >=3.10 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/idna?source=hash-mapping size: 50721 timestamp: 1760286526795 - conda: https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2 sha256: c2bfd7043e0c4c12d8b5593de666c1e81d67b83c474a0a79282cc5c4ef845460 md5: 7de5386c8fea29e76b303f37dde4c352 depends: - python >=3.4 license: MIT license_family: MIT purls: - pkg:pypi/imagesize?source=hash-mapping size: 10164 timestamp: 1656939625410 - pypi: ./ name: imbalanced-learn version: 0.15.dev0 sha256: 8bcb8b74e2904bd6e1b8b4f5ddc861fde89d035b80f1df848f9628cb54db1d93 requires_dist: - numpy>=1.25.2,<3 - scipy>=1.11.4,<2 - scikit-learn>=1.4.2,<2 - sklearn-compat>=0.1.5,<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' editable: true - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.7.0-pyhe01879c_1.conda sha256: c18ab120a0613ada4391b15981d86ff777b5690ca461ea7e9e49531e8f374745 md5: 63ccfdc3a3ce25b027b8767eb722fca8 depends: - python >=3.9 - zipp >=3.20 - python license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/importlib-metadata?source=hash-mapping size: 34641 timestamp: 1747934053147 - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-6.5.2-pyhd8ed1ab_0.conda sha256: acc1d991837c0afb67c75b77fdc72b4bf022aac71fedd8b9ea45918ac9b08a80 md5: c85c76dc67d75619a92f51dfbce06992 depends: - python >=3.9 - zipp >=3.1.0 constrains: - importlib-resources >=6.5.2,<6.5.3.0a0 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/importlib-resources?source=hash-mapping size: 33781 timestamp: 1736252433366 - 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=compressed-mapping size: 13387 timestamp: 1760831448842 - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.1.0-pyh5552912_0.conda sha256: b5f7eaba3bb109be49d00a0a8bda267ddf8fa66cc1b54fc5944529ed6f3e8503 md5: 1849eec35b60082d2bd66b4e36dec2b6 depends: - appnope - __osx - comm >=0.1.1 - debugpy >=1.6.5 - ipython >=7.23.1 - jupyter_client >=8.0.0 - jupyter_core >=4.12,!=5.0.* - matplotlib-inline >=0.1 - nest-asyncio >=1.4 - packaging >=22 - psutil >=5.7 - python >=3.10 - pyzmq >=25 - tornado >=6.2 - 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: 132289 timestamp: 1761567969884 - conda: https://conda.anaconda.org/conda-forge/noarch/ipykernel-7.1.0-pyha191276_0.conda sha256: a9d6b74115dbd62e19017ff8fa4885b07b5164427f262cc15b5307e5aaf3ee73 md5: c6f63cfe66adaa5650788e3106b6683a depends: - python - __linux - comm >=0.1.1 - debugpy >=1.6.5 - ipython >=7.23.1 - jupyter_client >=8.0.0 - jupyter_core >=4.12,!=5.0.* - matplotlib-inline >=0.1 - nest-asyncio >=1.4 - packaging >=22 - psutil >=5.7 - python >=3.10 - pyzmq >=25 - tornado >=6.2 - 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: 133820 timestamp: 1761567932044 - conda: https://conda.anaconda.org/conda-forge/noarch/ipython-9.8.0-pyh53cf698_0.conda sha256: 8a72c9945dc4726ee639a9652b622ae6b03f3eba0e16a21d1c6e5bfb562f5a3f md5: fd77b1039118a3e8ce1070ac8ed45bae depends: - __unix - pexpect >4.3 - decorator >=4.3.2 - ipython_pygments_lexers >=1.0.0 - jedi >=0.18.1 - matplotlib-inline >=0.1.5 - prompt-toolkit >=3.0.41,<3.1.0 - pygments >=2.11.0 - python >=3.11 - stack_data >=0.6.0 - traitlets >=5.13.0 - typing_extensions >=4.6 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/ipython?source=compressed-mapping size: 645145 timestamp: 1764766793792 - 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-pyhd8ed1ab_2.conda sha256: 3d16a0fa55a29fe723c918a979b2ee927eb0bf9616381cdfd26fa9ea2b649546 md5: ade6b25a6136661dadd1a43e4350b10b depends: - more-itertools - python >=3.9 license: MIT license_family: MIT purls: - pkg:pypi/jaraco-classes?source=hash-mapping size: 12109 timestamp: 1733326001034 - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.context-6.0.1-pyhd8ed1ab_0.conda sha256: bfaba92cd33a0ae2488ab64a1d4e062bcf52b26a71f88292c62386ccac4789d7 md5: bcc023a32ea1c44a790bbf1eae473486 depends: - backports.tarfile - python >=3.9 license: MIT license_family: MIT purls: - pkg:pypi/jaraco-context?source=hash-mapping size: 12483 timestamp: 1733382698758 - conda: https://conda.anaconda.org/conda-forge/noarch/jaraco.functools-4.4.0-pyhd8ed1ab_0.conda sha256: 3e297f27f24d56391b937a388d37a95ccf4eb869fb11a07383eb50444e0a3445 md5: 0d6555c4a8b8631cc8a89cdd27c64557 depends: - more-itertools - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/jaraco-functools?source=hash-mapping size: 16387 timestamp: 1766318885381 - 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=compressed-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.12.1-pyhd8ed1ab_0.conda sha256: 4e08ccf9fa1103b617a4167a270768de736a36be795c6cd34c2761100d332f74 md5: 0fc93f473c31a2f85c0bde213e7c63ca depends: - python >=3.9 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/json5?source=hash-mapping size: 34191 timestamp: 1755034963991 - conda: https://conda.anaconda.org/conda-forge/noarch/jsonpointer-3.0.0-pyhcf101f3_3.conda sha256: 1a1328476d14dfa8b84dbacb7f7cd7051c175498406dc513ca6c679dc44f3981 md5: cd2214824e36b0180141d422aba01938 depends: - python >=3.10 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/jsonpointer?source=hash-mapping size: 13967 timestamp: 1765026384757 - conda: https://conda.anaconda.org/conda-forge/noarch/jsonschema-4.25.1-pyhe01879c_0.conda sha256: ac377ef7762e49cb9c4f985f1281eeff471e9adc3402526eea78e6ac6589cf1d md5: 341fd940c242cf33e832c0402face56f depends: - attrs >=22.2.0 - jsonschema-specifications >=2023.3.6 - python >=3.9 - referencing >=0.28.4 - rpds-py >=0.7.1 - python license: MIT license_family: MIT purls: - pkg:pypi/jsonschema?source=hash-mapping size: 81688 timestamp: 1755595646123 - 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.25.1-he01879c_0.conda sha256: aef6705fe1335e6472e1b6365fcdb586356b18dceff72d8d6a315fc90e900ccf md5: 13e31c573c884962318a738405ca3487 depends: - jsonschema >=4.25.1,<4.25.2.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: 4744 timestamp: 1755595646123 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter-lsp-2.3.0-pyhcf101f3_0.conda sha256: 897ad2e2c2335ef3c2826d7805e16002a1fd0d509b4ae0bc66617f0e0ff07bc2 md5: 62b7c96c6cd77f8173cc5cada6a9acaa 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: 60377 timestamp: 1756388269267 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_client-8.7.0-pyhcf101f3_0.conda sha256: 6aa61417547b925de64905b7a4da7c98e0b355f48a7b21bdbef438f8950ee74e md5: 1b0397a7b1fbffa031feb690b5fd0277 depends: - jupyter_core >=5.1 - python >=3.10 - python-dateutil >=2.8.2 - pyzmq >=25.0 - tornado >=6.4.1 - traitlets >=5.3 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/jupyter-client?source=compressed-mapping size: 111367 timestamp: 1765375773813 - 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.0-pyh29332c3_0.conda sha256: 37e6ac3ccf7afcc730c3b93cb91a13b9ae827fd306f35dd28f958a74a14878b5 md5: f56000b36f09ab7533877e695e4e8cb0 depends: - jsonschema-with-format-nongpl >=4.18.0 - packaging - python >=3.9 - 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: 23647 timestamp: 1738765986736 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server-2.17.0-pyhcf101f3_0.conda sha256: 74c4e642be97c538dae1895f7052599dfd740d8bd251f727bce6453ce8d6cd9a md5: d79a87dcfa726bcea8e61275feed6f83 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=hash-mapping size: 347094 timestamp: 1755870522134 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyter_server_terminals-0.5.3-pyhd8ed1ab_1.conda sha256: 0890fc79422191bc29edf17d7b42cff44ba254aa225d31eb30819f8772b775b8 md5: 2d983ff1b82a1ccb6f2e9d8784bdd6bd depends: - python >=3.9 - terminado >=0.8.3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/jupyter-server-terminals?source=hash-mapping size: 19711 timestamp: 1733428049134 - conda: https://conda.anaconda.org/conda-forge/noarch/jupyterlab-4.5.1-pyhd8ed1ab_0.conda sha256: ac31a517238173eb565ba9f517b1f9437fba48035f1276a9c1190c145657cafd md5: f8e8f8db45e1a946ce9b20b0f60b3111 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 - 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: 8141875 timestamp: 1765819955819 - 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.12.0-pyh753f3f9_0.conda sha256: fc14954b4f916bade17fa03fc57371f1578236c3e4d5934926fef32880c87844 md5: 3e82727e0621a5d90ea57e4e345f42be depends: - absl-py - h5py - ml_dtypes - namex - numpy - optree - packaging - python >=3.10 - rich constrains: - tensorflow >=2.18.0 - pytorch >=2.6.0 - jax >=0.5.0 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/keras?source=hash-mapping size: 841185 timestamp: 1761616624583 - 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/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.4.9-py311h724c32c_2.conda sha256: 81181e88c0d49cc86bc687e2583da0cb0b651525bf17d4f4f3aecb1596441769 md5: 4089f739463c798e10d8644bc34e24de 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: 78452 timestamp: 1762488745068 - conda: https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.9-py312h0a2e395_2.conda sha256: 170d76b7ac7197012bb048e1021482a7b2455f3592a5e8d97c96f285ebad064b md5: 3a3004fddd39e3bb1a631b08d7045156 depends: - 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/kiwisolver?source=hash-mapping size: 77682 timestamp: 1762488738724 - conda: https://conda.anaconda.org/conda-forge/osx-64/kiwisolver-1.4.9-py311h591569d_2.conda sha256: e942e7bc46b422241fcfd604f803fddefc15db4ae69979c1ac808f7804e1b2e4 md5: be9c882a849efcda40e54a96552a2cbe depends: - python - libcxx >=19 - __osx >=10.13 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/kiwisolver?source=hash-mapping size: 67563 timestamp: 1762488901618 - conda: https://conda.anaconda.org/conda-forge/osx-64/kiwisolver-1.4.9-py312h90e26e8_2.conda sha256: 9e4e940969e6765bd2a13c76e131bcb02b8930a3c78adec0dbe83a8494b40a52 md5: b85c7204ae22668690eb1e95640202c4 depends: - python - libcxx >=19 - __osx >=10.13 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/kiwisolver?source=hash-mapping size: 69024 timestamp: 1762488958152 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.4.9-py311h26d6576_2.conda sha256: cd3792d2ad92ef951f2753e8b8b43e0fcafdd6c696747d05912142bfe6fff76f md5: 37b7d697e320b860ef2d8419714dc6d4 depends: - 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/kiwisolver?source=hash-mapping size: 66163 timestamp: 1762488860176 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/kiwisolver-1.4.9-py312hd8c8125_2.conda sha256: 8d68f6ec4d947902034fe9ed9d4a4c1180b5767bd9731af940f5a0e436bc3dfd md5: ddf4775023a2466ee308792ed80ca408 depends: - python - python 3.12.* *_cpython - 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: 67752 timestamp: 1762488827477 - conda: https://conda.anaconda.org/conda-forge/linux-64/krb5-1.21.3-h659f571_0.conda sha256: 99df692f7a8a5c27cd14b5fb1374ee55e756631b9c3d659ed3ee60830249b238 md5: 3f43953b7d3fb3aaa1d0d0723d91e368 depends: - keyutils >=1.6.1,<2.0a0 - libedit >=3.1.20191231,<3.2.0a0 - libedit >=3.1.20191231,<4.0a0 - libgcc-ng >=12 - libstdcxx-ng >=12 - openssl >=3.3.1,<4.0a0 license: MIT license_family: MIT purls: [] size: 1370023 timestamp: 1719463201255 - conda: https://conda.anaconda.org/conda-forge/osx-64/krb5-1.21.3-h37d8d59_0.conda sha256: 83b52685a4ce542772f0892a0f05764ac69d57187975579a0835ff255ae3ef9c md5: d4765c524b1d91567886bde656fb514b depends: - __osx >=10.13 - libcxx >=16 - libedit >=3.1.20191231,<3.2.0a0 - libedit >=3.1.20191231,<4.0a0 - openssl >=3.3.1,<4.0a0 license: MIT license_family: MIT purls: [] size: 1185323 timestamp: 1719463492984 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/krb5-1.21.3-h237132a_0.conda sha256: 4442f957c3c77d69d9da3521268cad5d54c9033f1a73f99cde0a3658937b159b md5: c6dc8a0fdec13a0565936655c33069a1 depends: - __osx >=11.0 - libcxx >=16 - libedit >=3.1.20191231,<3.2.0a0 - libedit >=3.1.20191231,<4.0a0 - openssl >=3.3.1,<4.0a0 license: MIT license_family: MIT purls: [] size: 1155530 timestamp: 1719463474401 - 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=compressed-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/linux-64/lcms2-2.17-h717163a_0.conda sha256: d6a61830a354da022eae93fa896d0991385a875c6bba53c82263a289deda9db8 md5: 000e85703f0fd9594c81710dd5066471 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - libjpeg-turbo >=3.0.0,<4.0a0 - libtiff >=4.7.0,<4.8.0a0 license: MIT license_family: MIT purls: [] size: 248046 timestamp: 1739160907615 - conda: https://conda.anaconda.org/conda-forge/osx-64/lcms2-2.17-h72f5680_0.conda sha256: bcb81543e49ff23e18dea79ef322ab44b8189fb11141b1af99d058503233a5fc md5: bf210d0c63f2afb9e414a858b79f0eaa depends: - __osx >=10.13 - libjpeg-turbo >=3.0.0,<4.0a0 - libtiff >=4.7.0,<4.8.0a0 license: MIT license_family: MIT purls: [] size: 226001 timestamp: 1739161050843 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lcms2-2.17-h7eeda09_0.conda sha256: 310a62c2f074ebd5aa43b3cd4b00d46385ce680fa2132ecee255a200e2d2f15f md5: 92a61fd30b19ebd5c1621a5bfe6d8b5f depends: - __osx >=11.0 - libjpeg-turbo >=3.0.0,<4.0a0 - libtiff >=4.7.0,<4.8.0a0 license: MIT license_family: MIT purls: [] size: 212125 timestamp: 1739161108467 - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.45-default_hbd61a6d_104.conda sha256: 9e191baf2426a19507f1d0a17be0fdb7aa155cdf0f61d5a09c808e0a69464312 md5: a6abd2796fc332536735f68ba23f7901 depends: - __glibc >=2.17,<3.0.a0 - zstd >=1.5.7,<1.6.0a0 constrains: - binutils_impl_linux-64 2.45 license: GPL-3.0-only license_family: GPL purls: [] size: 725545 timestamp: 1764007826689 - conda: https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h0aef613_1.conda sha256: 412381a43d5ff9bbed82cd52a0bbca5b90623f62e41007c9c42d3870c60945ff md5: 9344155d33912347b37f0ae6c410a835 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - libstdcxx >=13 license: Apache-2.0 license_family: Apache purls: [] size: 264243 timestamp: 1745264221534 - conda: https://conda.anaconda.org/conda-forge/osx-64/lerc-4.0.0-hcca01a6_1.conda sha256: cc1f1d7c30aa29da4474ec84026ec1032a8df1d7ec93f4af3b98bb793d01184e md5: 21f765ced1a0ef4070df53cb425e1967 depends: - __osx >=10.13 - libcxx >=18 license: Apache-2.0 license_family: Apache purls: [] size: 248882 timestamp: 1745264331196 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/lerc-4.0.0-hd64df32_1.conda sha256: 12361697f8ffc9968907d1a7b5830e34c670e4a59b638117a2cdfed8f63a38f8 md5: a74332d9b60b62905e3d30709df08bf1 depends: - __osx >=11.0 - libcxx >=18 license: Apache-2.0 license_family: Apache purls: [] size: 188306 timestamp: 1745264362794 - 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-20250512.1-cxx17_hba17884_0.conda sha256: dcd1429a1782864c452057a6c5bc1860f2b637dc20a2b7e6eacd57395bbceff8 md5: 83b160d4da3e1e847bf044997621ed63 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - libstdcxx >=13 constrains: - libabseil-static =20250512.1=cxx17* - abseil-cpp =20250512.1 license: Apache-2.0 license_family: Apache purls: [] size: 1310612 timestamp: 1750194198254 - 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-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-20240722.0-cxx17_h07bc746_4.conda sha256: 05fa5e5e908962b9c5aba95f962e2ca81d9599c4715aebe5e4ddb72b309d1770 md5: c2d95bd7aa8d564a9bd7eca5e571a5b3 depends: - __osx >=11.0 - libcxx >=18 constrains: - libabseil-static =20240722.0=cxx17* - abseil-cpp =20240722.0 license: Apache-2.0 license_family: Apache purls: [] size: 1178260 timestamp: 1736008642885 - conda: https://conda.anaconda.org/conda-forge/linux-64/libaec-1.1.4-h3f801dc_0.conda sha256: 410ab78fe89bc869d435de04c9ffa189598ac15bb0fe1ea8ace8fb1b860a2aa3 md5: 01ba04e414e47f95c03d6ddd81fd37be depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - libstdcxx >=13 license: BSD-2-Clause license_family: BSD purls: [] size: 36825 timestamp: 1749993532943 - conda: https://conda.anaconda.org/conda-forge/osx-64/libaec-1.1.4-ha6bc127_0.conda sha256: f4fe00ef0df58b670696c62f2ec3f6484431acbf366ecfbcb71141c81439e331 md5: 1a768b826dfc68e07786788d98babfc3 depends: - __osx >=10.13 - libcxx >=18 license: BSD-2-Clause license_family: BSD purls: [] size: 30034 timestamp: 1749993664561 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libaec-1.1.4-h51d1e36_0.conda sha256: 0ea6b73b3fb1511615d9648186a7409e73b7a8d9b3d890d39df797730e3d1dbb md5: 8ed0f86b7a5529b98ec73b43a53ce800 depends: - __osx >=11.0 - libcxx >=18 license: BSD-2-Clause license_family: BSD purls: [] size: 30173 timestamp: 1749993648288 - conda: https://conda.anaconda.org/conda-forge/linux-64/libblas-3.11.0-5_h4a7cf45_openblas.conda build_number: 5 sha256: 18c72545080b86739352482ba14ba2c4815e19e26a7417ca21a95b76ec8da24c md5: c160954f7418d7b6e87eaf05a8913fa9 depends: - libopenblas >=0.3.30,<0.3.31.0a0 - libopenblas >=0.3.30,<1.0a0 constrains: - mkl <2026 - liblapack 3.11.0 5*_openblas - libcblas 3.11.0 5*_openblas - blas 2.305 openblas - liblapacke 3.11.0 5*_openblas license: BSD-3-Clause license_family: BSD purls: [] size: 18213 timestamp: 1765818813880 - 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/osx-64/libblas-3.11.0-5_he492b99_openblas.conda build_number: 5 sha256: 4754de83feafa6c0b41385f8dab1b13f13476232e16f524564a340871a9fc3bc md5: 36d2e68a156692cbae776b75d6ca6eae depends: - libopenblas >=0.3.30,<0.3.31.0a0 - libopenblas >=0.3.30,<1.0a0 constrains: - liblapack 3.11.0 5*_openblas - blas 2.305 openblas - libcblas 3.11.0 5*_openblas - mkl <2026 - liblapacke 3.11.0 5*_openblas license: BSD-3-Clause license_family: BSD purls: [] size: 18476 timestamp: 1765819054657 - 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-arm64/libblas-3.11.0-5_h51639a9_openblas.conda build_number: 5 sha256: 620a6278f194dcabc7962277da6835b1e968e46ad0c8e757736255f5ddbfca8d md5: bcc025e2bbaf8a92982d20863fe1fb69 depends: - libopenblas >=0.3.30,<0.3.31.0a0 - libopenblas >=0.3.30,<1.0a0 constrains: - libcblas 3.11.0 5*_openblas - liblapack 3.11.0 5*_openblas - liblapacke 3.11.0 5*_openblas - blas 2.305 openblas - mkl <2026 license: BSD-3-Clause license_family: BSD purls: [] size: 18546 timestamp: 1765819094137 - 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/win-64/libblas-3.11.0-5_hf2e6a31_mkl.conda build_number: 5 sha256: f0cb7b2697461a306341f7ff32d5b361bb84f3e94478464c1e27ee01fc8f276b md5: f9decf88743af85c9c9e05556a4c47c0 depends: - mkl >=2025.3.0,<2026.0a0 constrains: - liblapack 3.11.0 5*_mkl - libcblas 3.11.0 5*_mkl - blas 2.305 mkl - liblapacke 3.11.0 5*_mkl license: BSD-3-Clause license_family: BSD purls: [] size: 67438 timestamp: 1765819100043 - 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/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-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/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/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-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/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/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-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/linux-64/libcblas-3.11.0-5_h0358290_openblas.conda build_number: 5 sha256: 0cbdcc67901e02dc17f1d19e1f9170610bd828100dc207de4d5b6b8ad1ae7ad8 md5: 6636a2b6f1a87572df2970d3ebc87cc0 depends: - libblas 3.11.0 5_h4a7cf45_openblas constrains: - liblapacke 3.11.0 5*_openblas - blas 2.305 openblas - liblapack 3.11.0 5*_openblas license: BSD-3-Clause license_family: BSD purls: [] size: 18194 timestamp: 1765818837135 - 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/osx-64/libcblas-3.11.0-5_h9b27e0a_openblas.conda build_number: 5 sha256: 8077c29ea720bd152be6e6859a3765228cde51301fe62a3b3f505b377c2cb48c md5: b31d771cbccff686e01a687708a7ca41 depends: - libblas 3.11.0 5_he492b99_openblas constrains: - liblapack 3.11.0 5*_openblas - blas 2.305 openblas - liblapacke 3.11.0 5*_openblas license: BSD-3-Clause license_family: BSD purls: [] size: 18484 timestamp: 1765819073006 - 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-arm64/libcblas-3.11.0-5_hb0561ab_openblas.conda build_number: 5 sha256: 38809c361bbd165ecf83f7f05fae9b791e1baa11e4447367f38ae1327f402fc0 md5: efd8bd15ca56e9d01748a3beab8404eb depends: - libblas 3.11.0 5_h51639a9_openblas constrains: - liblapacke 3.11.0 5*_openblas - liblapack 3.11.0 5*_openblas - blas 2.305 openblas license: BSD-3-Clause license_family: BSD purls: [] size: 18548 timestamp: 1765819108956 - 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/win-64/libcblas-3.11.0-5_h2a3cdd5_mkl.conda build_number: 5 sha256: 49dc59d8e58360920314b8d276dd80da7866a1484a9abae4ee2760bc68f3e68d md5: b3fa8e8b55310ba8ef0060103afb02b5 depends: - libblas 3.11.0 5_hf2e6a31_mkl constrains: - liblapack 3.11.0 5*_mkl - liblapacke 3.11.0 5*_mkl - blas 2.305 mkl license: BSD-3-Clause license_family: BSD purls: [] size: 68079 timestamp: 1765819124349 - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang-cpp21.1-21.1.7-default_h99862b1_2.conda sha256: b41513470499628c0f9b7e1b057c8d7641a75be482d4a296a4eb41234aaac971 md5: fd47a1021b2a3a91b0c05f4d7b529b68 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libllvm21 >=21.1.7,<21.2.0a0 - libstdcxx >=14 license: Apache-2.0 WITH LLVM-exception license_family: Apache purls: [] size: 21055117 timestamp: 1766016009924 - conda: https://conda.anaconda.org/conda-forge/linux-64/libclang13-21.1.7-default_h746c552_2.conda sha256: 7dece5ba0962c33b230db42c6fa6661cdf92ef08dea3e15ac2bc754c5878560a md5: 684375df603a5fcaffc47d04fe66efc0 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libllvm21 >=21.1.7,<21.2.0a0 - libstdcxx >=14 license: Apache-2.0 WITH LLVM-exception license_family: Apache purls: [] size: 12346744 timestamp: 1766016353069 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-hb8b1518_5.conda sha256: cb83980c57e311783ee831832eb2c20ecb41e7dee6e86e8b70b8cef0e43eab55 md5: d4a250da4737ee127fb1fa6452a9002e depends: - __glibc >=2.17,<3.0.a0 - krb5 >=1.21.3,<1.22.0a0 - libgcc >=13 - libstdcxx >=13 - libzlib >=1.3.1,<2.0a0 license: Apache-2.0 license_family: Apache purls: [] size: 4523621 timestamp: 1749905341688 - conda: https://conda.anaconda.org/conda-forge/linux-64/libcurl-8.17.0-h4e3cde8_1.conda sha256: 2d7be2fe0f58a0945692abee7bb909f8b19284b518d958747e5ff51d0655c303 md5: 117499f93e892ea1e57fdca16c2e8351 depends: - __glibc >=2.17,<3.0.a0 - krb5 >=1.21.3,<1.22.0a0 - libgcc >=14 - libnghttp2 >=1.67.0,<2.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.4,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT purls: [] size: 459417 timestamp: 1765379027010 - conda: https://conda.anaconda.org/conda-forge/osx-64/libcurl-8.17.0-h7dd4100_1.conda sha256: 80c7c8ff76eb699ec8d096dce80642b527fd8fc9dd72779bccec8d140c5b997a md5: 9ddfaeed0eafce233ae8f4a430816aa5 depends: - __osx >=10.13 - krb5 >=1.21.3,<1.22.0a0 - libnghttp2 >=1.67.0,<2.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.4,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT purls: [] size: 413119 timestamp: 1765379670120 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcurl-8.17.0-hdece5d2_1.conda sha256: 1a8a958448610ca3f8facddfe261fdbb010e7029a1571b84052ec9770fc0a36e md5: 1d6e791c6e264ae139d469ce011aab51 depends: - __osx >=11.0 - krb5 >=1.21.3,<1.22.0a0 - libnghttp2 >=1.67.0,<2.0a0 - libssh2 >=1.11.1,<2.0a0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.4,<4.0a0 - zstd >=1.5.7,<1.6.0a0 license: curl license_family: MIT purls: [] size: 394471 timestamp: 1765379821294 - conda: https://conda.anaconda.org/conda-forge/osx-64/libcxx-21.1.8-h3d58e20_0.conda sha256: cbd8e821e97436d8fc126c24b50df838b05ba4c80494fbb93ccaf2e3b2d109fb md5: 9f8a60a77ecafb7966ca961c94f33bd1 depends: - __osx >=10.13 license: Apache-2.0 WITH LLVM-exception license_family: Apache purls: [] size: 569777 timestamp: 1765919624323 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libcxx-21.1.8-hf598326_0.conda sha256: 82e228975fd491bcf1071ecd0a6ec2a0fcc5f57eb0bd1d52cb13a18d57c67786 md5: 780f0251b757564e062187044232c2b7 depends: - __osx >=11.0 license: Apache-2.0 WITH LLVM-exception license_family: Apache purls: [] size: 569118 timestamp: 1765919724254 - 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/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-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/linux-64/libdrm-2.4.125-hb03c661_1.conda sha256: c076a213bd3676cc1ef22eeff91588826273513ccc6040d9bea68bccdc849501 md5: 9314bc5a1fe7d1044dc9dfd3ef400535 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libpciaccess >=0.18,<0.19.0a0 license: MIT license_family: MIT purls: [] size: 310785 timestamp: 1757212153962 - 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/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-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/linux-64/libegl-1.7.0-ha4b6fd6_2.conda sha256: 7fd5408d359d05a969133e47af580183fbf38e2235b562193d427bb9dad79723 md5: c151d5eb730e9b7480e6d48c0fc44048 depends: - __glibc >=2.17,<3.0.a0 - libglvnd 1.7.0 ha4b6fd6_2 license: LicenseRef-libglvnd purls: [] size: 44840 timestamp: 1731330973553 - 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/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-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/linux-64/libexpat-2.7.3-hecca717_0.conda sha256: 1e1b08f6211629cbc2efe7a5bca5953f8f6b3cae0eeb04ca4dacee1bd4e2db2f md5: 8b09ae86839581147ef2e5c5e229d164 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 constrains: - expat 2.7.3.* license: MIT license_family: MIT purls: [] size: 76643 timestamp: 1763549731408 - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.3-heffb93a_0.conda sha256: d11b3a6ce5b2e832f430fd112084533a01220597221bee16d6c7dc3947dffba6 md5: 222e0732a1d0780a622926265bee14ef depends: - __osx >=10.13 constrains: - expat 2.7.3.* license: MIT license_family: MIT purls: [] size: 74058 timestamp: 1763549886493 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.3-haf25636_0.conda sha256: fce22610ecc95e6d149e42a42fbc3cc9d9179bd4eb6232639a60f06e080eec98 md5: b79875dbb5b1db9a4a22a4520f918e1a depends: - __osx >=11.0 constrains: - expat 2.7.3.* license: MIT license_family: MIT purls: [] size: 67800 timestamp: 1763549994166 - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.3-hac47afa_0.conda sha256: 844ab708594bdfbd7b35e1a67c379861bcd180d6efe57b654f482ae2f7f5c21e md5: 8c9e4f1a0e688eef2e95711178061a0f depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - expat 2.7.3.* license: MIT license_family: MIT purls: [] size: 70137 timestamp: 1763550049107 - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda sha256: 25cbdfa65580cfab1b8d15ee90b4c9f1e0d72128f1661449c9a999d341377d54 md5: 35f29eec58405aaf55e01cb470d8c26a depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: MIT license_family: MIT purls: [] size: 57821 timestamp: 1760295480630 - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda sha256: 277dc89950f5d97f1683f26e362d6dca3c2efa16cb2f6fdb73d109effa1cd3d0 md5: d214916b24c625bcc459b245d509f22e depends: - __osx >=10.13 license: MIT license_family: MIT purls: [] size: 52573 timestamp: 1760295626449 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda sha256: 9b8acdf42df61b7bfe8bdc545c016c29e61985e79748c64ad66df47dbc2e295f md5: 411ff7cd5d1472bba0f55c0faf04453b depends: - __osx >=11.0 license: MIT license_family: MIT purls: [] size: 40251 timestamp: 1760295839166 - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda sha256: ddff25aaa4f0aa535413f5d831b04073789522890a4d8626366e43ecde1534a3 md5: ba4ad812d2afc22b9a34ce8327a0930f depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT license_family: MIT purls: [] size: 44866 timestamp: 1760295760649 - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype-2.14.1-ha770c72_0.conda sha256: 4641d37faeb97cf8a121efafd6afd040904d4bca8c46798122f417c31d5dfbec md5: f4084e4e6577797150f9b04a4560ceb0 depends: - libfreetype6 >=2.14.1 license: GPL-2.0-only OR FTL purls: [] size: 7664 timestamp: 1757945417134 - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype-2.14.1-h694c41f_0.conda sha256: 035e23ef87759a245d51890aedba0b494a26636784910c3730d76f3dc4482b1d md5: e0e2edaf5e0c71b843e25a7ecc451cc9 depends: - libfreetype6 >=2.14.1 license: GPL-2.0-only OR FTL purls: [] size: 7780 timestamp: 1757945952392 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype-2.14.1-hce30654_0.conda sha256: 9de25a86066f078822d8dd95a83048d7dc2897d5d655c0e04a8a54fca13ef1ef md5: f35fb38e89e2776994131fbf961fa44b depends: - libfreetype6 >=2.14.1 license: GPL-2.0-only OR FTL purls: [] size: 7810 timestamp: 1757947168537 - conda: https://conda.anaconda.org/conda-forge/linux-64/libfreetype6-2.14.1-h73754d4_0.conda sha256: 4a7af818a3179fafb6c91111752954e29d3a2a950259c14a2fc7ba40a8b03652 md5: 8e7251989bca326a28f4a5ffbd74557a depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libpng >=1.6.50,<1.7.0a0 - libzlib >=1.3.1,<2.0a0 constrains: - freetype >=2.14.1 license: GPL-2.0-only OR FTL purls: [] size: 386739 timestamp: 1757945416744 - conda: https://conda.anaconda.org/conda-forge/osx-64/libfreetype6-2.14.1-h6912278_0.conda sha256: f5f28092e368efc773bcd1c381d123f8b211528385a9353e36f8808d00d11655 md5: dfbdc8fd781dc3111541e4234c19fdbd depends: - __osx >=10.13 - libpng >=1.6.50,<1.7.0a0 - libzlib >=1.3.1,<2.0a0 constrains: - freetype >=2.14.1 license: GPL-2.0-only OR FTL purls: [] size: 374993 timestamp: 1757945949585 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libfreetype6-2.14.1-h6da58f4_0.conda sha256: cc4aec4c490123c0f248c1acd1aeab592afb6a44b1536734e20937cda748f7cd md5: 6d4ede03e2a8e20eb51f7f681d2a2550 depends: - __osx >=11.0 - libpng >=1.6.50,<1.7.0a0 - libzlib >=1.3.1,<2.0a0 constrains: - freetype >=2.14.1 license: GPL-2.0-only OR FTL purls: [] size: 346703 timestamp: 1757947166116 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-15.2.0-he0feb66_16.conda sha256: 6eed58051c2e12b804d53ceff5994a350c61baf117ec83f5f10c953a3f311451 md5: 6d0363467e6ed84f11435eb309f2ff06 depends: - __glibc >=2.17,<3.0.a0 - _openmp_mutex >=4.5 constrains: - libgcc-ng ==15.2.0=*_16 - libgomp 15.2.0 he0feb66_16 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 1042798 timestamp: 1765256792743 - conda: https://conda.anaconda.org/conda-forge/osx-64/libgcc-15.2.0-h08519bb_15.conda sha256: e04b115ae32f8cbf95905971856ff557b296511735f4e1587b88abf519ff6fb8 md5: c816665789d1e47cdfd6da8a81e1af64 depends: - _openmp_mutex constrains: - libgomp 15.2.0 15 - libgcc-ng ==15.2.0=*_15 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 422960 timestamp: 1764839601296 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgcc-15.2.0-hcbb3090_16.conda sha256: 646c91dbc422fe92a5f8a3a5409c9aac66549f4ce8f8d1cab7c2aa5db789bb69 md5: 8b216bac0de7a9d60f3ddeba2515545c depends: - _openmp_mutex constrains: - libgcc-ng ==15.2.0=*_16 - libgomp 15.2.0 16 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 402197 timestamp: 1765258985740 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-15.2.0-h69a702a_16.conda sha256: 5f07f9317f596a201cc6e095e5fc92621afca64829785e483738d935f8cab361 md5: 5a68259fac2da8f2ee6f7bfe49c9eb8b depends: - libgcc 15.2.0 he0feb66_16 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 27256 timestamp: 1765256804124 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-15.2.0-h69a702a_16.conda sha256: 8a7b01e1ee1c462ad243524d76099e7174ebdd94ff045fe3e9b1e58db196463b md5: 40d9b534410403c821ff64f00d0adc22 depends: - libgfortran5 15.2.0 h68bc16d_16 constrains: - libgfortran-ng ==15.2.0=*_16 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 27215 timestamp: 1765256845586 - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran-15.2.0-h7e5c614_15.conda sha256: 7bb4d51348e8f7c1a565df95f4fc2a2021229d42300aab8366eda0ea1af90587 md5: a089323fefeeaba2ae60e1ccebf86ddc depends: - libgfortran5 15.2.0 hd16e46c_15 constrains: - libgfortran-ng ==15.2.0=*_15 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 139002 timestamp: 1764839892631 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran-15.2.0-h07b0088_16.conda sha256: 68a6c1384d209f8654112c4c57c68c540540dd8e09e17dd1facf6cf3467798b5 md5: 11e09edf0dde4c288508501fe621bab4 depends: - libgfortran5 15.2.0 hdae7583_16 constrains: - libgfortran-ng ==15.2.0=*_16 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 138630 timestamp: 1765259217400 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-15.2.0-h69a702a_16.conda sha256: dc13ce4ceecb5b3aaca4133731e459d1111961288a1e071cc18bd71d5a47e976 md5: e5eb2ddedabd0063e442f230755d2062 depends: - libgfortran 15.2.0 h69a702a_16 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 27300 timestamp: 1765257039455 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-15.2.0-h68bc16d_16.conda sha256: d0e974ebc937c67ae37f07a28edace978e01dc0f44ee02f29ab8a16004b8148b md5: 39183d4e0c05609fd65f130633194e37 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: 2480559 timestamp: 1765256819588 - conda: https://conda.anaconda.org/conda-forge/osx-64/libgfortran5-15.2.0-hd16e46c_15.conda sha256: 456385a7d3357d5fdfc8e11bf18dcdf71753c4016c440f92a2486057524dd59a md5: c2a6149bf7f82774a0118b9efef966dd 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: 1061950 timestamp: 1764839609607 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libgfortran5-15.2.0-hdae7583_16.conda sha256: 9fb7f4ff219e3fb5decbd0ee90a950f4078c90a86f5d8d61ca608c913062f9b0 md5: 265a9d03461da24884ecc8eb58396d57 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: 598291 timestamp: 1765258993165 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgl-1.7.0-ha4b6fd6_2.conda sha256: dc2752241fa3d9e40ce552c1942d0a4b5eeb93740c9723873f6fcf8d39ef8d2d md5: 928b8be80851f5d8ffb016f9c81dae7a depends: - __glibc >=2.17,<3.0.a0 - libglvnd 1.7.0 ha4b6fd6_2 - libglx 1.7.0 ha4b6fd6_2 license: LicenseRef-libglvnd purls: [] size: 134712 timestamp: 1731330998354 - conda: https://conda.anaconda.org/conda-forge/linux-64/libglib-2.86.3-h6548e54_0.conda sha256: 82d6c2ee9f548c84220fb30fb1b231c64a53561d6e485447394f0a0eeeffe0e6 md5: 034bea55a4feef51c98e8449938e9cee depends: - __glibc >=2.17,<3.0.a0 - libffi >=3.5.2,<3.6.0a0 - libgcc >=14 - libiconv >=1.18,<2.0a0 - libzlib >=1.3.1,<2.0a0 - pcre2 >=10.47,<10.48.0a0 constrains: - glib 2.86.3 *_0 license: LGPL-2.1-or-later purls: [] size: 3946542 timestamp: 1765221858705 - conda: https://conda.anaconda.org/conda-forge/linux-64/libglvnd-1.7.0-ha4b6fd6_2.conda sha256: 1175f8a7a0c68b7f81962699751bb6574e6f07db4c9f72825f978e3016f46850 md5: 434ca7e50e40f4918ab701e3facd59a0 depends: - __glibc >=2.17,<3.0.a0 license: LicenseRef-libglvnd purls: [] size: 132463 timestamp: 1731330968309 - conda: https://conda.anaconda.org/conda-forge/linux-64/libglx-1.7.0-ha4b6fd6_2.conda sha256: 2d35a679624a93ce5b3e9dd301fff92343db609b79f0363e6d0ceb3a6478bfa7 md5: c8013e438185f33b13814c5c488acd5c depends: - __glibc >=2.17,<3.0.a0 - libglvnd 1.7.0 ha4b6fd6_2 - xorg-libx11 >=1.8.10,<2.0a0 license: LicenseRef-libglvnd purls: [] size: 75504 timestamp: 1731330988898 - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-he0feb66_16.conda sha256: 5b3e5e4e9270ecfcd48f47e3a68f037f5ab0f529ccb223e8e5d5ac75a58fc687 md5: 26c46f90d0e727e95c6c9498a33a09f3 depends: - __glibc >=2.17,<3.0.a0 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 603284 timestamp: 1765256703881 - 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.73.1-h3288cfb_1.conda sha256: bc9d32af6167b1f5bcda216dc44eddcb27f3492440571ab12f6e577472a05e34 md5: ff63bb12ac31c176ff257e3289f20770 depends: - __glibc >=2.17,<3.0.a0 - c-ares >=1.34.5,<2.0a0 - libabseil * cxx17* - libabseil >=20250512.1,<20250513.0a0 - libgcc >=14 - libprotobuf >=6.31.1,<6.31.2.0a0 - libre2-11 >=2025.8.12 - libstdcxx >=14 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.4,<4.0a0 - re2 constrains: - grpc-cpp =1.73.1 license: Apache-2.0 license_family: APACHE purls: [] size: 8349777 timestamp: 1761058442526 - 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-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.67.1-h0a426d6_2.conda sha256: a6114f6020f02387aa8bc9167d77c23177f8a3650b55fb0ee100c5227ca475f9 md5: c368d17cdc54d96aa6bd73d07816cf60 depends: - __osx >=11.0 - 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: 5203869 timestamp: 1740786448002 - conda: https://conda.anaconda.org/conda-forge/win-64/libhwloc-2.12.1-default_h4379cf1_1003.conda sha256: 2d534c09f92966b885acb3f4a838f7055cea043165a03079a539b06c54e20a49 md5: d1699ce4fe195a9f61264a1c29b87035 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: 2412642 timestamp: 1765090345611 - 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/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/linux-64/libjpeg-turbo-3.1.2-hb03c661_0.conda sha256: cc9aba923eea0af8e30e0f94f2ad7156e2984d80d1e8e7fe6be5a1f257f0eb32 md5: 8397539e3a0bbd1695584fb4f927485a depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib purls: [] size: 633710 timestamp: 1762094827865 - conda: https://conda.anaconda.org/conda-forge/osx-64/libjpeg-turbo-3.1.2-h8616949_0.conda sha256: ebe2877abc046688d6ea299e80d8322d10c69763f13a102010f90f7168cc5f54 md5: 48dda187f169f5a8f1e5e07701d5cdd9 depends: - __osx >=10.13 constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib purls: [] size: 586189 timestamp: 1762095332781 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libjpeg-turbo-3.1.2-hc919400_0.conda sha256: 6c061c56058bb10374daaef50e81b39cf43e8aee21f0037022c0c39c4f31872f md5: f0695fbecf1006f27f4395d64bd0c4b8 depends: - __osx >=11.0 constrains: - jpeg <0.0.0a license: IJG AND BSD-3-Clause AND Zlib purls: [] size: 551197 timestamp: 1762095054358 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.11.0-5_h47877c9_openblas.conda build_number: 5 sha256: c723b6599fcd4c6c75dee728359ef418307280fa3e2ee376e14e85e5bbdda053 md5: b38076eb5c8e40d0106beda6f95d7609 depends: - libblas 3.11.0 5_h4a7cf45_openblas constrains: - blas 2.305 openblas - liblapacke 3.11.0 5*_openblas - libcblas 3.11.0 5*_openblas license: BSD-3-Clause license_family: BSD purls: [] size: 18200 timestamp: 1765818857876 - 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/osx-64/liblapack-3.11.0-5_h859234e_openblas.conda build_number: 5 sha256: 2c915fe2b3d806d4b82776c882ba66ba3e095e9e2c41cc5c3375bffec6bddfdc md5: eb5b1c25d4ac30813a6ca950a58710d6 depends: - libblas 3.11.0 5_he492b99_openblas constrains: - libcblas 3.11.0 5*_openblas - blas 2.305 openblas - liblapacke 3.11.0 5*_openblas license: BSD-3-Clause license_family: BSD purls: [] size: 18491 timestamp: 1765819090240 - 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-arm64/liblapack-3.11.0-5_hd9741b5_openblas.conda build_number: 5 sha256: 735a6e6f7d7da6f718b6690b7c0a8ae4815afb89138aa5793abe78128e951dbb md5: ca9d752201b7fa1225bca036ee300f2b depends: - libblas 3.11.0 5_h51639a9_openblas constrains: - libcblas 3.11.0 5*_openblas - blas 2.305 openblas - liblapacke 3.11.0 5*_openblas license: BSD-3-Clause license_family: BSD purls: [] size: 18551 timestamp: 1765819121855 - 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/win-64/liblapack-3.11.0-5_hf9ab0e9_mkl.conda build_number: 5 sha256: a2d33f5cc2b8a9042f2af6981c6733ab1a661463823eaa56595a9c58c0ab77e1 md5: e62c42a4196dee97d20400612afcb2b1 depends: - libblas 3.11.0 5_hf2e6a31_mkl constrains: - libcblas 3.11.0 5*_mkl - blas 2.305 mkl - liblapacke 3.11.0 5*_mkl license: BSD-3-Clause license_family: BSD purls: [] size: 80225 timestamp: 1765819148014 - conda: https://conda.anaconda.org/conda-forge/linux-64/libllvm21-21.1.8-hf7376ad_0.conda sha256: 91bb4f5be1601b40b4995911d785e29387970f0b3c80f33f7f9028f95335399f md5: 1a2708a460884d6861425b7f9a7bef99 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - libxml2 - libxml2-16 >=2.14.6 - libzlib >=1.3.1,<2.0a0 - zstd >=1.5.7,<1.6.0a0 license: Apache-2.0 WITH LLVM-exception license_family: Apache purls: [] size: 44333366 timestamp: 1765959132513 - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda sha256: f2591c0069447bbe28d4d696b7fcb0c5bd0b4ac582769b89addbcf26fb3430d8 md5: 1a580f7796c7bf6393fddb8bbbde58dc depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 constrains: - xz 5.8.1.* license: 0BSD purls: [] size: 112894 timestamp: 1749230047870 - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda sha256: 7e22fd1bdb8bf4c2be93de2d4e718db5c548aa082af47a7430eb23192de6bb36 md5: 8468beea04b9065b9807fc8b9cdc5894 depends: - __osx >=10.13 constrains: - xz 5.8.1.* license: 0BSD purls: [] size: 104826 timestamp: 1749230155443 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda sha256: 0cb92a9e026e7bd4842f410a5c5c665c89b2eb97794ffddba519a626b8ce7285 md5: d6df911d4564d77c4374b02552cb17d1 depends: - __osx >=11.0 constrains: - xz 5.8.1.* license: 0BSD purls: [] size: 92286 timestamp: 1749230283517 - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda sha256: 55764956eb9179b98de7cc0e55696f2eff8f7b83fc3ebff5e696ca358bca28cc md5: c15148b2e18da456f5108ccb5e411446 depends: - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 constrains: - xz 5.8.1.* license: 0BSD purls: [] size: 104935 timestamp: 1749230611612 - 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/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda sha256: 3aa92d4074d4063f2a162cd8ecb45dccac93e543e565c01a787e16a43501f7ee md5: c7e925f37e3b40d893459e625f6a53f1 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 license: BSD-2-Clause license_family: BSD purls: [] size: 91183 timestamp: 1748393666725 - conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-h6e16a3a_0.conda sha256: 98299c73c7a93cd4f5ff8bb7f43cd80389f08b5a27a296d806bdef7841cc9b9e md5: 18b81186a6adb43f000ad19ed7b70381 depends: - __osx >=10.13 license: BSD-2-Clause license_family: BSD purls: [] size: 77667 timestamp: 1748393757154 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h5505292_0.conda sha256: 0a1875fc1642324ebd6c4ac864604f3f18f57fbcf558a8264f6ced028a3c75b2 md5: 85ccccb47823dd9f7a99d2c7f530342f depends: - __osx >=11.0 license: BSD-2-Clause license_family: BSD purls: [] size: 71829 timestamp: 1748393749336 - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-h2466b09_0.conda sha256: fc529fc82c7caf51202cc5cec5bb1c2e8d90edbac6d0a4602c966366efe3c7bf md5: 74860100b2029e2523cf480804c76b9b depends: - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: BSD-2-Clause license_family: BSD purls: [] size: 88657 timestamp: 1723861474602 - conda: https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.67.0-had1ee68_0.conda sha256: a4a7dab8db4dc81c736e9a9b42bdfd97b087816e029e221380511960ac46c690 md5: b499ce4b026493a13774bcf0f4c33849 depends: - __glibc >=2.17,<3.0.a0 - c-ares >=1.34.5,<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.2,<4.0a0 license: MIT license_family: MIT purls: [] size: 666600 timestamp: 1756834976695 - conda: https://conda.anaconda.org/conda-forge/osx-64/libnghttp2-1.67.0-h3338091_0.conda sha256: c48d7e1cc927aef83ff9c48ae34dd1d7495c6ccc1edc4a3a6ba6aff1624be9ac md5: e7630cef881b1174d40f3e69a883e55f depends: - __osx >=10.13 - c-ares >=1.34.5,<2.0a0 - libcxx >=19 - libev >=4.33,<4.34.0a0 - libev >=4.33,<5.0a0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.2,<4.0a0 license: MIT license_family: MIT purls: [] size: 605680 timestamp: 1756835898134 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libnghttp2-1.67.0-hc438710_0.conda sha256: a07cb53b5ffa2d5a18afc6fd5a526a5a53dd9523fbc022148bd2f9395697c46d md5: a4b4dd73c67df470d091312ab87bf6ae depends: - __osx >=11.0 - c-ares >=1.34.5,<2.0a0 - libcxx >=19 - libev >=4.33,<4.34.0a0 - libev >=4.33,<5.0a0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.2,<4.0a0 license: MIT license_family: MIT purls: [] size: 575454 timestamp: 1756835746393 - 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.30-pthreads_h94d23a6_4.conda sha256: 199d79c237afb0d4780ccd2fbf829cea80743df60df4705202558675e07dd2c5 md5: be43915efc66345cccb3c310b6ed0374 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libgfortran - libgfortran5 >=14.3.0 constrains: - openblas >=0.3.30,<0.3.31.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 5927939 timestamp: 1763114673331 - 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.30-openmp_h6006d49_4.conda sha256: ba642353f7f41ab2d2eb6410fbe522238f0f4483bcd07df30b3222b4454ee7cd md5: 9241a65e6e9605e4581a2a8005d7f789 depends: - __osx >=10.13 - libgfortran - libgfortran5 >=14.3.0 - llvm-openmp >=19.1.7 constrains: - openblas >=0.3.30,<0.3.31.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 6268795 timestamp: 1763117623665 - 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.30-openmp_ha158390_3.conda sha256: dcc626c7103503d1dfc0371687ad553cb948b8ed0249c2a721147bdeb8db4a73 md5: a18a7f471c517062ee71b843ef95eb8a depends: - __osx >=11.0 - libgfortran - libgfortran5 >=14.3.0 - llvm-openmp >=19.1.7 constrains: - openblas >=0.3.30,<0.3.31.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 4285762 timestamp: 1761749506256 - conda: https://conda.anaconda.org/conda-forge/linux-64/libopengl-1.7.0-ha4b6fd6_2.conda sha256: 215086c108d80349e96051ad14131b751d17af3ed2cb5a34edd62fa89bfe8ead md5: 7df50d44d4a14d6c31a2c54f2cd92157 depends: - __glibc >=2.17,<3.0.a0 - libglvnd 1.7.0 ha4b6fd6_2 license: LicenseRef-libglvnd purls: [] size: 50757 timestamp: 1731330993524 - conda: https://conda.anaconda.org/conda-forge/linux-64/libpciaccess-0.18-hb9d3cd8_0.conda sha256: 0bd91de9b447a2991e666f284ae8c722ffb1d84acb594dbd0c031bd656fa32b2 md5: 70e3400cbbfa03e96dcde7fc13e38c7b depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 license: MIT license_family: MIT purls: [] size: 28424 timestamp: 1749901812541 - conda: https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.53-h421ea60_0.conda sha256: 8acdeb9a7e3d2630176ba8e947caf6bf4985a5148dec69b801e5eb797856688b md5: 00d4e66b1f746cb14944cad23fffb405 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libzlib >=1.3.1,<2.0a0 license: zlib-acknowledgement purls: [] size: 317748 timestamp: 1764981060755 - conda: https://conda.anaconda.org/conda-forge/osx-64/libpng-1.6.53-h380d223_0.conda sha256: 62a861e407bf0d0a2a983d0b0167ed263ae035cae7061976e9994f9963e6c68d md5: 0cdbbd56f660997cfe5d33e516afac2f depends: - __osx >=10.13 - libzlib >=1.3.1,<2.0a0 license: zlib-acknowledgement purls: [] size: 298397 timestamp: 1764981064303 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libpng-1.6.53-hfab5511_0.conda sha256: 6793e7284e175c515fc6453be45c7c0febdea853657d246d8136fbda791dd0ad md5: 62b6111feeffe607c3ecc8ca5bd1514b depends: - __osx >=11.0 - libzlib >=1.3.1,<2.0a0 license: zlib-acknowledgement purls: [] size: 288210 timestamp: 1764981075326 - conda: https://conda.anaconda.org/conda-forge/linux-64/libpq-18.1-h5c52fec_2.conda sha256: bbab2c3e6f650f2bd1bc84d88e6a20fefa6a401fa445bb4b97c509c1b3a89fa8 md5: a8ac9a6342569d1714ae1b53ae2fcadb depends: - __glibc >=2.17,<3.0.a0 - icu >=75.1,<76.0a0 - krb5 >=1.21.3,<1.22.0a0 - libgcc >=14 - openldap >=2.6.10,<2.7.0a0 - openssl >=3.5.4,<4.0a0 license: PostgreSQL purls: [] size: 2711480 timestamp: 1764345810429 - 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.31.1-h49aed37_4.conda sha256: 0ef142ac31e6fd59b4af89ac800acb6deb3fbd9cc4ccf070c03cc2c784dc7296 md5: 07479fc04ba3ddd5d9f760ef1635cfa7 depends: - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20250512.1,<20250513.0a0 - libgcc >=14 - libstdcxx >=14 - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 4372578 timestamp: 1766316228461 - 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-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-5.28.3-h3bd63a1_1.conda sha256: f58a16b13ad53346903c833e266f83c3d770a43a432659b98710aed85ca885e7 md5: bdbfea4cf45ae36652c6bbcc2e7ebe91 depends: - __osx >=11.0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - libcxx >=18 - libzlib >=1.3.1,<2.0a0 license: BSD-3-Clause license_family: BSD purls: [] size: 2271580 timestamp: 1735576361997 - 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-h7b12aa8_0.conda sha256: eb5d5ef4d12cdf744e0f728b35bca910843c8cf1249f758cf15488ca04a21dbb md5: a30848ebf39327ea078cf26d114cff53 depends: - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20250512.1,<20250513.0a0 - libgcc >=14 - libstdcxx >=14 constrains: - re2 2025.11.05.* license: BSD-3-Clause license_family: BSD purls: [] size: 211099 timestamp: 1762397758105 - 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-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-2024.07.02-h07bc746_2.conda sha256: 112a73ad483353751d4c5d63648c69a4d6fcebf5e1b698a860a3f5124fc3db96 md5: 6b1e3624d3488016ca4f1ca0c412efaa depends: - __osx >=11.0 - libabseil * cxx17* - libabseil >=20240722.0,<20240723.0a0 - libcxx >=18 constrains: - re2 2024.07.02.* license: BSD-3-Clause license_family: BSD purls: [] size: 167155 timestamp: 1735541067807 - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.20-h4ab18f5_0.conda sha256: 0105bd108f19ea8e6a78d2d994a6d4a8db16d19a41212070d2d1d48a63c34161 md5: a587892d3c13b6621a6091be690dbca2 depends: - libgcc-ng >=12 license: ISC purls: [] size: 205978 timestamp: 1716828628198 - conda: https://conda.anaconda.org/conda-forge/osx-64/libsodium-1.0.20-hfdf4475_0.conda sha256: d3975cfe60e81072666da8c76b993af018cf2e73fe55acba2b5ba0928efaccf5 md5: 6af4b059e26492da6013e79cbcb4d069 depends: - __osx >=10.13 license: ISC purls: [] size: 210249 timestamp: 1716828641383 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.20-h99b78c6_0.conda sha256: fade8223e1e1004367d7101dd17261003b60aa576df6d7802191f8972f7470b1 md5: a7ce36e284c5faaf93c220dfc39e3abd depends: - __osx >=11.0 license: ISC purls: [] size: 164972 timestamp: 1716828607917 - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-h0c1763c_1.conda sha256: 5ef162b2a1390d1495a759734afe2312a358a58441cf8f378be651903646f3b7 md5: ad1fd565aff83b543d726382c0ab0af2 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libzlib >=1.3.1,<2.0a0 license: blessing purls: [] size: 940686 timestamp: 1766319628770 - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.51.1-hf4e2dac_1.conda sha256: d614540c55f22ad555633f75e174089018ddfc65c49f447f7bbdbc3c3013bec1 md5: b1f35e70f047918b49fb4b181e40300e depends: - __glibc >=2.17,<3.0.a0 - icu >=78.1,<79.0a0 - libgcc >=14 - libzlib >=1.3.1,<2.0a0 license: blessing purls: [] size: 943451 timestamp: 1766319676469 - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.51.1-hb99441e_1.conda sha256: d62dcce2ecf555864db393fa0fdc0492f9f644c0435f516d0ba4c5f9f934234b md5: ec7a2bad1b422f3966e4776442adb05c depends: - __osx >=10.13 - libzlib >=1.3.1,<2.0a0 license: blessing purls: [] size: 987257 timestamp: 1766319833399 - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.51.1-hd09e2f1_1.conda sha256: 497b0a698ae87e024d24e242f93c56303731844d10861e1448f6d0a3d69c9ea7 md5: 75ba9aba95c277f12e23cdb0856fd9cd depends: - __osx >=10.13 - icu >=78.1,<79.0a0 - libzlib >=1.3.1,<2.0a0 license: blessing purls: [] size: 991497 timestamp: 1766319979749 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.51.1-h1b79a29_1.conda sha256: f2c3cbf2ca7d697098964a748fbf19d6e4adcefa23844ec49f0166f1d36af83c md5: 8c3951797658e10b610929c3e57e9ad9 depends: - __osx >=11.0 - libzlib >=1.3.1,<2.0a0 license: blessing purls: [] size: 905861 timestamp: 1766319901587 - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.51.1-hf5d6505_1.conda sha256: d6d86715a1afe11f626b7509935e9d2e14a4946632c0ac474526e20fc6c55f99 md5: be65be5f758709fc01b01626152e96b0 depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: blessing purls: [] size: 1292859 timestamp: 1766319616777 - 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/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-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/linux-64/libstdcxx-15.2.0-h934c35e_16.conda sha256: 813427918316a00c904723f1dfc3da1bbc1974c5cfe1ed1e704c6f4e0798cbc6 md5: 68f68355000ec3f1d6f26ea13e8f525f depends: - __glibc >=2.17,<3.0.a0 - libgcc 15.2.0 he0feb66_16 constrains: - libstdcxx-ng ==15.2.0=*_16 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 5856456 timestamp: 1765256838573 - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-15.2.0-hdf11a46_16.conda sha256: 81f2f246c7533b41c5e0c274172d607829019621c4a0823b5c0b4a8c7028ee84 md5: 1b3152694d236cf233b76b8c56bf0eae depends: - libstdcxx 15.2.0 h934c35e_16 license: GPL-3.0-only WITH GCC-exception-3.1 license_family: GPL purls: [] size: 27300 timestamp: 1765256885128 - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_cc-2.19.1-cpu_hf7abd0a_2.conda sha256: 5b13b874a6f2e75373992f936fae2c87c51a0531136d402be93d0e0c7a8ca49a md5: 5b5911b4fab716ec45704f84f611d557 depends: - __glibc >=2.17,<3.0.a0 - _x86_64-microarch-level >=1 - flatbuffers >=25.2.10,<25.2.11.0a0 - giflib >=5.2.2,<5.3.0a0 - icu >=75.1,<76.0a0 - libabseil * cxx17* - libabseil >=20250512.1,<20250513.0a0 - libcurl >=8.17.0,<9.0a0 - libgcc >=14 - libgrpc >=1.73.1,<1.74.0a0 - libjpeg-turbo >=3.1.2,<4.0a0 - libml_dtypes-headers >=0.5.1,<0.6 - libpng >=1.6.51,<1.7.0a0 - libprotobuf >=6.31.1,<6.31.2.0a0 - libsqlite >=3.51.1,<4.0a0 - libstdcxx >=14 - libtensorflow_framework 2.19.1 cpu_h12ecb4a_2 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.4,<4.0a0 - snappy >=1.2.2,<1.3.0a0 license: Apache-2.0 license_family: Apache purls: [] size: 145175442 timestamp: 1764396561544 - 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-arm64/libtensorflow_cc-2.18.0-cpu_hf321e49_1.conda sha256: 6b96f28b9ac3c0224bbb3bfa20b214728d698774293c85253280bda3e314c6e5 md5: 282c90a2f61920045a68a2c336af3c51 depends: - __osx >=11.0 - 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_h2398287_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: 120088427 timestamp: 1754497895714 - conda: https://conda.anaconda.org/conda-forge/linux-64/libtensorflow_framework-2.19.1-cpu_h12ecb4a_2.conda sha256: 7ab669b19a40047c658ff332e5b0d177d9d34032abcdf6b9b8cda806fee70347 md5: 2e96c94fdeb028fb413b972913ee9c35 depends: - __glibc >=2.17,<3.0.a0 - _x86_64-microarch-level >=1 - giflib >=5.2.2,<5.3.0a0 - icu >=75.1,<76.0a0 - libabseil * cxx17* - libabseil >=20250512.1,<20250513.0a0 - libcurl >=8.17.0,<9.0a0 - libgcc >=14 - libgrpc >=1.73.1,<1.74.0a0 - libjpeg-turbo >=3.1.2,<4.0a0 - libpng >=1.6.51,<1.7.0a0 - libprotobuf >=6.31.1,<6.31.2.0a0 - libsqlite >=3.51.1,<4.0a0 - libstdcxx >=14 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.4,<4.0a0 - snappy >=1.2.2,<1.3.0a0 license: Apache-2.0 license_family: Apache purls: [] size: 9265067 timestamp: 1764396133215 - 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-arm64/libtensorflow_framework-2.18.0-cpu_h2398287_1.conda sha256: ffe63dcf069914d686dd8a6c7a0586f8b46b3ed84d219cefb89a5a58c2ed021f md5: 3ebee6d8f4550dd64cc76fcefbd8b3a9 depends: - __osx >=11.0 - 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: 7683489 timestamp: 1754497703678 - 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/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-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/linux-64/libuuid-2.41.3-h5347b49_0.conda sha256: 1a7539cfa7df00714e8943e18de0b06cceef6778e420a5ee3a2a145773758aee md5: db409b7c1720428638e7c0d509d3e1b5 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 license: BSD-3-Clause purls: [] size: 40311 timestamp: 1766271528534 - conda: https://conda.anaconda.org/conda-forge/linux-64/libvulkan-loader-1.4.328.1-h5279c79_0.conda sha256: bbabc5c48b63ff03f440940a11d4648296f5af81bb7630d98485405cd32ac1ce md5: 372a62464d47d9e966b630ffae3abe73 depends: - __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - libgcc >=14 - xorg-libx11 >=1.8.12,<2.0a0 - xorg-libxrandr >=1.5.4,<2.0a0 constrains: - libvulkan-headers 1.4.328.1.* license: Apache-2.0 license_family: APACHE purls: [] size: 197672 timestamp: 1759972155030 - 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/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-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/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/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/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-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/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.1-hca5e8e5_0.conda sha256: d2195b5fbcb0af1ff7b345efdf89290c279b8d1d74f325ae0ac98148c375863c md5: 2bca1fbb221d9c3c8e3a155784bbc2e9 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: 837922 timestamp: 1764794163823 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.15.1-h26afc86_0.conda sha256: ec0735ae56c3549149eebd7dc22c0bed91fd50c02eaa77ff418613ddda190aa8 md5: e512be7dc1f84966d50959e900ca121f depends: - __glibc >=2.17,<3.0.a0 - icu >=75.1,<76.0a0 - libgcc >=14 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.1,<6.0a0 - libxml2-16 2.15.1 ha9997c6_0 - libzlib >=1.3.1,<2.0a0 license: MIT license_family: MIT purls: [] size: 45283 timestamp: 1761015644057 - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-2.15.1-h779ef1b_1.conda sha256: 8b47d5fb00a6ccc0f495d16787ab5f37a434d51965584d6000966252efecf56d md5: 68dc154b8d415176c07b6995bd3a65d9 depends: - icu >=78.1,<79.0a0 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.1,<6.0a0 - libxml2-16 2.15.1 h3cfd58e_1 - libzlib >=1.3.1,<2.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: MIT license_family: MIT purls: [] size: 43387 timestamp: 1766327259710 - conda: https://conda.anaconda.org/conda-forge/linux-64/libxml2-16-2.15.1-ha9997c6_0.conda sha256: 71436e72a286ef8b57d6f4287626ff91991eb03c7bdbe835280521791efd1434 md5: e7733bc6785ec009e47a224a71917e84 depends: - __glibc >=2.17,<3.0.a0 - icu >=75.1,<76.0a0 - libgcc >=14 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.1,<6.0a0 - libzlib >=1.3.1,<2.0a0 constrains: - libxml2 2.15.1 license: MIT license_family: MIT purls: [] size: 556302 timestamp: 1761015637262 - conda: https://conda.anaconda.org/conda-forge/win-64/libxml2-16-2.15.1-h3cfd58e_1.conda sha256: a857e941156b7f462063e34e086d212c6ccbc1521ebdf75b9ed66bd90add57dc md5: 07d73826fde28e7dbaec52a3297d7d26 depends: - icu >=78.1,<79.0a0 - libiconv >=1.18,<2.0a0 - liblzma >=5.8.1,<6.0a0 - libzlib >=1.3.1,<2.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - libxml2 2.15.1 license: MIT license_family: MIT purls: [] size: 518964 timestamp: 1766327232819 - 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.1-hb9d3cd8_2.conda sha256: d4bfe88d7cb447768e31650f06257995601f89076080e76df55e3112d4e47dc4 md5: edb0dca6bc32e4f4789199455a1dbeb8 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 constrains: - zlib 1.3.1 *_2 license: Zlib license_family: Other purls: [] size: 60963 timestamp: 1727963148474 - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda sha256: 8412f96504fc5993a63edf1e211d042a1fd5b1d51dedec755d2058948fcced09 md5: 003a54a4e32b02f7355b50a837e699da depends: - __osx >=10.13 constrains: - zlib 1.3.1 *_2 license: Zlib license_family: Other purls: [] size: 57133 timestamp: 1727963183990 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda sha256: ce34669eadaba351cd54910743e6a2261b67009624dbc7daeeafdef93616711b md5: 369964e85dc26bfe78f41399b366c435 depends: - __osx >=11.0 constrains: - zlib 1.3.1 *_2 license: Zlib license_family: Other purls: [] size: 46438 timestamp: 1727963202283 - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda sha256: ba945c6493449bed0e6e29883c4943817f7c79cbff52b83360f7b341277c6402 md5: 41fbfac52c601159df6c01f875de31b9 depends: - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 constrains: - zlib 1.3.1 *_2 license: Zlib license_family: Other purls: [] size: 55476 timestamp: 1727963768015 - conda: https://conda.anaconda.org/conda-forge/osx-64/llvm-openmp-21.1.8-h472b3d1_0.conda sha256: 2a41885f44cbc1546ff26369924b981efa37a29d20dc5445b64539ba240739e6 md5: e2d811e9f464dd67398b4ce1f9c7c872 depends: - __osx >=10.13 constrains: - openmp 21.1.8|21.1.8.* - intel-openmp <0.0a0 license: Apache-2.0 WITH LLVM-exception license_family: APACHE purls: [] size: 311405 timestamp: 1765965194247 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/llvm-openmp-21.1.8-h4a912ad_0.conda sha256: 56bcd20a0a44ddd143b6ce605700fdf876bcf5c509adc50bf27e76673407a070 md5: 206ad2df1b5550526e386087bef543c7 depends: - __osx >=11.0 constrains: - openmp 21.1.8|21.1.8.* - intel-openmp <0.0a0 license: Apache-2.0 WITH LLVM-exception license_family: APACHE purls: [] size: 285974 timestamp: 1765964756583 - conda: https://conda.anaconda.org/conda-forge/win-64/llvm-openmp-21.1.8-h4fa8253_0.conda sha256: 145c4370abe870f10987efa9fc15a8383f1dab09abbc9ad4ff15a55d45658f7b md5: 0d8b425ac862bcf17e4b28802c9351cb depends: - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - intel-openmp <0.0a0 - openmp 21.1.8|21.1.8.* license: Apache-2.0 WITH LLVM-exception license_family: APACHE purls: [] size: 347566 timestamp: 1765964942856 - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-3.10-pyhcf101f3_1.conda sha256: 32af5d32e3193b7c0ea02c33cc8753bfc0965d07e1aa58418a851d0bb94a7792 md5: 934afb77580165027b869d4104ee002f depends: - importlib-metadata >=4.4 - python >=3.10 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/markdown?source=hash-mapping size: 85401 timestamp: 1762856570927 - conda: https://conda.anaconda.org/conda-forge/noarch/markdown-it-py-4.0.0-pyhd8ed1ab_0.conda sha256: 7b1da4b5c40385791dbc3cc85ceea9fad5da680a27d5d3cb8bfaa185e304a89e md5: 5b5203189eb668f042ac2b0826244964 depends: - mdurl >=0.1,<1 - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/markdown-it-py?source=hash-mapping size: 64736 timestamp: 1754951288511 - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py310h3406613_0.conda sha256: b3894b37cab530d1adab5b9ce39a1b9f28040403cc0042b77e04a2f227a447de md5: 8854df4fb4e37cc3ea0a024e48c9c180 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: 23673 timestamp: 1759055396627 - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py311h3778330_0.conda sha256: 66c072c37aefa046f3fd4ca69978429421ef9e8a8572e19de534272a6482e997 md5: 0954f1a6a26df4a510b54f73b2a0345c 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: 26016 timestamp: 1759055312513 - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_0.conda sha256: f77f9f1a4da45cbc8792d16b41b6f169f649651a68afdc10b2da9da12b9aa42b md5: f775a43412f7f3d7ed218113ad233869 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: 25321 timestamp: 1759055268795 - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py310hd951482_0.conda sha256: 65f5d2362d2e2a9315f4e494b7199dffe151e7852e1a4da04da4c5738060cadb md5: 75b267b39ca96ef05de0ae6f2611c74a depends: - __osx >=10.13 - 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: 23003 timestamp: 1759055553623 - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py311he13f9b5_0.conda sha256: 28c82f7087027a72989cd030d1bb75da289da07ca2a17fe8db1d495fd6ee01f1 md5: 37b12b2523c1ef48318330b33410567b depends: - __osx >=10.13 - 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: 25452 timestamp: 1759055544260 - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py312hacf3034_0.conda sha256: e50fa11ea301d42fe64e587e2262f6afbe2ec42afe95e3ad4ccba06910b63155 md5: 2e6f78b0281181edc92337aa12b96242 depends: - __osx >=10.13 - 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: 24541 timestamp: 1759055509267 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py310hf4fd40f_0.conda sha256: fe90edbce0137081fb6f7c14ef56b9954628abb6f52882011f8cd5d44425fc37 md5: cd0fbf3b6ffdda2958e4b720f03429ba 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: 23707 timestamp: 1759055558733 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py311ha9b3269_0.conda sha256: c6b20ca60d739f78525dff778292f7011454befda2cc3e1a725ded897fbf9b33 md5: df124303925c7ad5d7eb15179d38c4e3 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: 26326 timestamp: 1759055494628 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h5748b74_0.conda sha256: b6aadcee6a0b814a0cb721e90575cbbe911b17ec46542460a9416ed2ec1a568e md5: 82221456841d3014a175199e4792465b 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: 25121 timestamp: 1759055677633 - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.8-py311h38be061_0.conda sha256: ead3fed3b8709abaf25ac8995ff748ecbbdbfe0f097181754e542ec9dda680c9 md5: 08b5a4eac150c688c9f924bcb3317e02 depends: - matplotlib-base >=3.10.8,<3.10.9.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: 17484 timestamp: 1763055534609 - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.10.8-py312h7900ff3_0.conda sha256: 6d66175e1a4ffb91ed954e2c11066d2e03a05bce951a808275069836ddfc993e md5: 2a7663896e5aab10b60833a768c4c272 depends: - matplotlib-base >=3.10.8,<3.10.9.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: 17415 timestamp: 1763055550515 - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.10.8-py311h6eed73b_0.conda sha256: afc86d58dffdf1bdc49beabb3fe98a97cf59001c3ed1994c36b23dad4a117cc7 md5: 6f7d2d63ebb0b08a3d7c4dcfed613523 depends: - matplotlib-base >=3.10.8,<3.10.9.0a0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - tornado >=5 license: PSF-2.0 license_family: PSF purls: [] size: 17477 timestamp: 1763055974448 - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-3.10.8-py312hb401068_0.conda sha256: a42cff0706c6e4d43234b9dc366f9d9b99555cee5c259969978e8741faf335db md5: c2a15b38125fe68d31901e7fa63ca049 depends: - matplotlib-base >=3.10.8,<3.10.9.0a0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - tornado >=5 license: PSF-2.0 license_family: PSF purls: [] size: 17476 timestamp: 1763055659354 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.8-py311ha1ab1f8_0.conda sha256: 1ae6c55a2b0149308ba8164fc96074929a18b97a4425af129f95360a71cac415 md5: 7e75ce08514ea74dacd32cbc92dab00c depends: - matplotlib-base >=3.10.8,<3.10.9.0a0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - tornado >=5 license: PSF-2.0 license_family: PSF purls: [] size: 17545 timestamp: 1763055695555 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-3.10.8-py312h1f38498_0.conda sha256: e3e8448b10273807bf1aa9b1aa6a4ee3a686ccfd0c296560b51b1d1581bb42ae md5: 534ed7eb4471c088285fdb382805e6ef depends: - matplotlib-base >=3.10.8,<3.10.9.0a0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - tornado >=5 license: PSF-2.0 license_family: PSF purls: [] size: 17526 timestamp: 1763060540928 - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.8-py311h0f3be63_0.conda sha256: 300bbdb9c90cc1332cb72bc79baf25fa58fd78e0c16f4698ad719b206e42ee1b md5: 21a0139015232dc0edbf6c2179b5ec24 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.1 - libfreetype6 >=2.14.1 - 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: 8298261 timestamp: 1763055503500 - conda: https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.10.8-py312he3d6523_0.conda sha256: 70cf0e7bfd50ef50eb712a6ca1eef0ef0d63b7884292acc81353327b434b548c md5: b8dc157bbbb69c1407478feede8b7b42 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.1 - libfreetype6 >=2.14.1 - 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: 8442149 timestamp: 1763055517581 - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.10.8-py311h48d7e91_0.conda sha256: f92ea9e3ba1cbf51d93aacf29deee82d01a201c086178554fceea535387211e3 md5: 358e04ffb2ddef9528397d1c65e7a634 depends: - __osx >=10.13 - contourpy >=1.0.1 - cycler >=0.10 - fonttools >=4.22.0 - freetype - kiwisolver >=1.3.1 - libcxx >=19 - libfreetype >=2.14.1 - libfreetype6 >=2.14.1 - 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: 8169963 timestamp: 1763055894833 - conda: https://conda.anaconda.org/conda-forge/osx-64/matplotlib-base-3.10.8-py312h7894933_0.conda sha256: 2ce31cad23d5d5fc16ca9d25f47dcfc52e93f2a0c6e1dc6db28e583c42f88bdc md5: 853618b60fdd11a6c3dbaadaa413407c depends: - __osx >=10.13 - contourpy >=1.0.1 - cycler >=0.10 - fonttools >=4.22.0 - freetype - kiwisolver >=1.3.1 - libcxx >=19 - libfreetype >=2.14.1 - libfreetype6 >=2.14.1 - 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: 8295843 timestamp: 1763055621386 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.8-py311h29553df_0.conda sha256: 393b3ad223fa3504feefb2662bbc1331875cf2b886cc3aba33517c6c60929b8d md5: dd155e8d4eeba4e1e096947abc996ddd 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.1 - libfreetype6 >=2.14.1 - 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: 8264527 timestamp: 1763055652980 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/matplotlib-base-3.10.8-py312h605b88b_0.conda sha256: 3c96c85dd723a4c16fce4446d1f0dc7d64e46b6ae4629c66d65984b8593ee999 md5: fbc4f90b3d63ea4e6c30f7733a0b5bfd 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.1 - libfreetype6 >=2.14.1 - 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: 8243636 timestamp: 1763060482877 - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.1-pyhd8ed1ab_0.conda sha256: 9d690334de0cd1d22c51bc28420663f4277cfa60d34fa5cad1ce284a13f1d603 md5: 00e120ce3e40bad7bfc78861ce3c4a25 depends: - python >=3.10 - traitlets license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/matplotlib-inline?source=compressed-mapping size: 15175 timestamp: 1761214578417 - 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.1.4-pyhcf101f3_0.conda sha256: 609ea628ace5c6cdbdce772704e6cb159ead26969bb2f386ca1757632b0f74c6 md5: f5a4d548d1d3bdd517260409fc21e205 depends: - python >=3.10 - typing_extensions - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/mistune?source=hash-mapping size: 72996 timestamp: 1756495311698 - conda: https://conda.anaconda.org/conda-forge/win-64/mkl-2025.3.0-hac47afa_454.conda sha256: 3c432e77720726c6bd83e9ee37ac8d0e3dd7c4cf9b4c5805e1d384025f9e9ab6 md5: c83ec81713512467dfe1b496a8292544 depends: - llvm-openmp >=21.1.4 - tbb >=2022.2.0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: LicenseRef-IntelSimplifiedSoftwareOct2022 license_family: Proprietary purls: [] size: 99909095 timestamp: 1761668703167 - 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_0.conda sha256: 5171b5c9201850e41841ddefbdd4fa4c87092bd166e9f728804c4f1865a44c3c md5: 081ed4a08a82c9b594bf2c17f18b457e depends: - python - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - libgcc >=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: 342726 timestamp: 1764068352219 - conda: https://conda.anaconda.org/conda-forge/linux-64/ml_dtypes-0.5.4-np2py311h912ec1f_0.conda sha256: 47461adaa90ecf9a22b231fba278dc5b7c9028ba8ab446a52e357e98526ed37f md5: efdaff95cef05cb02ff629bd7d17025a depends: - python - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - libgcc >=14 - 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: 345481 timestamp: 1764068374821 - conda: https://conda.anaconda.org/conda-forge/linux-64/ml_dtypes-0.5.4-np2py312h0f77346_0.conda sha256: a85c18c5526acce27758943a33d440bcb33aa73a31aae01cbb9b5a088ba4b963 md5: e595b02dca6bcc8257df8f94eb3d9903 depends: - python - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - libgcc >=14 - 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: 344917 timestamp: 1764068356648 - 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-np2py310hc20d22f_0.conda sha256: d714e9598a4d47a459081668b5cd5aa84c2d928818687a6cb28b80cf799b75bf md5: ed700ba4f3e1d764e36af8d162a76c1b depends: - python - __osx >=10.13 - libcxx >=19 - 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: 275379 timestamp: 1764068383461 - conda: https://conda.anaconda.org/conda-forge/osx-64/ml_dtypes-0.5.4-np2py311hd5df55d_0.conda sha256: 11ae1194e088671ac952eea434b9bd5cc17b67033cd4e2888fde657061be30aa md5: 8ed71486018b7bdeb715295463aaf60c 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: 278224 timestamp: 1764068370651 - 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.4.0-py311h9cb3ce9_2.conda sha256: fe4e2223aef3637eba3930252ec374f4caf81d41acb55b149a88b656f4d44e6a md5: c7facb1eaa9f3d7fe8b4216c2ad802ae depends: - __osx >=11.0 - libcxx >=17 - numpy >=1.19,<3 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 license: MPL-2.0 AND Apache-2.0 purls: - pkg:pypi/ml-dtypes?source=hash-mapping size: 126145 timestamp: 1725475284063 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ml_dtypes-0.4.0-py312hcd31e36_2.conda sha256: 3931aef8f1e04f2fd9cff55a0c8dd76f818a3eb4fad5ef6cfd83649d14a663e4 md5: 70b338acc912c1989a36ed8511f884a7 depends: - __osx >=11.0 - libcxx >=17 - numpy >=1.19,<3 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 license: MPL-2.0 AND Apache-2.0 purls: - pkg:pypi/ml-dtypes?source=hash-mapping size: 123771 timestamp: 1725475270272 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ml_dtypes-0.5.4-np2py310h9cec423_0.conda sha256: 1b569d904af7e210ea411dede7a6f0dfe462da6ccf4d513e92320f60053f58a2 md5: 9132cb0db46d9f0ed8dcc280e47b5796 depends: - python - __osx >=11.0 - python 3.10.* *_cpython - libcxx >=19 - 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: 238922 timestamp: 1764068369365 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ml_dtypes-0.5.4-np2py311h76ff34e_0.conda sha256: f2823ed696980fa2ea79a5a84356ffe6bff7ee9ba89e4777fafae7d02bf9d609 md5: 12463c221fbfe31460d1c6790a3a8607 depends: - python - libcxx >=19 - python 3.11.* *_cpython - __osx >=11.0 - 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: 241551 timestamp: 1764068369398 - conda: https://conda.anaconda.org/conda-forge/noarch/more-itertools-10.8.0-pyhcf101f3_1.conda sha256: 449609f0d250607a300754474350a3b61faf45da183d3071e9720e453c765b8a md5: 32f78e9d06e8593bc4bbf1338da06f5f depends: - python >=3.10 - python license: MIT license_family: MIT purls: - pkg:pypi/more-itertools?source=hash-mapping size: 69210 timestamp: 1764487059562 - 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/nbclient-0.10.2-pyhd8ed1ab_0.conda sha256: a20cff739d66c2f89f413e4ba4c6f6b59c50d5c30b5f0d840c13e8c9c2df9135 md5: 6bb0d77277061742744176ab555b723c depends: - jupyter_client >=6.1.12 - jupyter_core >=4.12,!=5.0.* - nbformat >=5.1 - python >=3.8 - traitlets >=5.4 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/nbclient?source=hash-mapping size: 28045 timestamp: 1734628936013 - conda: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.16.6-pyhcf101f3_1.conda sha256: 8f575e5c042b17f4677179a6ba474bdbe76573936d3d3e2aeb42b511b9cb1f3f md5: cfc86ccc3b1de35d36ccaae4c50391f5 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.16.6 *_1 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/nbconvert?source=compressed-mapping size: 199273 timestamp: 1760797634443 - 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/linux-64/ncurses-6.5-h2d0b736_3.conda sha256: 3fde293232fa3fca98635e1167de6b7c7fda83caf24b9d6c91ec9eefb4f4d586 md5: 47e340acb35de30501a76c7c799c41d7 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 license: X11 AND BSD-3-Clause purls: [] size: 891641 timestamp: 1738195959188 - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda sha256: ea4a5d27ded18443749aefa49dc79f6356da8506d508b5296f60b8d51e0c4bd9 md5: ced34dd9929f491ca6dab6a2927aff25 depends: - __osx >=10.13 license: X11 AND BSD-3-Clause purls: [] size: 822259 timestamp: 1738196181298 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda sha256: 2827ada40e8d9ca69a153a45f7fd14f32b2ead7045d3bbb5d10964898fe65733 md5: 068d497125e4bf8a66bf707254fff5ae depends: - __osx >=11.0 license: X11 AND BSD-3-Clause purls: [] size: 797030 timestamp: 1738196177597 - 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/linux-64/nh3-0.3.2-py310h1570de5_0.conda noarch: python sha256: f6095c759df15baa9cccc20394b21667f4d0440f3c432e07539c3b47ef195c0b md5: 383616287311316d120b028aac89f6f4 depends: - python - __glibc >=2.17,<3.0.a0 - libgcc >=14 - _python_abi3_support 1.* - cpython >=3.10 constrains: - __glibc >=2.17 license: MIT license_family: MIT purls: - pkg:pypi/nh3?source=hash-mapping size: 669207 timestamp: 1761831302988 - conda: https://conda.anaconda.org/conda-forge/osx-64/nh3-0.3.2-py310h6cf5e2e_0.conda noarch: python sha256: de37d8dfb3e9d94b9e90ed9eeadd93e861b1022daaf8542869d2ce2bfc9d7822 md5: d683cb6c8973a6b6e554768810710608 depends: - python - __osx >=10.13 - _python_abi3_support 1.* - cpython >=3.10 constrains: - __osx >=10.13 license: MIT license_family: MIT purls: - pkg:pypi/nh3?source=hash-mapping size: 651406 timestamp: 1761831393643 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/nh3-0.3.2-py310h06fc29a_0.conda noarch: python sha256: cee5e0acd60b4377cc5b66a7d4741fbe3dbd6613660d5fa10a7a92003710f405 md5: cdd83f3879f87dab2b214cd3db09b71b 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: 624675 timestamp: 1761831518941 - 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/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.3.5-py311h2e04523_0.conda sha256: 62953ae2bd17bb7e9d29721879e76bfdaa5c725cc1e28b3840be2d454467432a md5: 01da92ddaf561cabebd06019ae521510 depends: - python - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - libgcc >=14 - python_abi 3.11.* *_cp311 - libcblas >=3.9.0,<4.0a0 - liblapack >=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: 9451141 timestamp: 1763351006818 - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py312h33ff503_0.conda sha256: 68b5dd7e4d12295c44130e3a777462dbc8886ca0a7d141f1ff5ab0375df5da30 md5: 1570db96376f9f01cf495afe203672e5 depends: - python - libgcc >=14 - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - libcblas >=3.9.0,<4.0a0 - libblas >=3.9.0,<4.0a0 - python_abi 3.12.* *_cp312 - 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: 8820654 timestamp: 1763351074641 - conda: https://conda.anaconda.org/conda-forge/linux-64/numpy-2.3.5-py314h2b28147_0.conda sha256: 4fa3b8b80dd848a70f679b31d74d6fb28f9c4de9cd81086aa8e10256e9de20d1 md5: 6d2cff81447b8fe424645d7dd3bde8bf depends: - python - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 - libcblas >=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: 8983459 timestamp: 1763350996398 - 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.3.5-py311hf157cb9_0.conda sha256: 9682437ae9c98095e3ae67ba8781158ae2b83059ab3927ec6c5acfb8882827fa md5: 8679a497f58ff931c4f95143da0506e8 depends: - python - libcxx >=19 - __osx >=10.13 - libblas >=3.9.0,<4.0a0 - python_abi 3.11.* *_cp311 - 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: 8596533 timestamp: 1763350979305 - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.3.5-py312ha3982b3_0.conda sha256: 62c2a6fb30fec82f8d46defcf33c94a04d5c890ce02b3ddeeda3263f9043688c md5: 6941ace329a1f088d1b3b399369aecec depends: - python - libcxx >=19 - __osx >=10.13 - 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=hash-mapping size: 7992092 timestamp: 1763350891083 - conda: https://conda.anaconda.org/conda-forge/osx-64/numpy-2.3.5-py314hf08249b_0.conda sha256: 77e0b2ddb433ac23ca9d587c37a8f6da9baee3888c34d19e530fe8cbaaf49bdc md5: 5c9e4bc0c170115fd3602d7377c9e8da depends: - python - libcxx >=19 - __osx >=10.13 - libcblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 - python_abi 3.14.* *_cp314 - 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: 8140127 timestamp: 1763350902772 - 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.3.5-py311h8685306_0.conda sha256: 0d1e143adbaca3c8c7698434c09e0656f65677b579d0f8f41778abff9089f81a md5: 1c90d71be9d263f263ae14e7552a6293 depends: - python - libcxx >=19 - python 3.11.* *_cpython - __osx >=11.0 - liblapack >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - libblas >=3.9.0,<4.0a0 - python_abi 3.11.* *_cp311 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=compressed-mapping size: 7321322 timestamp: 1763350912784 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py312h85ea64e_0.conda sha256: 095dc7f15d2f8d9970a6a4e9d4a1980989a4209cd34c2b756fbd40e71f6990cc md5: ee4c185ae9c1edeb8e8cd26273c90a9a depends: - python - __osx >=11.0 - python 3.12.* *_cpython - libcxx >=19 - libcblas >=3.9.0,<4.0a0 - libblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 - python_abi 3.12.* *_cp312 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=compressed-mapping size: 6704341 timestamp: 1763350985482 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/numpy-2.3.5-py314h5b5928d_0.conda sha256: a8731e3e31013be69cb585dbc57cb225437bb0c945ddce9a550c1cd10b6fad37 md5: e126981f973ddc2510d7a249c5b69533 depends: - python - python 3.14.* *_cp314 - __osx >=11.0 - libcxx >=19 - libcblas >=3.9.0,<4.0a0 - libblas >=3.9.0,<4.0a0 - python_abi 3.14.* *_cp314 - 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: 6861174 timestamp: 1763350930747 - 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.3.5-py311h80b3fa1_0.conda sha256: 0b42280fe945637104798744ac7bb429d5a950af45c887520d4ced808c6a479c md5: 1e0fb210584b09130000c4404b77f0f6 depends: - python - 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 - libcblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 - libblas >=3.9.0,<4.0a0 - python_abi 3.11.* *_cp311 constrains: - numpy-base <0a0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/numpy?source=hash-mapping size: 8074590 timestamp: 1763350917989 - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.3.5-py312ha72d056_0.conda sha256: 1db03d0b892a196351544dabf8ac93a7f9f78dc85d3732de31ecb52c0da65d1b md5: 1c96af76fd575e8dcc48eea3e851579f depends: - python - 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 - libblas >=3.9.0,<4.0a0 - python_abi 3.12.* *_cp312 - 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: 7438208 timestamp: 1763350928802 - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.3.5-py314h06c3c77_0.conda sha256: e64d4c049c9c69ef02d924ac1750b32e08f57732cbc6a3fe11794f3169b59d14 md5: ddc6687a8f402695bd22229aaf69fb26 depends: - python - 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 - liblapack >=3.9.0,<4.0a0 - python_abi 3.14.* *_cp314 - 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: 7588219 timestamp: 1763350950306 - conda: https://conda.anaconda.org/conda-forge/win-64/numpy-2.3.5-py314hf08e5d2_0.conda sha256: 0f7ca0ac38f056771157f775284a7ed9bb143d624d2b7a66a598ad16f6e895ff md5: 0a3a6dd37d4df747c3ab804d3c20728f depends: - python - 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 - liblapack >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - python_abi 3.14.* *_cp314t - 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: 7741298 timestamp: 1763350915443 - 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/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/osx-64/openjpeg-2.5.4-h87e8dc5_0.conda sha256: fdf4708a4e45b5fd9868646dd0c0a78429f4c0b8be490196c975e06403a841d0 md5: a67d3517ebbf615b91ef9fdc99934e0c depends: - __osx >=10.13 - libcxx >=19 - libpng >=1.6.50,<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: 334875 timestamp: 1758489493148 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openjpeg-2.5.4-hbfb3c88_0.conda sha256: dd73e8f1da7dd6a5494c5586b835cbe2ec68bace55610b1c4bf927400fe9c0d7 md5: 6bf3d24692c157a41c01ce0bd17daeea depends: - __osx >=11.0 - libcxx >=19 - libpng >=1.6.50,<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: 319967 timestamp: 1758489514651 - conda: https://conda.anaconda.org/conda-forge/linux-64/openldap-2.6.10-he970967_0.conda sha256: cb0b07db15e303e6f0a19646807715d28f1264c6350309a559702f4f34f37892 md5: 2e5bf4f1da39c0b32778561c3c4e5878 depends: - __glibc >=2.17,<3.0.a0 - cyrus-sasl >=2.1.27,<3.0a0 - krb5 >=1.21.3,<1.22.0a0 - libgcc >=13 - libstdcxx >=13 - openssl >=3.5.0,<4.0a0 license: OLDAP-2.8 license_family: BSD purls: [] size: 780253 timestamp: 1748010165522 - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.6.0-h26f9b46_0.conda sha256: a47271202f4518a484956968335b2521409c8173e123ab381e775c358c67fe6d md5: 9ee58d5c534af06558933af3c845a780 depends: - __glibc >=2.17,<3.0.a0 - ca-certificates - libgcc >=14 license: Apache-2.0 license_family: Apache purls: [] size: 3165399 timestamp: 1762839186699 - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.6.0-h230baf5_0.conda sha256: 36fe9fb316be22fcfb46d5fa3e2e85eec5ef84f908b7745f68f768917235b2d5 md5: 3f50cdf9a97d0280655758b735781096 depends: - __osx >=10.13 - ca-certificates license: Apache-2.0 license_family: Apache purls: [] size: 2778996 timestamp: 1762840724922 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.6.0-h5503f6c_0.conda sha256: ebe93dafcc09e099782fe3907485d4e1671296bc14f8c383cb6f3dfebb773988 md5: b34dc4172653c13dcf453862f251af2b depends: - __osx >=11.0 - ca-certificates license: Apache-2.0 license_family: Apache purls: [] size: 3108371 timestamp: 1762839712322 - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.6.0-h725018a_0.conda sha256: 6d72d6f766293d4f2aa60c28c244c8efed6946c430814175f959ffe8cab899b3 md5: 84f8fb4afd1157f59098f618cd2437e4 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: 9440812 timestamp: 1762841722179 - 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/linux-64/optree-0.18.0-py310h03d9f68_0.conda sha256: 12fbf35dc6a4b189f2a774be7f839f86a4adff60437281027443c3358f41a2fd md5: f3f25e4e121a4bc05edb5e08949f1126 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.12 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 408311 timestamp: 1763124404367 - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.18.0-py311hdf67eae_0.conda sha256: 04b3dcf0a10c536eb952ac95b40314a54062f0e90690053323a58ffd19184c1f md5: 7eb0268142bf6bea29a2573b10ae4d32 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.12 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 441668 timestamp: 1763124461030 - conda: https://conda.anaconda.org/conda-forge/linux-64/optree-0.18.0-py312hd9148b4_0.conda sha256: f34a33825d0e925c6b0e09c81632657935e2c0cc595d2a70d9902c7b9f891a51 md5: 4d4148297810361256ebf85b17693dff 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.12 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 444884 timestamp: 1763124490090 - conda: https://conda.anaconda.org/conda-forge/osx-64/optree-0.18.0-py310h8cf47bc_0.conda sha256: 30a0c11931073d94404adcf90b3638c8ae77ae3cad3a6011f2ab8a7a9cbc4f20 md5: bf5e6d46a73c64e26c60658dc110f7b8 depends: - __osx >=10.13 - libcxx >=19 - python >=3.10,<3.11.0a0 - python_abi 3.10.* *_cp310 - typing-extensions >=4.12 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 381534 timestamp: 1763124794961 - conda: https://conda.anaconda.org/conda-forge/osx-64/optree-0.18.0-py311haec20ae_0.conda sha256: fe6dc3cfcc4b4554bbc9b3b5f43a7ea3b82e23aeb9c2578593afc42c59c4b4b3 md5: da8b87e0d382db72485b56b7dce1561b depends: - __osx >=10.13 - libcxx >=19 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - typing-extensions >=4.12 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 410448 timestamp: 1763124927112 - conda: https://conda.anaconda.org/conda-forge/osx-64/optree-0.18.0-py312hd099df3_0.conda sha256: a400df42b6dc1804742dc84a01663129681cdc7f686943648e08fd07f35191f7 md5: 6c8a92f3e8a1093a4d458053b4d6e555 depends: - __osx >=10.13 - libcxx >=19 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - typing-extensions >=4.12 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 413061 timestamp: 1763124876339 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.18.0-py310h0e897d2_0.conda sha256: c3fcbf2c0a9392fbcb860d9a54c8d98563c75540c0a36d640de57e86ee2441fa md5: c999f329cac20f7d847feeb3b883c628 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.12 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 363079 timestamp: 1763124856851 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.18.0-py311h5a5e7c7_0.conda sha256: 79a00c605a0a7186a006f2722d258e87da70a33acdf74b98c2f981a986d0a461 md5: c2e1c2dd8f7d25888660f6bd4ba92b69 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.12 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 388921 timestamp: 1763124959643 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/optree-0.18.0-py312h84eede6_0.conda sha256: 7497c12d56fde35509acae73f91eb602e8cf6abb4ff8483525684b76e8b01f10 md5: 5e75b9a5f0eeb12b8bd45446ed3b1d1e 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.12 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/optree?source=hash-mapping size: 390301 timestamp: 1763124958546 - 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-25.0-pyh29332c3_1.conda sha256: 289861ed0c13a15d7bbb408796af4de72c2fe67e2bcb0de98f4c3fce259d7991 md5: 58335b26c38bf4a20f399384c33cbcf9 depends: - python >=3.8 - python license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/packaging?source=hash-mapping size: 62477 timestamp: 1745345660407 - 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/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-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=compressed-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=compressed-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/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/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.5-pyhcf101f3_0.conda sha256: 30de7b4d15fbe53ffe052feccde31223a236dae0495bab54ab2479de30b2990f md5: a110716cdb11cf51482ff4000dc253d7 depends: - python >=3.10 - python license: MIT license_family: MIT purls: - pkg:pypi/parso?source=hash-mapping size: 81562 timestamp: 1755974222274 - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-0.12.1-pyhd8ed1ab_1.conda sha256: 9f64009cdf5b8e529995f18e03665b03f5d07c0b17445b8badef45bde76249ee md5: 617f15191456cc6a13db418a275435e5 depends: - python >=3.9 license: MPL-2.0 license_family: MOZILLA purls: - pkg:pypi/pathspec?source=hash-mapping size: 41075 timestamp: 1733233471940 - 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/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/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/linux-64/pillow-12.0.0-py311hf88fc01_2.conda sha256: adbedbd40e21eb4f923d1dc01316454cf7527338eef3fe71f80a8985966003e6 md5: 79edb22fb652ee142099df18042ca8c0 depends: - python - libgcc >=14 - __glibc >=2.17,<3.0.a0 - libfreetype >=2.14.1 - libfreetype6 >=2.14.1 - python_abi 3.11.* *_cp311 - zlib-ng >=2.3.1,<2.4.0a0 - libjpeg-turbo >=3.1.2,<4.0a0 - libtiff >=4.7.1,<4.8.0a0 - lcms2 >=2.17,<3.0a0 - openjpeg >=2.5.4,<3.0a0 - libxcb >=1.17.0,<2.0a0 - libwebp-base >=1.6.0,<2.0a0 - tk >=8.6.13,<8.7.0a0 license: HPND purls: - pkg:pypi/pillow?source=hash-mapping size: 1044779 timestamp: 1764330106862 - conda: https://conda.anaconda.org/conda-forge/linux-64/pillow-12.0.0-py312h50c33e8_2.conda sha256: 58c4589d7dc2d2bf66fc57fc21abd43ca85b23d14b24466d8e8bef60ca51185b md5: f2aef8ecea68f4d35330f0c48949bff2 depends: - python - __glibc >=2.17,<3.0.a0 - libgcc >=14 - openjpeg >=2.5.4,<3.0a0 - lcms2 >=2.17,<3.0a0 - libjpeg-turbo >=3.1.2,<4.0a0 - libwebp-base >=1.6.0,<2.0a0 - libtiff >=4.7.1,<4.8.0a0 - python_abi 3.12.* *_cp312 - libxcb >=1.17.0,<2.0a0 - libfreetype >=2.14.1 - libfreetype6 >=2.14.1 - tk >=8.6.13,<8.7.0a0 - zlib-ng >=2.3.1,<2.4.0a0 license: HPND purls: - pkg:pypi/pillow?source=compressed-mapping size: 1028596 timestamp: 1764330106863 - conda: https://conda.anaconda.org/conda-forge/osx-64/pillow-12.0.0-py311h56465c8_2.conda sha256: dcc53d9bf01feb6331b3b76a99605c04e58370d0fcdedbd3c6b15ba06f1cf78a md5: 5d4ebf4b8797461dd7061a217ace76fa depends: - python - __osx >=10.13 - libxcb >=1.17.0,<2.0a0 - libjpeg-turbo >=3.1.2,<4.0a0 - python_abi 3.11.* *_cp311 - libtiff >=4.7.1,<4.8.0a0 - openjpeg >=2.5.4,<3.0a0 - libfreetype >=2.14.1 - libfreetype6 >=2.14.1 - zlib-ng >=2.3.1,<2.4.0a0 - libwebp-base >=1.6.0,<2.0a0 - lcms2 >=2.17,<3.0a0 - tk >=8.6.13,<8.7.0a0 license: HPND purls: - pkg:pypi/pillow?source=hash-mapping size: 976184 timestamp: 1764330318998 - conda: https://conda.anaconda.org/conda-forge/osx-64/pillow-12.0.0-py312hea0c9db_2.conda sha256: 8c2fc5ff5d9b6d9e285ef217e78d90820d507c98b961256dd410f48307360754 md5: 1d9e77d994f7593d52f6f42ec2712b4d depends: - python - __osx >=10.13 - tk >=8.6.13,<8.7.0a0 - lcms2 >=2.17,<3.0a0 - python_abi 3.12.* *_cp312 - libjpeg-turbo >=3.1.2,<4.0a0 - libxcb >=1.17.0,<2.0a0 - openjpeg >=2.5.4,<3.0a0 - zlib-ng >=2.3.1,<2.4.0a0 - libwebp-base >=1.6.0,<2.0a0 - libfreetype >=2.14.1 - libfreetype6 >=2.14.1 - libtiff >=4.7.1,<4.8.0a0 license: HPND purls: - pkg:pypi/pillow?source=hash-mapping size: 961639 timestamp: 1764330318999 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.0.0-py311hb52a7e5_2.conda sha256: c1f1805f05494b1e944ba6b54c6d952e3a78ef2751c290d6c187477890604eab md5: b2febbc4b15932546175af55001110f4 depends: - python - python 3.11.* *_cpython - __osx >=11.0 - libtiff >=4.7.1,<4.8.0a0 - openjpeg >=2.5.4,<3.0a0 - zlib-ng >=2.3.1,<2.4.0a0 - python_abi 3.11.* *_cp311 - libwebp-base >=1.6.0,<2.0a0 - libxcb >=1.17.0,<2.0a0 - libjpeg-turbo >=3.1.2,<4.0a0 - tk >=8.6.13,<8.7.0a0 - libfreetype >=2.14.1 - libfreetype6 >=2.14.1 - lcms2 >=2.17,<3.0a0 license: HPND purls: - pkg:pypi/pillow?source=hash-mapping size: 967111 timestamp: 1764330196014 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pillow-12.0.0-py312h95c711c_2.conda sha256: b720df83d27af31466c77554b95a78fa03e458810537570fb05850a119667c07 md5: 817cd66153338f403cf05d8a09d93fad depends: - python - python 3.12.* *_cpython - __osx >=11.0 - libjpeg-turbo >=3.1.2,<4.0a0 - libxcb >=1.17.0,<2.0a0 - libfreetype >=2.14.1 - libfreetype6 >=2.14.1 - zlib-ng >=2.3.1,<2.4.0a0 - libtiff >=4.7.1,<4.8.0a0 - libwebp-base >=1.6.0,<2.0a0 - tk >=8.6.13,<8.7.0a0 - lcms2 >=2.17,<3.0a0 - openjpeg >=2.5.4,<3.0a0 - python_abi 3.12.* *_cp312 license: HPND purls: - pkg:pypi/pillow?source=hash-mapping size: 950740 timestamp: 1764330196015 - conda: https://conda.anaconda.org/conda-forge/noarch/pip-25.3-pyh8b19718_0.conda sha256: b67692da1c0084516ac1c9ada4d55eaf3c5891b54980f30f3f444541c2706f1e md5: c55515ca43c6444d2572e0f0d93cb6b9 depends: - python >=3.10,<3.13.0a0 - setuptools - wheel license: MIT license_family: MIT purls: - pkg:pypi/pip?source=compressed-mapping size: 1177534 timestamp: 1762776258783 - 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/noarch/platformdirs-4.5.1-pyhcf101f3_0.conda sha256: 04c64fb78c520e5c396b6e07bc9082735a5cc28175dbe23138201d0a9441800b md5: 1bd2e65c8c7ef24f4639ae6e850dacc2 depends: - python >=3.10 - python license: MIT license_family: MIT purls: - pkg:pypi/platformdirs?source=hash-mapping size: 23922 timestamp: 1764950726246 - 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=compressed-mapping size: 25877 timestamp: 1764896838868 - conda: https://conda.anaconda.org/conda-forge/noarch/pre-commit-4.5.1-pyha770c72_0.conda sha256: 5b81b7516d4baf43d0c185896b245fa7384b25dc5615e7baa504b7fa4e07b706 md5: 7f3ac694319c7eaf81a0325d6405e974 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=compressed-mapping size: 200827 timestamp: 1765937577534 - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.23.1-pyhd8ed1ab_0.conda sha256: 13dc67de68db151ff909f2c1d2486fa7e2d51355b25cee08d26ede1b62d48d40 md5: a1e91db2d17fd258c64921cb38e6745a depends: - python >=3.10 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/prometheus-client?source=hash-mapping size: 54592 timestamp: 1758278323953 - 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/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.31.1-py311h425ed32_2.conda sha256: f5216cb89239542d39b9dfc9a757157f8c779e88a769c165e275da035b38cd02 md5: 28ef5e67a2544510913d04a4a6dd9e12 depends: - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20250512.1,<20250513.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.31.1 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/protobuf?source=hash-mapping size: 486563 timestamp: 1760393355981 - conda: https://conda.anaconda.org/conda-forge/linux-64/protobuf-6.31.1-py312hb8af0ac_2.conda sha256: 9c1dffa6371b5ae5a7659f08fa075a1a9d7b32fd11d5eaa1e7192eba4cae1207 md5: 2aaf8d6c729beb30d1b41964e7fb2cd6 depends: - __glibc >=2.17,<3.0.a0 - libabseil * cxx17* - libabseil >=20250512.1,<20250513.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.31.1 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/protobuf?source=hash-mapping size: 479025 timestamp: 1760393393854 - 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-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-5.28.3-py311h155a34a_0.conda sha256: 18a1b3e59b76c27b03318818e85f7a66b035de77c6b32f077e4af72efbc12269 md5: ab0b501f96671046b577316280ddb72b depends: - __osx >=11.0 - libcxx >=18 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - 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: 457403 timestamp: 1731367189837 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/protobuf-5.28.3-py312hd8f9ff3_0.conda sha256: 9d572a97419bdace14d7c7cc8cc8c4bf2dcb22b56965dac87a27fbdb5061b926 md5: 5afbe52a59f04dd1fe566d0d17590d7e depends: - __osx >=11.0 - libcxx >=18 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - 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: 448803 timestamp: 1731367010746 - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.3-py311haee01d2_0.conda sha256: 6a0b791e00368b6b635c65d5fb31d385129da790d21923387c6b546230ffdf14 md5: 2092b7977bc8e05eb17a1048724593a4 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: 513789 timestamp: 1762092898190 - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.1.3-py312h5253ce2_0.conda sha256: 1b679202ebccf47be64509a4fc2a438a66229403257630621651b2886b882597 md5: 82ce56c5a4a55165aed95e04923ab363 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: 495011 timestamp: 1762092914381 - conda: https://conda.anaconda.org/conda-forge/osx-64/psutil-7.1.3-py311h62e9434_0.conda sha256: 2b16aa4a8d1df8810129d3edbe74d760c826dc998ee65a4b0db3957fc7d97374 md5: 76219ffe585f6877d15dbbd28c5a93c4 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: 524561 timestamp: 1762092999229 - conda: https://conda.anaconda.org/conda-forge/osx-64/psutil-7.1.3-py312h01f6755_0.conda sha256: 053018613cabc02e87252104a597fc469ebf6af210ae1d24e9855fa5ac419205 md5: 9587fcc6d21e10f59b708690399c5a66 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: 505701 timestamp: 1762093032445 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.1.3-py311h5bb9006_0.conda sha256: ea916cf3e0ac41d9eb6d644b8317b2cf4878d974339e56ab081cc2544315d4d1 md5: cf10a93bacbb33e52075e536051efe97 depends: - python - __osx >=11.0 - python 3.11.* *_cpython - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/psutil?source=hash-mapping size: 526877 timestamp: 1762093036674 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.1.3-py312h37e1c23_0.conda sha256: cd831dfe655fdb581e1c2c71fa072d2fce38538474a36cbde3ae2dd910a2ae76 md5: d0b2f83de57eafaa6d7700b589c66096 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: 508014 timestamp: 1762093047823 - 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/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-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/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.25.1-pyhd8ed1ab_0.conda sha256: 3053895e08ce56923e48eea7d1c07a6d8bf09948d1e69a21ae7ab9e459b0a227 md5: 9c25a850410220d31085173fbfdfa191 depends: - importlib-metadata - latexcodec >=1.0.4 - python >=3.9 - pyyaml >=3.01 - setuptools license: MIT license_family: MIT purls: - pkg:pypi/pybtex?source=hash-mapping size: 73965 timestamp: 1751015096707 - 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-2.22-pyh29332c3_1.conda sha256: 79db7928d13fab2d892592223d7570f5061c192f27b9febd1a418427b719acc6 md5: 12c566707c80111f9799308d9e265aef depends: - python >=3.9 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pycparser?source=hash-mapping size: 110100 timestamp: 1733195786147 - conda: https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.16.1-pyhd8ed1ab_0.conda sha256: 073473ba9c0cc3946026dde9112d2edb0ac52f6cc35d2126f4bff8bad1cc74a6 md5: 837aaf71ddf3b27acae0e7e9015eebc6 depends: - accessible-pygments - babel - beautifulsoup4 - docutils !=0.17.0 - pygments >=2.7 - python >=3.9 - sphinx >=6.1 - typing_extensions license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/pydata-sphinx-theme?source=hash-mapping size: 1547597 timestamp: 1734446468767 - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.19.2-pyhd8ed1ab_0.conda sha256: 5577623b9f6685ece2697c6eb7511b4c9ac5fb607c9babc2646c811b428fd46a md5: 6b6ece66ebcae2d5f326c77ef2c5a066 depends: - python >=3.9 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/pygments?source=hash-mapping size: 889287 timestamp: 1750615908735 - 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-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-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-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/noarch/pyparsing-3.2.5-pyhcf101f3_0.conda sha256: 6814b61b94e95ffc45ec539a6424d8447895fef75b0fec7e1be31f5beee883fb md5: 6c8979be6d7a17692793114fa26916e8 depends: - python >=3.10 - python license: MIT license_family: MIT purls: - pkg:pypi/pyparsing?source=hash-mapping size: 104044 timestamp: 1758436411254 - 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' - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.10.1-py311he4c1a5a_0.conda sha256: 4af7ffeb981b29973a00e6de42abea205fc62c537f53adfbfc43591173812b22 md5: 6b0c36cdc506dc560538fba50e43dd03 depends: - __glibc >=2.17,<3.0.a0 - libclang13 >=21.1.7 - libegl >=1.7.0,<2.0a0 - libgcc >=14 - libgl >=1.7.0,<2.0a0 - libopengl >=1.7.0,<2.0a0 - libstdcxx >=14 - libvulkan-loader >=1.4.328.1,<2.0a0 - libxml2 - libxml2-16 >=2.14.6 - libxslt >=1.1.43,<2.0a0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - qt6-main 6.10.1.* - qt6-main >=6.10.1,<6.11.0a0 license: LGPL-3.0-only license_family: LGPL purls: - pkg:pypi/pyside6?source=hash-mapping - pkg:pypi/shiboken6?source=hash-mapping size: 10414644 timestamp: 1765812025467 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyside6-6.10.1-py312h9da60e5_0.conda sha256: dccbc2674aaae31711933942fd16d87b127e6335556d5701cb760f27986f0375 md5: dda0a61b6186fc914cf6c1581f64229d depends: - __glibc >=2.17,<3.0.a0 - libclang13 >=21.1.7 - libegl >=1.7.0,<2.0a0 - libgcc >=14 - libgl >=1.7.0,<2.0a0 - libopengl >=1.7.0,<2.0a0 - libstdcxx >=14 - libvulkan-loader >=1.4.328.1,<2.0a0 - libxml2 - libxml2-16 >=2.14.6 - libxslt >=1.1.43,<2.0a0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - qt6-main 6.10.1.* - qt6-main >=6.10.1,<6.11.0a0 license: LGPL-3.0-only license_family: LGPL purls: - pkg:pypi/pyside6?source=hash-mapping - pkg:pypi/shiboken6?source=hash-mapping size: 11606305 timestamp: 1765811838817 - 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/linux-64/python-3.10.19-h3c07f61_2_cpython.conda build_number: 2 sha256: 6e3b6b69b3cacfc7610155d58407a003820eaacd50fbe039abff52b5e70b1e9b md5: 27ac896a8b4970f8977503a9e70dc745 depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - ld_impl_linux-64 >=2.36.1 - libexpat >=2.7.1,<3.0a0 - libffi >=3.4,<4.0a0 - libgcc >=14 - liblzma >=5.8.1,<6.0a0 - libnsl >=2.0.1,<2.1.0a0 - libsqlite >=3.50.4,<4.0a0 - libuuid >=2.41.2,<3.0a0 - libxcrypt >=4.4.36 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.4,<4.0a0 - readline >=8.2,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.10.* *_cp310 license: Python-2.0 purls: [] size: 25311690 timestamp: 1761173015969 - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.14-hd63d673_2_cpython.conda build_number: 2 sha256: 5b872f7747891e50e990a96d2b235236a5c66cc9f8c9dcb7149aee674ea8145a md5: c4202a55b4486314fbb8c11bc43a29a0 depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - ld_impl_linux-64 >=2.36.1 - libexpat >=2.7.1,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - libgcc >=14 - liblzma >=5.8.1,<6.0a0 - libnsl >=2.0.1,<2.1.0a0 - libsqlite >=3.50.4,<4.0a0 - libuuid >=2.41.2,<3.0a0 - libxcrypt >=4.4.36 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.4,<4.0a0 - readline >=8.2,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.11.* *_cp311 license: Python-2.0 purls: [] size: 30874708 timestamp: 1761174520369 - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.12-hd63d673_1_cpython.conda build_number: 1 sha256: 39898d24769a848c057ab861052e50bdc266310a7509efa3514b840e85a2ae98 md5: 5c00c8cea14ee8d02941cab9121dce41 depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - ld_impl_linux-64 >=2.36.1 - libexpat >=2.7.1,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - libgcc >=14 - liblzma >=5.8.1,<6.0a0 - libnsl >=2.0.1,<2.1.0a0 - libsqlite >=3.50.4,<4.0a0 - libuuid >=2.41.2,<3.0a0 - libxcrypt >=4.4.36 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.4,<4.0a0 - readline >=8.2,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.12.* *_cp312 license: Python-2.0 purls: [] size: 31537229 timestamp: 1761176876216 - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.2-h32b2ec7_100_cp314.conda build_number: 100 sha256: a120fb2da4e4d51dd32918c149b04a08815fd2bd52099dad1334647984bb07f1 md5: 1cef1236a05c3a98f68c33ae9425f656 depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 - ld_impl_linux-64 >=2.36.1 - libexpat >=2.7.3,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - libgcc >=14 - liblzma >=5.8.1,<6.0a0 - libmpdec >=4.0.0,<5.0a0 - libsqlite >=3.51.1,<4.0a0 - libuuid >=2.41.2,<3.0a0 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.4,<4.0a0 - python_abi 3.14.* *_cp314 - readline >=8.2,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata - zstd >=1.5.7,<1.6.0a0 license: Python-2.0 purls: [] size: 36790521 timestamp: 1765021515427 python_site_packages_path: lib/python3.14/site-packages - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.10.19-h988dfef_2_cpython.conda build_number: 2 sha256: cda6726872b13f92d4dea6bf1aa4cbc594e7de008e37df28da05b94d0d18f489 md5: f46421dd285f5cb0213c0fdce20ab196 depends: - __osx >=10.13 - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.1,<3.0a0 - libffi >=3.4,<4.0a0 - liblzma >=5.8.1,<6.0a0 - libsqlite >=3.50.4,<4.0a0 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.4,<4.0a0 - readline >=8.2,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.10.* *_cp310 license: Python-2.0 purls: [] size: 13135247 timestamp: 1761173952753 - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.11.14-h74c2667_2_cpython.conda build_number: 2 sha256: 0a17479efb8df514c3777c015ffe430d38a3a59c01dc46358e87d7ff459c9aeb md5: 37ac5f13a245f08746e0d658b245d670 depends: - __osx >=10.13 - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.1,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.1,<6.0a0 - libsqlite >=3.50.4,<4.0a0 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.4,<4.0a0 - readline >=8.2,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.11.* *_cp311 license: Python-2.0 purls: [] size: 15697126 timestamp: 1761174493171 - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.12.12-h74c2667_1_cpython.conda build_number: 1 sha256: 7d711e7a5085c05d186e1dbc86b8f10fb3d88fb3ce3034944ededef39173ff32 md5: 902046b662c35d8d644514df0d9c7109 depends: - __osx >=10.13 - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.1,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.1,<6.0a0 - libsqlite >=3.50.4,<4.0a0 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.4,<4.0a0 - readline >=8.2,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.12.* *_cp312 license: Python-2.0 purls: [] size: 13779792 timestamp: 1761176993883 - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.14.2-hf88997e_100_cp314.conda build_number: 100 sha256: cd9d41368cb7c531e82fbfdb01e274efbb176c464b59ec619538dd2580602191 md5: 48921d5efb314c3e628089fc6e27e54a depends: - __osx >=10.13 - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.3,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.1,<6.0a0 - libmpdec >=4.0.0,<5.0a0 - libsqlite >=3.51.1,<4.0a0 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.4,<4.0a0 - python_abi 3.14.* *_cp314 - readline >=8.2,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata - zstd >=1.5.7,<1.6.0a0 license: Python-2.0 purls: [] size: 14323056 timestamp: 1765026108189 python_site_packages_path: lib/python3.14/site-packages - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.10.19-hcd7f573_2_cpython.conda build_number: 2 sha256: 7bac6cc075d1d7897f06fa14c1bc87eb16b9524c6002e0c72b0ed3326af51695 md5: feb559b139819a7326f992711cb50872 depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.1,<3.0a0 - libffi >=3.4,<4.0a0 - liblzma >=5.8.1,<6.0a0 - libsqlite >=3.50.4,<4.0a0 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.4,<4.0a0 - readline >=8.2,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.10.* *_cp310 license: Python-2.0 purls: [] size: 11674631 timestamp: 1761173465015 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.14-h18782d2_2_cpython.conda build_number: 2 sha256: 64a2bc6be8582fae75f1f2da7bdc49afd81c2793f65bb843fc37f53c99734063 md5: da948e6cd735249ab4cfbb3fdede785e depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.1,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.1,<6.0a0 - libsqlite >=3.50.4,<4.0a0 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.4,<4.0a0 - readline >=8.2,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.11.* *_cp311 license: Python-2.0 purls: [] size: 14788204 timestamp: 1761174033541 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.12-h18782d2_1_cpython.conda build_number: 1 sha256: 626da9bb78459ce541407327d1e22ee673fd74e9103f1a0e0f4e3967ad0a23a7 md5: 0322f2ddca2cafbf34ef3ddbea100f73 depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.1,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.1,<6.0a0 - libsqlite >=3.50.4,<4.0a0 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.4,<4.0a0 - readline >=8.2,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - python_abi 3.12.* *_cp312 license: Python-2.0 purls: [] size: 12062421 timestamp: 1761176476561 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.2-h40d2674_100_cp314.conda build_number: 100 sha256: 1a93782e90b53e04c2b1a50a0f8bf0887936649d19dba6a05b05c4b44dae96b7 md5: 14f15ab0d31a2ee5635aa56e77132594 depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.3,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.1,<6.0a0 - libmpdec >=4.0.0,<5.0a0 - libsqlite >=3.51.1,<4.0a0 - libzlib >=1.3.1,<2.0a0 - ncurses >=6.5,<7.0a0 - openssl >=3.5.4,<4.0a0 - python_abi 3.14.* *_cp314 - readline >=8.2,<9.0a0 - tk >=8.6.13,<8.7.0a0 - tzdata - zstd >=1.5.7,<1.6.0a0 license: Python-2.0 purls: [] size: 13575758 timestamp: 1765021280625 python_site_packages_path: lib/python3.14/site-packages - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.10.19-hc20f281_2_cpython.conda build_number: 2 sha256: 58c3066571c9c8ba62254dfa1cee696d053f9f78cd3a92c8032af58232610c32 md5: cd78c55405743e88fda2464be3c902b3 depends: - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.1,<3.0a0 - libffi >=3.4,<4.0a0 - liblzma >=5.8.1,<6.0a0 - libsqlite >=3.50.4,<4.0a0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.4,<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: 16106778 timestamp: 1761172101787 - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.11.14-h0159041_2_cpython.conda build_number: 2 sha256: d5f455472597aefcdde1bc39bca313fcb40bf084f3ad987da0441f2a2ec242e4 md5: 02a9ba5950d8b78e6c9862d6ba7a5045 depends: - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.1,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.1,<6.0a0 - libsqlite >=3.50.4,<4.0a0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.4,<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: 18514691 timestamp: 1761172844103 - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.12.12-h0159041_1_cpython.conda build_number: 1 sha256: 9b163b0426c92eee1881d5c838e230a750a3fa372092db494772886ab91c2548 md5: 42ae551e4c15837a582bea63412dc0b4 depends: - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.1,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.1,<6.0a0 - libsqlite >=3.50.4,<4.0a0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.4,<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: 15883484 timestamp: 1761175152489 - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.2-h4b44e0e_100_cp314.conda build_number: 100 sha256: 6857d7c97cc71fe9ba298dcb1d3b66cc7df425132ab801babd655faa3df48f32 md5: c3c73414d5ae3f543c531c978d9cc8b8 depends: - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.3,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.1,<6.0a0 - libmpdec >=4.0.0,<5.0a0 - libsqlite >=3.51.1,<4.0a0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.4,<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: 16833248 timestamp: 1765020224759 python_site_packages_path: Lib/site-packages - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.2-h7c1dbca_0_cp314t.conda sha256: 19f72e35862ec440537236c9852e1d849faeeb3201f2314dae5e8c5c858789ff md5: c2df3512978c64bcf6fb142fc60b8b8f depends: - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.3,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - liblzma >=5.8.1,<6.0a0 - libmpdec >=4.0.0,<5.0a0 - libsqlite >=3.51.1,<4.0a0 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.4,<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: 17215998 timestamp: 1765019544568 python_site_packages_path: Lib/site-packages - 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-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.14-hd8ed1ab_2.conda sha256: 9261923f0dc8a3c8c517c29d8f3b7eea80f2577b094198239895bd7a88b534c5 md5: a4effc7e6eb335d0e1080a5554590425 depends: - cpython 3.11.14.* - python_abi * *_cp311 license: Python-2.0 purls: [] size: 47569 timestamp: 1761172935811 - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-2.0.7-pyhd8ed1ab_0.conda sha256: 4790787fe1f4e8da616edca4acf6a4f8ed4e7c6967aa31b920208fc8f95efcca md5: a61bf9ec79426938ff785eb69dbb1960 depends: - python >=3.6 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/python-json-logger?source=hash-mapping size: 13383 timestamp: 1677079727691 - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2025.3-pyhd8ed1ab_0.conda sha256: 467134ef39f0af2dbb57d78cb3e4821f01003488d331a8dd7119334f4f47bfbd md5: 7ead57407430ba33f681738905278d03 depends: - python >=3.10 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/tzdata?source=compressed-mapping size: 143542 timestamp: 1765719982349 - 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-2025.2-pyhd8ed1ab_0.conda sha256: 8d2a8bf110cc1fc3df6904091dead158ba3e614d8402a83e51ed3a8aa93cdeb0 md5: bc8e3267d44011051f2eb14d22fb0960 depends: - python >=3.9 license: MIT license_family: MIT purls: - pkg:pypi/pytz?source=hash-mapping size: 189015 timestamp: 1742920947249 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py311h3778330_0.conda sha256: 7dc5c27c0c23474a879ef5898ed80095d26de7f89f4720855603c324cca19355 md5: 707c3d23f2476d3bfde8345b4e7d7853 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: 211606 timestamp: 1758892088237 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_0.conda sha256: 1b3dc4c25c83093fff08b86a3574bc6b94ba355c8eba1f35d805c5e256455fc7 md5: fba10c2007c8b06f77c5a23ce3a635ad 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: 204539 timestamp: 1758892248166 - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py311he13f9b5_0.conda sha256: be448cd6d759cd21d40bc9a3850672187a8d37fcd3abdc3f637abc0ca1ed2f44 md5: 2d9ba0ec796516a17d3c87efdb881aff 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: 196463 timestamp: 1758892069824 - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py312hacf3034_0.conda sha256: 28814df783a5581758d197262d773c92a72c8cedbec3ccadac90adf22daecd25 md5: dbc6cfbec3095d84d9f3baab0c6a5c24 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: 192483 timestamp: 1758892060370 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py311ha9b3269_0.conda sha256: 747c1b94222481a727aeeb912407f862a93a1bb4e704be3a8236768182ac0290 md5: 109a9c326951cc9ab5df6a06cf5b930a 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: 195537 timestamp: 1758892104856 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py312h5748b74_0.conda sha256: 690943c979a5bf014348933a68cd39e3bb9114d94371c4c5d846d2daaa82c7d9 md5: 6a2d7f8a026223c2fa1027c96c615752 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: 190579 timestamp: 1758891996097 - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py311h3f79411_0.conda sha256: 22dcc6c6779e5bd970a7f5208b871c02bf4985cf4d827d479c4a492ced8ce577 md5: 4e9b677d70d641f233b29d5eab706e20 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: 188290 timestamp: 1758892467876 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py311h2315fbb_0.conda sha256: 719104f31c414166a20281c973b6e29d1a2ab35e7930327368949895b8bc5629 md5: 6c87a0f4566469af3585b11d89163fd7 depends: - python - __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - libgcc >=14 - 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: 386618 timestamp: 1757387012835 - conda: https://conda.anaconda.org/conda-forge/osx-64/pyzmq-27.1.0-py311h0ab6910_0.conda sha256: a0200b5e781c22a5d7b64fd0edf5e9ab881772f7a15a6bc2359db8d0ac437bb3 md5: 840bdfbb93e35d650205af10883ff8a0 depends: - python - libcxx >=19 - __osx >=10.13 - 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: 362304 timestamp: 1757387126324 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.1.0-py311h13abfa4_0.conda sha256: 5a213744d267241e23f849c7671dc97eb98d7789fb559bf5d423ae1884294c7e md5: 0d16883d4ab2d3fcb38460d018d6762f depends: - python - __osx >=11.0 - libcxx >=19 - python 3.11.* *_cpython - 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: 359600 timestamp: 1757387159663 - 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/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-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/linux-64/qt6-main-6.10.1-h6f76662_2.conda sha256: d89aa34f9c05944664c277f2a06f751b9559028d6228a2be5ff6130f19ce0e41 md5: cb26b00c816d80d73c8f6f00064fa123 depends: - __glibc >=2.17,<3.0.a0 - alsa-lib >=1.2.14,<1.3.0a0 - dbus >=1.16.2,<2.0a0 - double-conversion >=3.4.0,<3.5.0a0 - fontconfig >=2.15.0,<3.0a0 - fonts-conda-ecosystem - harfbuzz >=12.2.0 - icu >=75.1,<76.0a0 - krb5 >=1.21.3,<1.22.0a0 - libclang-cpp21.1 >=21.1.7,<21.2.0a0 - libclang13 >=21.1.7 - libcups >=2.3.3,<2.4.0a0 - libdrm >=2.4.125,<2.5.0a0 - libegl >=1.7.0,<2.0a0 - libfreetype >=2.14.1 - libfreetype6 >=2.14.1 - libgcc >=14 - libgl >=1.7.0,<2.0a0 - libglib >=2.86.3,<3.0a0 - libjpeg-turbo >=3.1.2,<4.0a0 - libllvm21 >=21.1.7,<21.2.0a0 - libpng >=1.6.53,<1.7.0a0 - libpq >=18.1,<19.0a0 - libsqlite >=3.51.1,<4.0a0 - libstdcxx >=14 - libtiff >=4.7.1,<4.8.0a0 - libvulkan-loader >=1.4.328.1,<2.0a0 - libwebp-base >=1.6.0,<2.0a0 - libxcb >=1.17.0,<2.0a0 - libxkbcommon >=1.13.1,<2.0a0 - libxml2 - libxml2-16 >=2.14.6 - libzlib >=1.3.1,<2.0a0 - openssl >=3.5.4,<4.0a0 - pcre2 >=10.47,<10.48.0a0 - wayland >=1.24.0,<2.0a0 - xcb-util >=0.4.1,<0.5.0a0 - xcb-util-cursor >=0.1.6,<0.2.0a0 - xcb-util-image >=0.4.0,<0.5.0a0 - xcb-util-keysyms >=0.4.1,<0.5.0a0 - xcb-util-renderutil >=0.3.10,<0.4.0a0 - xcb-util-wm >=0.4.2,<0.5.0a0 - xorg-libice >=1.1.2,<2.0a0 - xorg-libsm >=1.2.6,<2.0a0 - xorg-libx11 >=1.8.12,<2.0a0 - xorg-libxcomposite >=0.4.6,<1.0a0 - xorg-libxcursor >=1.2.3,<2.0a0 - xorg-libxdamage >=1.1.6,<2.0a0 - xorg-libxext >=1.3.6,<2.0a0 - xorg-libxrandr >=1.5.4,<2.0a0 - xorg-libxtst >=1.2.5,<2.0a0 - xorg-libxxf86vm >=1.1.6,<2.0a0 - zstd >=1.5.7,<1.6.0a0 constrains: - qt 6.10.1 license: LGPL-3.0-only license_family: LGPL purls: [] size: 57037651 timestamp: 1765675961370 - 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_0.conda sha256: 2f225ddf4a274743045aded48053af65c31721e797a45beed6774fdc783febfb md5: 0227d04521bc3d28c7995c7e1f99a721 depends: - libre2-11 2025.11.05 h7b12aa8_0 license: BSD-3-Clause license_family: BSD purls: [] size: 27316 timestamp: 1762397780316 - 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-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-2024.07.02-h6589ca4_2.conda sha256: 4d3799c05f8f662922a0acd129d119774760a3281b883603678e128d1cb307fb md5: 7a8b4ad8c58a3408ca89d78788c78178 depends: - libre2-11 2024.07.02 h07bc746_2 license: BSD-3-Clause license_family: BSD purls: [] size: 26861 timestamp: 1735541088455 - 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/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-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/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.32.5-pyhd8ed1ab_0.conda sha256: 8dc54e94721e9ab545d7234aa5192b74102263d3e704e6d0c8aa7008f2da2a7b md5: db0c6b99149880c8ba515cf4abe93ee4 depends: - certifi >=2017.4.17 - charset-normalizer >=2,<4 - idna >=2.5,<4 - python >=3.9 - urllib3 >=1.21.1,<3 constrains: - chardet >=3.0.2,<6 license: Apache-2.0 license_family: APACHE purls: - pkg:pypi/requests?source=hash-mapping size: 59263 timestamp: 1755614348400 - 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-14.2.0-pyhcf101f3_0.conda sha256: edfb44d0b6468a8dfced728534c755101f06f1a9870a7ad329ec51389f16b086 md5: a247579d8a59931091b16a1e932bbed6 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: 200840 timestamp: 1760026188268 - 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=compressed-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=compressed-mapping size: 11074 timestamp: 1766025162370 - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.30.0-py311h902ca64_0.conda sha256: bf5e6197fb08b8c6e421ca0126e966b7c3ae62b84d7b98523356b4fd5ae6f8ae md5: 3893f7b40738f9fe87510cb4468cdda5 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=hash-mapping size: 383153 timestamp: 1764543197251 - conda: https://conda.anaconda.org/conda-forge/osx-64/rpds-py-0.30.0-py311hd2a4513_0.conda sha256: ae8d3455662c94043e1bebefe594bb7e0c83d142dca73fa4dfb8c046b08f8831 md5: e9c6e8d9c5d7aa0309332460fef57f49 depends: - python - __osx >=10.13 - python_abi 3.11.* *_cp311 constrains: - __osx >=10.13 license: MIT license_family: MIT purls: - pkg:pypi/rpds-py?source=hash-mapping size: 367310 timestamp: 1764543117432 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-0.30.0-py311h71babbd_0.conda sha256: 15873755f078583cea046f7ca7fc0d5348d1f29a16a30b73bdb53dd62f2ba379 md5: 4408829b022e8e0d19365c0c00be00c4 depends: - python - python 3.11.* *_cpython - __osx >=11.0 - python_abi 3.11.* *_cp311 constrains: - __osx >=11.0 license: MIT license_family: MIT purls: - pkg:pypi/rpds-py?source=hash-mapping size: 357600 timestamp: 1764543142990 - 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/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-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/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/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.8.0-np2py311ha15b03d_1.conda sha256: 1b28c914e59b0e346fef3574c192ea919c710784a9b5a4eff640fb81825a91a9 md5: 20166c092c5a5093bbaca59925d573a3 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.3.0 - threadpoolctl >=3.2.0 - _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: 9975664 timestamp: 1765801236131 - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.8.0-np2py312h3226591_1.conda sha256: 23c643c37fafa14ba3f2b7a407126ea5e732a3655ea8157cf9f977098f863448 md5: 38decbeae260892040709cafc0514162 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.3.0 - threadpoolctl >=3.2.0 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - libstdcxx >=14 - _openmp_mutex >=4.5 - numpy >=1.23,<3 - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9726193 timestamp: 1765801245538 - conda: https://conda.anaconda.org/conda-forge/linux-64/scikit-learn-1.8.0-np2py314hf09ca88_1.conda sha256: bcf374fe61712928c624f410a831e9f2a36ad13429f598e6028203588d24b914 md5: c9d90e43202c721281f3d74129223515 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.3.0 - threadpoolctl >=3.2.0 - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - _openmp_mutex >=4.5 - python_abi 3.14.* *_cp314 - numpy >=1.23,<3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9992698 timestamp: 1765801260253 - 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.8.0-np2py311hd641367_1.conda sha256: 093890496b4443bb2da5242b29c379dfdd08f642ffde2f1599b8ef1202b2c536 md5: 95cfd22b3cb27be57698f7494c558a4b depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.3.0 - threadpoolctl >=3.2.0 - llvm-openmp >=19.1.7 - __osx >=10.13 - libcxx >=19 - numpy >=1.23,<3 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9539227 timestamp: 1765801309981 - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.8.0-np2py312hc921ccd_1.conda sha256: 46af92a133ca30018b09cd6417a9d830769a9611257fe5665b6d1e32e2feec02 md5: 10d570b3cc0d2b0ad363f781bbcefeb2 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.3.0 - threadpoolctl >=3.2.0 - libcxx >=19 - __osx >=10.13 - llvm-openmp >=19.1.7 - python_abi 3.12.* *_cp312 - numpy >=1.23,<3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9289017 timestamp: 1765801375049 - conda: https://conda.anaconda.org/conda-forge/osx-64/scikit-learn-1.8.0-np2py314h17d75c0_1.conda sha256: d5b4310c08784b9c60c1b960d34497dfc4c747fc46b01de7042fb277f4477f09 md5: d768cb7c352d818ac7f4f4acc1c4d8f7 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.3.0 - threadpoolctl >=3.2.0 - libcxx >=19 - llvm-openmp >=19.1.7 - __osx >=10.13 - python_abi 3.14.* *_cp314 - numpy >=1.23,<3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9551332 timestamp: 1765801457910 - 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.8.0-np2py311hece8d6f_1.conda sha256: c843bd6c881b57a71c5abae24f54c0090b565f783bd23fff595a0ddcb81f6ccd md5: 0c714aea5448debd809f991f663694fd depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.3.0 - threadpoolctl >=3.2.0 - llvm-openmp >=19.1.7 - libcxx >=19 - __osx >=11.0 - python 3.11.* *_cpython - numpy >=1.23,<3 - python_abi 3.11.* *_cp311 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9358658 timestamp: 1765801351129 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.8.0-np2py312he7bfc6a_1.conda sha256: 7e58d791fadcba97d1bcc3208f8953128a38da9d614f6b6f2e410b3af0f36e5f md5: c93be01f32810fb3b237f9e59fb13eb0 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.3.0 - threadpoolctl >=3.2.0 - libcxx >=19 - llvm-openmp >=19.1.7 - python 3.12.* *_cpython - __osx >=11.0 - python_abi 3.12.* *_cp312 - numpy >=1.23,<3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9124600 timestamp: 1765801343261 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scikit-learn-1.8.0-np2py314h3d7c909_1.conda sha256: c48790dba6db4aff5b46b83d825135ada31646db0e62a682b6c2699107d05d1b md5: 861eaf9a16f11ae79d9d79ef19749ca0 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.3.0 - threadpoolctl >=3.2.0 - libcxx >=19 - __osx >=11.0 - python 3.14.* *_cp314 - llvm-openmp >=19.1.7 - python_abi 3.14.* *_cp314 - numpy >=1.23,<3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9383471 timestamp: 1765801446406 - 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.8.0-np2py311hd01f973_1.conda sha256: 5899a0bd83d382025446fa8c1459c57e5cbb15a9e6db9fdcb35e4961717aa916 md5: e05096da659f628aadc0c95ec0c34123 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.3.0 - threadpoolctl >=3.2.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - python_abi 3.11.* *_cp311 - numpy >=1.23,<3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9133816 timestamp: 1765801251916 - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.8.0-np2py314h1b5b07a_1.conda sha256: ce701fcf35e0b65d0822fe916f5536ed326c1b842fe1ba6d08c5fcac4ec8dc75 md5: ba2216c82d626684433912bfec8a4843 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.3.0 - threadpoolctl >=3.2.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - python_abi 3.14.* *_cp314 - numpy >=1.23,<3 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/scikit-learn?source=hash-mapping size: 9139165 timestamp: 1765801295593 - conda: https://conda.anaconda.org/conda-forge/win-64/scikit-learn-1.8.0-np2py314hb10d3ac_1.conda sha256: cfd3d971ae87c608d28a39c1ab1abafb38a0f12b90675680cec3bd65d5b49eb8 md5: 5da3c37367e765444e801c29f096ac88 depends: - python - numpy >=1.24.1 - scipy >=1.10.0 - joblib >=1.3.0 - threadpoolctl >=3.2.0 - 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: 9664809 timestamp: 1765801257209 - 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.16.3-py311hbe70eeb_2.conda sha256: a13084f1556674ea74de2ecbe50333d938dab8ef27f536408592ba312363c400 md5: 1f9587850322d7d77ea14d4fee3d16d8 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.6 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause purls: - pkg:pypi/scipy?source=compressed-mapping size: 17026343 timestamp: 1766108701646 - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.3-py312h54fa4ab_2.conda sha256: 2f73242ca3164b4f305becb535a8245ff25839a42d4e62b222f866a5bf58b989 md5: e82683871cbc4bb257b7694f31a91327 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.6 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause purls: - pkg:pypi/scipy?source=compressed-mapping size: 16666973 timestamp: 1766108740332 - conda: https://conda.anaconda.org/conda-forge/linux-64/scipy-1.16.3-py314hf07bd8e_2.conda sha256: 652f9a235051c1d39ccd2fe7e9326792b046a1d93de42171977fa1ba9668a0e8 md5: ee95e8bb52e35c3267a53d3ee1347cc4 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.6 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 license: BSD-3-Clause purls: - pkg:pypi/scipy?source=compressed-mapping size: 16982488 timestamp: 1766108668132 - 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.16.3-py311hd77d3c2_2.conda sha256: dee00542bb0aed60d6160883365c19317fe63b34ea0f3237a8725fee297341f6 md5: 5153a584333b37a433b5e1244606ef3d depends: - __osx >=10.13 - 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.6 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 license: BSD-3-Clause purls: - pkg:pypi/scipy?source=hash-mapping size: 15290138 timestamp: 1766108637965 - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.16.3-py312h79cf0a0_2.conda sha256: ce00b56cbd8dd7e4c7c3367a40946eafafe2728c95598478d3c3e134e7bbc86b md5: 9379a86fa21719bc2af4c0845f24a739 depends: - __osx >=10.13 - 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.6 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 license: BSD-3-Clause purls: - pkg:pypi/scipy?source=hash-mapping size: 15117052 timestamp: 1766108456706 - conda: https://conda.anaconda.org/conda-forge/osx-64/scipy-1.16.3-py314hbb40827_2.conda sha256: 28ca934c3f9bb1d1d126b4e6f7f37ee14e11407e5ea99e6c9c0a772d537d1ce4 md5: 306e89b8db5482c47324978efcf59f7d depends: - __osx >=10.13 - 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.6 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.14,<3.15.0a0 - python_abi 3.14.* *_cp314 license: BSD-3-Clause purls: - pkg:pypi/scipy?source=hash-mapping size: 15135829 timestamp: 1766108573799 - 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.16.3-py311ha71c161_2.conda sha256: 3a3bd525f126e7414c07bcf915060a36bfc3dac4cf31335585be942128d4337f md5: 1283a5d5d1c10e981638d2bd02c4eac6 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.6 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.11,<3.12.0a0 - python >=3.11,<3.12.0a0 *_cpython - python_abi 3.11.* *_cp311 license: BSD-3-Clause purls: - pkg:pypi/scipy?source=hash-mapping size: 13815921 timestamp: 1766108875815 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.16.3-py312h39258fd_2.conda sha256: beae2ee638cff6f954026d4ed7ca316fb6b4fa451af7f0ebbb83a94b832740ed md5: 24765804abd9985bf6fbc57c9691479b 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.6 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.12,<3.13.0a0 - python >=3.12,<3.13.0a0 *_cpython - python_abi 3.12.* *_cp312 license: BSD-3-Clause purls: - pkg:pypi/scipy?source=compressed-mapping size: 13758691 timestamp: 1766108954684 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/scipy-1.16.3-py314h725efaa_2.conda sha256: 282f8b244f31d8c2e0ce401b0473e8090de4b59326018a360419693b629e6b87 md5: 6333b784ddfcccd3f5569f812f66c352 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.6 - numpy >=1.23,<3 - numpy >=1.25.2 - python >=3.14,<3.15.0a0 - python >=3.14,<3.15.0a0 *_cp314 - python_abi 3.14.* *_cp314 license: BSD-3-Clause purls: - pkg:pypi/scipy?source=compressed-mapping size: 13880523 timestamp: 1766109018710 - 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.16.3-py311h9c22a71_2.conda sha256: 49129601dc89d49742d342ace70f4ec0127a5eb24a50d66f95f91db01b3a23d5 md5: 4b663de0f0c8ac0fbb4a4d9ee8536b0f depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 - numpy <2.6 - 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 purls: - pkg:pypi/scipy?source=hash-mapping size: 15129579 timestamp: 1766109708812 - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.16.3-py314h221f224_2.conda sha256: 99d6198dc05171610073083c9d218d2a9adfa756659b391183d21cca55f888f1 md5: b600c47282ee91e492b89f65708a5c9a depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 - numpy <2.6 - 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 purls: - pkg:pypi/scipy?source=hash-mapping size: 15082636 timestamp: 1766109482825 - conda: https://conda.anaconda.org/conda-forge/win-64/scipy-1.16.3-py314h9c5632b_2.conda sha256: 8b4901aa8b5b99fbb210dd2a31c8ccc48b90b62d03e7b65f784c8ec9796e3b9a md5: 3396a485523bfb1276e20476b0fadf1d depends: - libblas >=3.9.0,<4.0a0 - libcblas >=3.9.0,<4.0a0 - liblapack >=3.9.0,<4.0a0 - numpy <2.6 - 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 purls: - pkg:pypi/scipy?source=hash-mapping size: 15291726 timestamp: 1766109699079 - 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/linux-64/secretstorage-3.4.1-py311h38be061_0.conda sha256: 47f28b12e760ae3ce8a1e616c5b56f5e874e0e4a036bdd09516ebf263c19521f md5: ec955e67147942a68469a46d0bdf0a7b 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: 32968 timestamp: 1763045430433 - conda: https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh0d859eb_1.conda sha256: 00926652bbb8924e265caefdb1db100f86a479e8f1066efe395d5552dde54d02 md5: 938c8de6b9de091997145b3bf25cdbf9 depends: - __linux - python >=3.9 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/send2trash?source=hash-mapping size: 22736 timestamp: 1733322148326 - conda: https://conda.anaconda.org/conda-forge/noarch/send2trash-1.8.3-pyh31c8845_1.conda sha256: 5282eb5b462502c38df8cb37cd1542c5bbe26af2453a18a0a0602d084ca39f53 md5: e67b1b1fa7a79ff9e8e326d0caf55854 depends: - __osx - pyobjc-framework-cocoa - python >=3.9 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/send2trash?source=hash-mapping size: 23100 timestamp: 1733322309409 - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-80.9.0-pyhff2d567_0.conda sha256: 972560fcf9657058e3e1f97186cc94389144b46dbdf58c807ce62e83f977e863 md5: 4de79c071274a53dcaf2a8c749d1499e depends: - python >=3.9 license: MIT license_family: MIT purls: - pkg:pypi/setuptools?source=hash-mapping size: 748788 timestamp: 1748804951958 - 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.5-pyhd8ed1ab_0.conda sha256: 003f33fa9e555ba8adc3da59b8be98ca2a61829da123abb7a9bea2d95b7f6261 md5: 5a9f81c5642665ff94675c05096828e4 depends: - python >=3.10 - scikit-learn >=1.2,<1.9 license: BSD-3-Clause purls: - pkg:pypi/sklearn-compat?source=hash-mapping size: 24060 timestamp: 1766329827681 - 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/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-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/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=compressed-mapping size: 15698 timestamp: 1762941572482 - conda: https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-3.0.1-pyhd8ed1ab_0.conda sha256: 17007a4cfbc564dc3e7310dcbe4932c6ecb21593d4fec3c68610720f19e73fb2 md5: 755cf22df8693aa0d1aec1c123fa5863 depends: - python >=3.9 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/snowballstemmer?source=hash-mapping size: 73009 timestamp: 1747749529809 - conda: https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.8.1-pyhd8ed1ab_0.conda sha256: 4ba9b8c45862e54d05ed9a04cc6aab5a17756ab9865f57cbf2836e47144153d2 md5: 7de28c27fe620a4f7dbfaea137c6232b depends: - python >=3.10 license: MIT purls: - pkg:pypi/soupsieve?source=compressed-mapping size: 37951 timestamp: 1766075884412 - 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.6.1-pyhd8ed1ab_2.conda sha256: eb335aef48e49107b55299cedc197f86d05651f1eeff83ed8acf89df7cdc9765 md5: 3e6c15d914b03f83fc96344f917e0838 depends: - python >=3.9 - sphinx >=6,<9 license: MIT license_family: MIT purls: - pkg:pypi/sphinx-design?source=hash-mapping size: 911336 timestamp: 1734614675610 - conda: https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.20.0-pyhd8ed1ab_0.conda sha256: f6452238ba9c4478ba1bdf630a07744f0fc2eb6d85a084f142a5a881a290f1c7 md5: 4cae490c8d142824fb80d9aed672fddd depends: - pillow - python >=3.10 - sphinx >=4 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/sphinx-gallery?source=hash-mapping size: 392291 timestamp: 1764692947861 - 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.6.5-pyhd8ed1ab_0.conda sha256: b128f051391c67c5ee77bf5aa2e6e4073adfc22631829491db112fcafe58f196 md5: 6267ad9b8e6c02ea6280a9d6eabe1026 depends: - docutils >=0.8,!=0.18.*,!=0.19.* - importlib-metadata >=3.6 - pybtex >=0.25 - pybtex-docutils >=1.0.0 - python >=3.9 - setuptools - sphinx >=3.5 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/sphinxcontrib-bibtex?source=hash-mapping size: 33137 timestamp: 1751029066274 - 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/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=compressed-mapping size: 11903737 timestamp: 1764983555676 - 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-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/win-64/tbb-2022.3.0-hd094cb3_1.conda sha256: c31cac57913a699745d124cdc016a63e31c5749f16f60b3202414d071fc50573 md5: 17c38aaf14c640b85c4617ccb59c1146 depends: - libhwloc >=2.12.1,<2.12.2.0a0 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: APACHE purls: [] size: 155714 timestamp: 1762510341121 - 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/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/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-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/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_py311h6ac8430_2.conda sha256: bbb336a7caee25c0dcc1d5a32693c2650565155098b7d6a3edf53ab93cf3166d md5: ee775ba6ef8dab178c3c85e5d2399d5a depends: - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - tensorflow-base 2.19.1 cpu_py311h690c71f_2 - tensorflow-estimator 2.19.1 cpu_py311ha1e2c70_2 track_features: - tensorflow-cpu license: Apache-2.0 license_family: Apache purls: [] size: 50434 timestamp: 1764398317507 - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-2.19.1-cpu_py312h69ecde4_2.conda sha256: 47e0ab5d4b85454b040d2197aa80396c306938531266a4e4efa520e3d865f733 md5: 6ed85633a453870a94c5bf58072afcbf depends: - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - tensorflow-base 2.19.1 cpu_py312h0afb428_2 - tensorflow-estimator 2.19.1 cpu_py312h6e6b5e2_2 track_features: - tensorflow-cpu license: Apache-2.0 license_family: Apache purls: [] size: 50430 timestamp: 1764398336276 - 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-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.18.0-cpu_py311h9d3d1e9_1.conda sha256: 30fcf449fd7ed74e6f2b412d949a8062f953e2a0fff418b5586d2501db2125ff md5: 78f3619b558ea007aef0da81dd7369c3 depends: - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - tensorflow-base 2.18.0 cpu_py311h5147c6a_1 - tensorflow-estimator 2.18.0 cpu_py311h61c9c21_1 track_features: - tensorflow-cpu license: Apache-2.0 license_family: Apache purls: [] size: 46171 timestamp: 1754499427251 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-2.18.0-cpu_py312hb6b62e0_1.conda sha256: 816595f0080d58e0617bda5fee4bb3bd60be258417e1bc9a4cf70973c308aba0 md5: 94339beae4bae4682242df1c448d1306 depends: - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - tensorflow-base 2.18.0 cpu_py312h81ab8d0_1 - tensorflow-estimator 2.18.0 cpu_py312h5e86b3d_1 track_features: - tensorflow-cpu license: Apache-2.0 license_family: Apache purls: [] size: 46145 timestamp: 1754499440394 - 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_py311h690c71f_2.conda sha256: 77ebccb1247a4b3b2be1daf2ad9c2f4d96c333c24b68aea8a9ec39e057edaa61 md5: 4696f7c1eaebcf8d0afb308b516d1c6f depends: - __glibc >=2.17,<3.0.a0 - _x86_64-microarch-level >=1 - absl-py >=1.0.0 - astunparse >=1.6.0 - flatbuffers >=25.2.10,<25.2.11.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.73.* - h5py >=3.11 - icu >=75.1,<76.0a0 - keras >=3.5 - libabseil * cxx17* - libabseil >=20250512.1,<20250513.0a0 - libcurl >=8.17.0,<9.0a0 - libgcc >=14 - libgrpc >=1.73.1,<1.74.0a0 - libjpeg-turbo >=3.1.2,<4.0a0 - libpng >=1.6.51,<1.7.0a0 - libprotobuf >=6.31.1,<6.31.2.0a0 - libsqlite >=3.51.1,<4.0a0 - libstdcxx >=14 - libtensorflow_cc 2.19.1 cpu_hf7abd0a_2 - libtensorflow_framework 2.19.1 cpu_h12ecb4a_2 - libzlib >=1.3.1,<2.0a0 - ml_dtypes >=0.5.1,<1.0 - numpy >=1.23,<3 - openssl >=3.5.4,<4.0a0 - opt_einsum >=2.3.2 - packaging - protobuf >=5.26 - 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.19,<2.20 - 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: 65586155 timestamp: 1764397243100 - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-base-2.19.1-cpu_py312h0afb428_2.conda sha256: 16b24f777133ccec3a31de20b059900fd74a52dd0bd54b2cdf7081504607d336 md5: 798d2e923a450153a751382bafb42f78 depends: - __glibc >=2.17,<3.0.a0 - _x86_64-microarch-level >=1 - absl-py >=1.0.0 - astunparse >=1.6.0 - flatbuffers >=25.2.10,<25.2.11.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.73.* - h5py >=3.11 - icu >=75.1,<76.0a0 - keras >=3.5 - libabseil * cxx17* - libabseil >=20250512.1,<20250513.0a0 - libcurl >=8.17.0,<9.0a0 - libgcc >=14 - libgrpc >=1.73.1,<1.74.0a0 - libjpeg-turbo >=3.1.2,<4.0a0 - libpng >=1.6.51,<1.7.0a0 - libprotobuf >=6.31.1,<6.31.2.0a0 - libsqlite >=3.51.1,<4.0a0 - libstdcxx >=14 - libtensorflow_cc 2.19.1 cpu_hf7abd0a_2 - libtensorflow_framework 2.19.1 cpu_h12ecb4a_2 - libzlib >=1.3.1,<2.0a0 - ml_dtypes >=0.5.1,<1.0 - numpy >=1.23,<3 - openssl >=3.5.4,<4.0a0 - opt_einsum >=2.3.2 - packaging - protobuf >=5.26 - 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.19,<2.20 - 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: 66247661 timestamp: 1764397531143 - 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-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.18.0-cpu_py311h5147c6a_1.conda sha256: 1c1d1b49a26d95c077e055d8267fdcde3940c7c339febcb9a1bbf525226da270 md5: fa8776e4a4282f29b6459c2ed6cc5167 depends: - __osx >=11.0 - 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_hf321e49_1 - libtensorflow_framework 2.18.0 cpu_h2398287_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: 73114560 timestamp: 1754498205860 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-base-2.18.0-cpu_py312h81ab8d0_1.conda sha256: a0e4bf6530a5e8f9efd2e8db5708a6664372aac866ebdd5987f5318b27fc8efa md5: 0f4a1295b15d2797f8beaee0f22af723 depends: - __osx >=11.0 - 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_hf321e49_1 - libtensorflow_framework 2.18.0 cpu_h2398287_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: 73631276 timestamp: 1754498401137 - 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/tensorflow-estimator-2.19.1-cpu_py311ha1e2c70_2.conda sha256: 59197cd410fd13a8362c33f2aed371f5aefb6cef8dbcf32ec494d82b01874ebd md5: acb775876c7a2f9824c39902905321e7 depends: - __glibc >=2.17,<3.0.a0 - _x86_64-microarch-level >=1 - libgcc >=14 - libstdcxx >=14 - openssl >=3.5.4,<4.0a0 - python >=3.11,<3.12.0a0 - python_abi 3.11.* *_cp311 - tensorflow-base 2.19.1 cpu_py311h690c71f_2 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tensorflow-estimator?source=hash-mapping size: 726568 timestamp: 1764398105178 - conda: https://conda.anaconda.org/conda-forge/linux-64/tensorflow-estimator-2.19.1-cpu_py312h6e6b5e2_2.conda sha256: a5eafae32d7b1f2753c9cefaf20b3798be5c6d13464c2de395618b1d2e5aaa01 md5: f0c5802e45cf651108db4862a64e10d4 depends: - __glibc >=2.17,<3.0.a0 - _x86_64-microarch-level >=1 - libgcc >=14 - libstdcxx >=14 - openssl >=3.5.4,<4.0a0 - python >=3.12,<3.13.0a0 - python_abi 3.12.* *_cp312 - tensorflow-base 2.19.1 cpu_py312h0afb428_2 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tensorflow-estimator?source=hash-mapping size: 704088 timestamp: 1764398199860 - 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-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/tensorflow-estimator-2.18.0-cpu_py311h61c9c21_1.conda sha256: 1422189be7f85c4ab37fbdeb9d7e2da0d1b00e4c768a248008f444c3aac3adb4 md5: c96062e28110ed8f6f074329f8eb5e56 depends: - __osx >=11.0 - 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_py311h5147c6a_1 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tensorflow-estimator?source=hash-mapping size: 722554 timestamp: 1754499045339 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tensorflow-estimator-2.18.0-cpu_py312h5e86b3d_1.conda sha256: 0e91eb003237bb8279ce870fc12e7e21d1c04a39eb6d1cac116ec67fda23884d md5: 65d1fbbbb27cad0fb2266cb0f612b9fc depends: - __osx >=11.0 - 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_py312h81ab8d0_1 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tensorflow-estimator?source=hash-mapping size: 699391 timestamp: 1754499167407 - conda: https://conda.anaconda.org/conda-forge/noarch/termcolor-3.2.0-pyhd8ed1ab_0.conda sha256: a06b613f6f1d4b87200d775c6625cf0b5eff49856b0dd44625ec50797c99b64a md5: ac2888874deebb7c177b5cb6781cc48b depends: - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/termcolor?source=hash-mapping size: 13126 timestamp: 1761565850813 - conda: https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh0d859eb_0.conda sha256: b300557c0382478cf661ddb520263508e4b3b5871b471410450ef2846e8c352c md5: efba281bbdae5f6b0a1d53c6d4a97c93 depends: - __linux - ptyprocess - python >=3.8 - tornado >=6.1.0 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/terminado?source=hash-mapping size: 22452 timestamp: 1710262728753 - conda: https://conda.anaconda.org/conda-forge/noarch/terminado-0.18.1-pyh31c8845_0.conda sha256: 4daae56fc8da17784578fbdd064f17e3b3076b394730a14119e571707568dc8a md5: 00b54981b923f5aefcd5e8547de056d5 depends: - __osx - ptyprocess - python >=3.8 - tornado >=6.1.0 license: BSD-2-Clause license_family: BSD purls: - pkg:pypi/terminado?source=hash-mapping size: 22717 timestamp: 1710265922593 - 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.5.1-pyhcf101f3_0.conda sha256: 7c803480dbfb8b536b9bf6287fa2aa0a4f970f8c09075694174eb4550a4524cd md5: c0d0b883e97906f7524e2aac94be0e0d depends: - python >=3.10 - webencodings >=0.4 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/tinycss2?source=compressed-mapping size: 30571 timestamp: 1764621508086 - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_ha0e22de_103.conda sha256: 1544760538a40bcd8ace2b1d8ebe3eb5807ac268641f8acdc18c69c5ebfeaf64 md5: 86bc20552bf46075e3d92b67f089172d depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - libzlib >=1.3.1,<2.0a0 constrains: - xorg-libx11 >=1.8.12,<2.0a0 license: TCL license_family: BSD purls: [] size: 3284905 timestamp: 1763054914403 - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-hf689a15_3.conda sha256: 0d0b6cef83fec41bc0eb4f3b761c4621b7adfb14378051a8177bd9bb73d26779 md5: bd9f1de651dbd80b51281c694827f78f depends: - __osx >=10.13 - libzlib >=1.3.1,<2.0a0 license: TCL license_family: BSD purls: [] size: 3262702 timestamp: 1763055085507 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_3.conda sha256: ad0c67cb03c163a109820dc9ecf77faf6ec7150e942d1e8bb13e5d39dc058ab7 md5: a73d54a5abba6543cb2f0af1bfbd6851 depends: - __osx >=11.0 - libzlib >=1.3.1,<2.0a0 license: TCL license_family: BSD purls: [] size: 3125484 timestamp: 1763055028377 - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_3.conda sha256: 4581f4ffb432fefa1ac4f85c5682cc27014bcd66e7beaa0ee330e927a7858790 md5: 7cb36e506a7dba4817970f8adb6396f9 depends: - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 license: TCL license_family: BSD purls: [] size: 3472313 timestamp: 1763055164278 - 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.3.0-pyhcf101f3_0.conda sha256: cb77c660b646c00a48ef942a9e1721ee46e90230c7c570cdeb5a893b5cce9bff md5: d2732eb636c264dc9aa4cbee404b1a53 depends: - python >=3.10 - python license: MIT license_family: MIT purls: - pkg:pypi/tomli?source=compressed-mapping size: 20973 timestamp: 1760014679845 - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.3-py311h49ec1c0_0.conda sha256: 0d5c53a3ae7531ddf6bc28fb95edded05f1908f3ccffe5ab820f5992b81e5418 md5: a0d8cab7384ccfca582b952d9c8c619a 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=compressed-mapping size: 871254 timestamp: 1765458944370 - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.3-py312h4c3975b_0.conda sha256: bed440cad040f0fe76266f9a527feecbaf00385b68a96532aa69614fe5153f8e md5: e03a4bf52d2170d64c816b2a52972097 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: 850918 timestamp: 1765458857375 - conda: https://conda.anaconda.org/conda-forge/osx-64/tornado-6.5.4-py311ha2bb86f_0.conda sha256: a7ef7e9e43e27a4ae6b1c7b523d4beae202eedafaa6c5fd4e116d4437fd5c0da md5: 354f5036a42f0ee629877a63a4ac801e 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: 872287 timestamp: 1765836899438 - conda: https://conda.anaconda.org/conda-forge/osx-64/tornado-6.5.4-py312h404bc50_0.conda sha256: 44ba44075b754a0da5a476d5cdc6783e290d3f26d355c9fc236abaaefa902d4d md5: fc935f8c37abef2b3cc3b9f15b951c6d 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: 854453 timestamp: 1765836802876 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.4-py311h9408147_0.conda sha256: b7098eb573af83912e13df4d373feb35854b3e37a7f7cc97f8fc01a03a10b995 md5: df911f6f4b283ce96679e1031a8ff530 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: 872651 timestamp: 1765836782876 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.4-py312h4409184_0.conda sha256: 114bfa1b859a64c589c428fce0ff8e358d8f0aaa7b98d353b94a95c7bceae640 md5: fde4548a1e99c14eea9752f270ab68aa 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: 854598 timestamp: 1765836762571 - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda sha256: f39a5620c6e8e9e98357507262a7869de2ae8cc07da8b7f84e517c9fd6c2b959 md5: 019a7385be9af33791c989871317e1ed depends: - python >=3.9 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/traitlets?source=hash-mapping size: 110051 timestamp: 1733367480074 - 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-h8577fbf_0.conda sha256: 50fad5db6734d1bb73df1cf5db73215e326413d4b2137933f70708aa1840e25b md5: 338201218b54cadff2e774ac27733990 license: LicenseRef-Public-Domain purls: [] size: 119204 timestamp: 1765745742795 - 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/linux-64/ukkonen-1.0.1-py311hdf67eae_6.conda sha256: a6201979b8619bb9122609eb2189287c33e4a75ad240e4880898941764022782 md5: 57e703b0057f992687fb9ad154dc48e4 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: 14597 timestamp: 1761594653571 - conda: https://conda.anaconda.org/conda-forge/osx-64/ukkonen-1.0.1-py311hd4d69bb_6.conda sha256: db2a1043a6d2916bc88719eaf5f970523b429e3aaa04e734fb86554726f196e9 md5: b44817c2fd57f7d6b3b2be9af922a8f7 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: 13991 timestamp: 1761595177556 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ukkonen-1.0.1-py311h57a9ea7_6.conda sha256: b11dee8446a369aaa149b1650dec45105949c9043cc8a5802b292305d48b8718 md5: 2534d14fa2ebc5a6657bc835bc695557 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: 14449 timestamp: 1761595344417 - conda: https://conda.anaconda.org/conda-forge/win-64/ukkonen-1.0.1-py311h3fd045d_6.conda sha256: 0d1e5387856d81510504b966337b188061010e25c290975c51355e839037de65 md5: 6305e35221cc08c9a3d1eeb57961ccb4 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: 18229 timestamp: 1761594869187 - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.0-py311h49ec1c0_1.conda sha256: d3c0e3ca6eb49095159d8c78970a279a30b98863eff5c3eeb037296d2e1d1670 md5: 5e6d4026784e83c0a51c86ec428e8cc8 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=compressed-mapping size: 408540 timestamp: 1763054987009 - conda: https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-17.0.0-py312h4c3975b_1.conda sha256: 3c812c634e78cec74e224cc6adf33aed533d9fe1ee1eff7f692e1f338efb8c5b md5: a0b8efbe73c90f810a171a6c746be087 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: 408399 timestamp: 1763054875733 - conda: https://conda.anaconda.org/conda-forge/osx-64/unicodedata2-17.0.0-py311hf197a57_1.conda sha256: 5881bc012c534c9427c5f3e07373b901fa494a84e30f7125e7fffd5691a03cef md5: 915e0e550ae72c7754845c58a3ea79f5 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: 404132 timestamp: 1763055336694 - conda: https://conda.anaconda.org/conda-forge/osx-64/unicodedata2-17.0.0-py312h80b0991_1.conda sha256: 1e85f9891f5f1e03aaf4b02af66b296596a2c487180f7c21ee9f57ed104821ac md5: 32a0138cbc4a3934d61fef34a4b8e1c5 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: 403881 timestamp: 1763055352529 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.0-py311h9408147_1.conda sha256: 69cd342d0f3d21af2e2b4bafd257d76c65f011d25c38fef086f35212db895f09 md5: a1d9fe82627b095af51c65e0fff3dbf4 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: 416311 timestamp: 1763055361431 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/unicodedata2-17.0.0-py312h4409184_1.conda sha256: 567cebbb3a1a5c76e5ec43508e01ccbe98923ad0003eafd87acbbc546fcd588c md5: b0b0c7ea4888b6f4009afa7001e6adaa 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: 416271 timestamp: 1763055285615 - 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.6.2-pyhd8ed1ab_0.conda sha256: f4302a80ee9b76279ad061df05003abc2a29cc89751ffab2fd2919b43455dac0 md5: 4949ca7b83065cfe94ebe320aece8c72 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=compressed-mapping size: 102842 timestamp: 1765719817255 - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h2b53caa_33.conda sha256: 7036945b5fff304064108c22cbc1bb30e7536363782b0456681ee6cf209138bd md5: 2d1c042360c09498891809a3765261be depends: - vc14_runtime >=14.42.34433 track_features: - vc14 license: BSD-3-Clause license_family: BSD purls: [] size: 19070 timestamp: 1765216452130 - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_33.conda sha256: 7e8f7da25d7ce975bbe7d7e6d6e899bf1f253e524a3427cc135a79f3a79c457c md5: fb8e4914c5ad1c71b3c519621e1df7b8 depends: - ucrt >=10.0.20348.0 - vcomp14 14.44.35208 h818238b_33 constrains: - vs2015_runtime 14.44.35208.* *_33 license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime license_family: Proprietary purls: [] size: 684323 timestamp: 1765216366832 - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_33.conda sha256: f79edd878094e86af2b2bc1455b0a81e02839a784fb093d5996ad4cf7b810101 md5: 4cb6942b4bd846e51b4849f4a93c7e6d depends: - ucrt >=10.0.20348.0 constrains: - vs2015_runtime 14.44.35208.* *_33 license: LicenseRef-MicrosoftVisualCpp2015-2022Runtime license_family: Proprietary purls: [] size: 115073 timestamp: 1765216325898 - conda: https://conda.anaconda.org/conda-forge/noarch/virtualenv-20.35.4-pyhd8ed1ab_0.conda sha256: 77193c99c6626c58446168d3700f9643d8c0dab1f6deb6b9dd039e6872781bfb md5: cfccfd4e8d9de82ed75c8e2c91cab375 depends: - distlib >=0.3.7,<1 - filelock >=3.12.2,<4 - platformdirs >=3.9.1,<5 - python >=3.10 - typing_extensions >=4.13.2 license: MIT license_family: MIT purls: - pkg:pypi/virtualenv?source=hash-mapping size: 4401341 timestamp: 1761726489722 - conda: https://conda.anaconda.org/conda-forge/linux-64/wayland-1.24.0-hd6090a7_1.conda sha256: 3aa04ae8e9521d9b56b562376d944c3e52b69f9d2a0667f77b8953464822e125 md5: 035da2e4f5770f036ff704fa17aace24 depends: - __glibc >=2.17,<3.0.a0 - libexpat >=2.7.1,<3.0a0 - libffi >=3.5.2,<3.6.0a0 - libgcc >=14 - libstdcxx >=14 license: MIT license_family: MIT purls: [] size: 329779 timestamp: 1761174273487 - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.2.14-pyhd8ed1ab_0.conda sha256: e311b64e46c6739e2a35ab8582c20fa30eb608da130625ed379f4467219d4813 md5: 7e1e5ff31239f9cd5855714df8a3783d depends: - python >=3.10 license: MIT license_family: MIT purls: - pkg:pypi/wcwidth?source=hash-mapping size: 33670 timestamp: 1758622418893 - 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.4-pyhcf101f3_0.conda sha256: 0c4a30a4713347ff822d73d2349fd9ef67a6f34993e7ae3434cdee7047e9ee1e md5: de082192dffe45d19aff3d86a4d06a24 depends: - markupsafe >=2.1.1 - python >=3.10 - python license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/werkzeug?source=hash-mapping size: 256621 timestamp: 1764423417723 - conda: https://conda.anaconda.org/conda-forge/noarch/wheel-0.45.1-pyhd8ed1ab_1.conda sha256: 1b34021e815ff89a4d902d879c3bd2040bc1bd6169b32e9427497fa05c55f1ce md5: 75cb7132eb58d97896e173ef12ac9986 depends: - python >=3.9 license: MIT license_family: MIT purls: - pkg:pypi/wheel?source=hash-mapping size: 62931 timestamp: 1733130309598 - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.0.1-py310h7c4b9e2_1.conda sha256: 78e25379fc4863ef423ac555dc6a106b5608ea0de627f979eb01e274a73f538a md5: 8b1bc6c8f7cb83c20d755cbf8c067e73 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: 77797 timestamp: 1762594995230 - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.0.1-py311h49ec1c0_1.conda sha256: 81327871ef18cbdd095e1e7650a198fd13f02355a39f23f1e377aa6d899f8ce0 md5: e8442f5d358d1449deaf4ba76f5e212c 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=hash-mapping size: 88027 timestamp: 1762595033219 - conda: https://conda.anaconda.org/conda-forge/linux-64/wrapt-2.0.1-py312h4c3975b_1.conda sha256: d1d352da699a642be0fd7a5cc894c37b1e8ddfda37c723cacfa9b1986824f80d md5: b6ee834617873da8a2196c109275b38b 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: 86544 timestamp: 1762594992655 - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.0.1-py310hd2d5e8d_1.conda sha256: 239c567a353d0ee23a513d4612eb0536f44a7c99b760c865457012577650a0a4 md5: e1e26971b9d562e9c578d8b59ddafb73 depends: - __osx >=10.13 - 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: 73229 timestamp: 1762595295062 - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.0.1-py311hf197a57_1.conda sha256: 20e08397523fa073a572a47d63f4ac004087f09932e0928eef8d6786218be59c md5: 54e2a24577747909ae96011f103a1757 depends: - __osx >=10.13 - 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: 83599 timestamp: 1762595191397 - conda: https://conda.anaconda.org/conda-forge/osx-64/wrapt-2.0.1-py312h80b0991_1.conda sha256: 2258e7766c912b387b33ff7aa743a6e02359d9faacb5b6a0e824c2b1d7b08522 md5: 44ae6baf386bc605c091fa40bed30434 depends: - __osx >=10.13 - 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: 81858 timestamp: 1762595214211 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.0.1-py310hfe3a0ae_1.conda sha256: 5ce70859368e399b70a255f000a1fc41d7cc82a6f97bbd45a1743cb773a05fac md5: 84c3687112aee5d533ff209135405626 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: 74263 timestamp: 1762595229984 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.0.1-py311h9408147_1.conda sha256: 4701eef37ea9905f36250e94027348ef33768ccac77a579c7f0d6c508e6b9918 md5: fd167167b3aabafec6a4e77562128f0b 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: 84530 timestamp: 1762595301878 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/wrapt-2.0.1-py312h4409184_1.conda sha256: 53b97d650332321e67e046de2c29ff60bc742a17d5e4c48a15c704933843e156 md5: 2b3afb010681e2dcfbf27366d373f5c8 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: 83380 timestamp: 1762595281305 - 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.46-hb03c661_0.conda sha256: aa03b49f402959751ccc6e21932d69db96a65a67343765672f7862332aa32834 md5: 71ae752a748962161b4740eaff510258 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - xorg-libx11 >=1.8.12,<2.0a0 license: MIT license_family: MIT purls: [] size: 396975 timestamp: 1759543819846 - 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.12-h4f16b4b_0.conda sha256: 51909270b1a6c5474ed3978628b341b4d4472cd22610e5f22b506855a5e20f67 md5: db038ce880f100acc74dba10302b5630 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - libxcb >=1.17.0,<2.0a0 license: MIT license_family: MIT purls: [] size: 835896 timestamp: 1741901112627 - 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/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-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/linux-64/xorg-libxcomposite-0.4.6-hb9d3cd8_2.conda sha256: 753f73e990c33366a91fd42cc17a3d19bb9444b9ca5ff983605fa9e953baf57f md5: d3c295b50f092ab525ffe3c2aa4b7413 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - xorg-libx11 >=1.8.10,<2.0a0 - xorg-libxfixes >=6.0.1,<7.0a0 license: MIT license_family: MIT purls: [] size: 13603 timestamp: 1727884600744 - 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/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-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/linux-64/xorg-libxext-1.3.6-hb9d3cd8_0.conda sha256: da5dc921c017c05f38a38bd75245017463104457b63a1ce633ed41f214159c14 md5: febbab7d15033c913d53c7a2c102309d depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - xorg-libx11 >=1.8.10,<2.0a0 license: MIT license_family: MIT purls: [] size: 50060 timestamp: 1727752228921 - 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.2-hb9d3cd8_0.conda sha256: 1a724b47d98d7880f26da40e45f01728e7638e6ec69f35a3e11f92acd05f9e7a md5: 17dcc85db3c7886650b8908b183d6876 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: 47179 timestamp: 1727799254088 - conda: https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrandr-1.5.4-hb9d3cd8_0.conda sha256: ac0f037e0791a620a69980914a77cb6bb40308e26db11698029d6708f5aa8e0d md5: 2de7f99d6581a4a7adbff607b5c278ca depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - xorg-libx11 >=1.8.10,<2.0a0 - xorg-libxext >=1.3.6,<2.0a0 - xorg-libxrender >=0.9.11,<0.10.0a0 license: MIT license_family: MIT purls: [] size: 29599 timestamp: 1727794874300 - 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.6-hb9d3cd8_0.conda sha256: 8a4e2ee642f884e6b78c20c0892b85dd9b2a6e64a6044e903297e616be6ca35b md5: 5efa5fa6243a622445fdfd72aee15efa depends: - __glibc >=2.17,<3.0.a0 - libgcc >=13 - xorg-libx11 >=1.8.10,<2.0a0 - xorg-libxext >=1.3.6,<2.0a0 license: MIT license_family: MIT purls: [] size: 17819 timestamp: 1734214575628 - 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/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-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/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/linux-64/zeromq-4.3.5-h387f397_9.conda sha256: 47cfe31255b91b4a6fa0e9dbaf26baa60ac97e033402dbc8b90ba5fee5ffe184 md5: 8035e5b54c08429354d5d64027041cad depends: - libstdcxx >=14 - libgcc >=14 - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libsodium >=1.0.20,<1.0.21.0a0 - krb5 >=1.21.3,<1.22.0a0 license: MPL-2.0 license_family: MOZILLA purls: [] size: 310648 timestamp: 1757370847287 - conda: https://conda.anaconda.org/conda-forge/osx-64/zeromq-4.3.5-h6c33b1e_9.conda sha256: 30aa5a2e9c7b8dbf6659a2ccd8b74a9994cdf6f87591fcc592970daa6e7d3f3c md5: d940d809c42fbf85b05814c3290660f5 depends: - __osx >=10.13 - libcxx >=19 - libsodium >=1.0.20,<1.0.21.0a0 - krb5 >=1.21.3,<1.22.0a0 license: MPL-2.0 license_family: MOZILLA purls: [] size: 259628 timestamp: 1757371000392 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zeromq-4.3.5-h888dc83_9.conda sha256: b6f9c130646e5971f6cad708e1eee278f5c7eea3ca97ec2fdd36e7abb764a7b8 md5: 26f39dfe38a2a65437c29d69906a0f68 depends: - __osx >=11.0 - libcxx >=19 - libsodium >=1.0.20,<1.0.21.0a0 - krb5 >=1.21.3,<1.22.0a0 license: MPL-2.0 license_family: MOZILLA purls: [] size: 244772 timestamp: 1757371008525 - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.0-pyhcf101f3_1.conda sha256: b4533f7d9efc976511a73ef7d4a2473406d7f4c750884be8e8620b0ce70f4dae md5: 30cd29cb87d819caead4d55184c1d115 depends: - python >=3.10 - python license: MIT license_family: MIT purls: - pkg:pypi/zipp?source=compressed-mapping size: 24194 timestamp: 1764460141901 - conda: https://conda.anaconda.org/conda-forge/linux-64/zlib-ng-2.3.2-hceb46e0_1.conda sha256: f2b6a175677701a0b6ce556b3bd362dc94a4e36ffcd10e3860e52ca036b4ad96 md5: 40feea2979654ed579f1cda7c63ccb94 depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 license: Zlib license_family: Other purls: [] size: 122303 timestamp: 1766076745735 - conda: https://conda.anaconda.org/conda-forge/osx-64/zlib-ng-2.3.2-h8bce59a_1.conda sha256: 945725769bc668435af1c23733c3c1dba01eb115ad3bad5393c9df2e23de6cfc md5: cdd69480d52f2b871fad1a91324d9942 depends: - __osx >=10.13 - libcxx >=19 license: Zlib license_family: Other purls: [] size: 120585 timestamp: 1766077108928 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zlib-ng-2.3.2-hed4e4f5_1.conda sha256: ab481487381a6a6213d667e883252e52b8ca867b3b466c31a058126f964efffe md5: 75f39a44c08cb5dc4ea847698de34ba3 depends: - __osx >=11.0 - libcxx >=19 license: Zlib license_family: Other purls: [] size: 94882 timestamp: 1766076931977 - 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/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/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/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 scikit-learn-contrib-imbalanced-learn-fc39a83/pyproject.toml000066400000000000000000000173771512206630300242640ustar00rootroot00000000000000[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.5,<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.5,<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-fc39a83/references.bib000066400000000000000000000155461512206630300241430ustar00rootroot00000000000000 @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} }