diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 79b6859a..ddeea9a2 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -8,7 +8,7 @@ _Put your message here_ - [ ] Make sure all CI workflows are green - [ ] When adding a public feature/fix: Update the `Unreleased` section of `CHANGELOG.md` (no date) - [ ] Self-Review: Review "Files Changed" tab and fix any problems you find -- API Docs (only if there are changes in docstrings, rst files or samples): +- API Docs (only if there are changes in docstrings, markdown files or samples): - [ ] Check the docs build **without** warning: see the log of the API Docs workflow - [ ] Check that your changes render well in HTML: download the API Docs artifact and open `index.html` - - If there are any problems it is faster to iterate by [building locally the API Docs](../blob/main/doc/README.md#build-the-documentation) + - If there are any problems it is faster to iterate by [building locally the API Docs](doc/README.md#build-the-documentation) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index b6b97a56..d5895541 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -53,7 +53,7 @@ supervised models (classifiers, regressors, encoders) and unsupervised models - **`khiops.extras`** — Optional integrations such as the Docker runner - **`khiops.tools`** — Miscellaneous utility tools and CLI entry points - **`khiops.samples`** — Sample scripts, also used to generate parts of the - documentation via `doc/convert-samples-hook` + documentation via `doc/util/convert-samples-hook` Keep changes inside these layer boundaries. @@ -64,7 +64,7 @@ Keep changes inside these layer boundaries. - Do not add new external dependencies without discussion. Minimize external package dependencies to reduce installation problems. - Development and documentation generation dependencies (e.g., `black`, - `isort`, `sphinx`, `wrapt`, `furo`) can be more permissive, but still avoid + `isort`, `zensical`, `wrapt`) can be more permissive, but still avoid unnecessary additions. - Test dependencies are listed in `test-requirements.txt` (`coverage`, `wrapt`). Package dependencies are extracted from `pyproject.toml` at CI time via diff --git a/.github/instructions/ci-workflows.instructions.md b/.github/instructions/ci-workflows.instructions.md index 0b3d0d21..77a554da 100644 --- a/.github/instructions/ci-workflows.instructions.md +++ b/.github/instructions/ci-workflows.instructions.md @@ -10,10 +10,9 @@ guidance. ## Workflow Overview -This repository has six GitHub Actions workflows in `.github/workflows/`. Most -workflows use concurrency groups to cancel in-progress runs when superseded, -except `api-docs.yml` (which uses a `pages` concurrency group that does not -cancel in-progress runs). +This repository has six GitHub Actions workflows in `.github/workflows/`. All +workflows except `api-docs.yml` use concurrency groups to cancel in-progress +runs when superseded. ### `quick-checks.yml` @@ -22,7 +21,7 @@ hooks (configured in `.pre-commit-config.yaml`) are: Black, pylint, isort (with special no-sections config for sample files), yamlfix, shellcheck, GitHub workflow/action schema validation (`check-github-workflows`, `check-github-actions`), and a local -`samples-generation` hook that regenerates reST samples when +`samples-generation` hook that regenerates Markdown samples when `khiops/samples/samples.py` or `khiops/samples/samples_sklearn.py` change. ### `tests.yml` @@ -71,16 +70,20 @@ corresponding GitHub environment (`testpypi` or `pypi`). Only runs for the ### `api-docs.yml` -Builds Sphinx documentation inside a dev Docker container. Triggers on: +Builds Zensical documentation inside a dev Docker container for validation. +Triggers on: -- Tag pushes — builds docs and uploads a zip archive to GitHub Releases -- PRs touching `doc/**/*.rst`, `doc/create-doc`, `doc/clean-doc`, `doc/*.py`, - `khiops/**/*.py`, or the workflow file +- PRs touching `doc/site/**/*.md`, `doc/util/create-doc`, `doc/util/clean-doc`, `doc/util/*.py`, + `zensical.toml`, `khiops/**/*.py`, or the workflow file - `workflow_dispatch` with optional tutorial and samples revision inputs Uses the `khiopspydev-ubuntu22.04` Docker image and runs -`./create-doc -t -d -g `. Uses a `pages` concurrency group that does -**not** cancel in-progress runs (to avoid interrupting production deployments). +`doc/util/create-doc -t -d -g `. The built HTML is uploaded as a +`api-docs` artifact for review. + +Note: the production API docs are built by the +[khiops-doc](https://github.com/KhiopsML/khiops-doc) CI, which clones this +repository and builds the docs using mkdocstrings. ### `dev-docker.yml` diff --git a/.github/instructions/doc-changes.instructions.md b/.github/instructions/doc-changes.instructions.md index d448674c..287db185 100644 --- a/.github/instructions/doc-changes.instructions.md +++ b/.github/instructions/doc-changes.instructions.md @@ -12,112 +12,110 @@ guidance. ``` doc/ -├── conf.py # Sphinx configuration -├── index.rst # Top-level doc page -├── create-doc # Full build script (tutorials + Sphinx) -├── clean-doc # Clean script (supports --clean-tutorial) -├── convert-samples-hook # Pre-commit hook: regenerates sample reST + notebooks -├── convert_samples.py # Converts samples.py / samples_sklearn.py to reST or .ipynb -├── convert_tutorials.py # Converts tutorial Jupyter notebooks to reST -├── requirements.txt # Python doc-build dependencies -├── multi_table_primer.rst # Multi-table learning guide -├── notes.rst # API notes (common params, input types, sampling) -├── core/index.rst # khiops.core API reference (autosummary) -├── sklearn/index.rst # khiops.sklearn API reference (autosummary) -├── internal/index.rst # Internal modules reference -├── tools/index.rst # khiops.tools reference -├── samples/ # Generated reST sample pages (via convert-samples-hook) -├── tutorials/ # Generated reST tutorials (via create-doc -t) -├── _static/ # CSS and images (branding, logo) -└── _templates/autosummary/ # Custom autosummary templates (class, function, method, module) +├── README.md # Documentation guide +├── util/ # Build tooling (outside Zensical's docs_dir) +│ ├── create-doc # Full build script (tutorials + Zensical) +│ ├── clean-doc # Clean script (supports --clean-tutorial) +│ ├── convert-samples-hook # Pre-commit hook: regenerates sample Markdown + notebooks +│ ├── convert_samples.py # Converts samples.py / samples_sklearn.py to Markdown or .ipynb +│ ├── convert_tutorials.py # Converts tutorial Jupyter notebooks to Markdown +│ └── requirements.txt # Python doc-build dependencies +├── site/ # docs_dir (Zensical content only) +│ ├── index.md # Top-level doc page +│ ├── multi_table_primer.md # Multi-table learning guide +│ ├── notes.md # API notes (common params, input types, sampling) +│ ├── core/index.md # khiops.core API reference (mkdocstrings) +│ ├── sklearn/index.md # khiops.sklearn API reference (mkdocstrings) +│ ├── internal/index.md # Internal modules reference +│ ├── tools/index.md # khiops.tools reference +│ ├── samples/ # Generated Markdown sample pages (via convert-samples-hook) +│ ├── tutorials/ # Generated Markdown tutorials (via create-doc -t) +│ ├── _static/ # CSS and images (branding, logo) +│ └── _templates/ # mkdocstrings Jinja templates +└── build/ # Zensical output (site_dir) + └── html/ ``` +The Zensical configuration file `zensical.toml` is at the repository root. + ## Build and Validation ```bash -cd doc - -# Install doc dependencies (do NOT create a virtualenv inside doc/ — Sphinx will process its .rst files) -pip install -U -r requirements.txt +# Install doc dependencies (do NOT create a virtualenv inside doc/util — Zensical will process its .md files). +uv pip install -U -r doc/util/requirements.txt # Also requires: -# - A system-wide pandoc installation (used by nbconvert for notebook→reST conversion) # - The 'black' Python package (used by convert_samples.py to format code snippets) -# Regenerate reST samples and notebooks from samples.py / samples_sklearn.py. +# Regenerate Markdown samples and notebooks from samples.py / samples_sklearn.py. # This hook also runs automatically via pre-commit when those files are modified. -./convert-samples-hook +doc/util/convert-samples-hook + +# Full build: download tutorials, convert notebooks to Markdown, run Zensical +doc/util/create-doc -d -t -# Full build: download tutorials, convert notebooks to reST, run Sphinx -./create-doc -d -t +# Incremental build (Zensical only, after Markdown files are already generated): +zensical build -# Incremental build (Sphinx only, after reST files are already generated): -sphinx-build -M html . _build/ +# Serve locally for development: +zensical serve # Clean generated docs (add --clean-tutorial to also remove tutorials/ and khiops-python-tutorial/) -./clean-doc +doc/util/clean-doc ``` -The `create-doc` script requires `tar`, `python`, `make`, `zip`, and `git` (if -downloading tutorials). Output goes to `doc/_build/html/`. +The `create-doc` script requires `python`, `uv` and `git` (if +downloading tutorials). Output goes to `doc/build/html/`. The `create-doc` script accepts the following options: - `-d` — Download the khiops-python-tutorial repository (implies `-t`) -- `-t` — Transform tutorial Jupyter notebooks into reST +- `-t` — Transform tutorial Jupyter notebooks into Markdown - `-r REPO_URL` — Set the tutorial repository URL - `-g GIT_REF` — Set the tutorial repository Git reference (branch or tag) - `-l DIR` — Set the local directory of the tutorial repository +- `-p` — Prepare only: download tutorials, convert notebooks, create ZIPs, + and copy samples, but skip the final Zensical build. Used by the + [khiops-doc](https://github.com/KhiopsML/khiops-doc) CI. ## CI Workflow -The **API Docs** workflow (`.github/workflows/api-docs.yml`) triggers on: +The **API Docs** workflow (`.github/workflows/api-docs.yml`) validates +documentation builds. It triggers on: -- **Tag pushes** — builds docs and uploads a zip archive to GitHub Releases as - a prerelease (with `allowUpdates: true`) -- **PRs** touching `doc/**.rst`, `doc/create-doc`, `doc/clean-doc`, `doc/*.py`, - `khiops/**.py`, or the workflow file itself +- **PRs** touching `doc/site/**.md`, `doc/util/create-doc`, `doc/util/clean-doc`, `doc/util/*.py`, + `zensical.toml`, `khiops/**.py`, or the workflow file itself - **`workflow_dispatch`** with optional inputs: + - `khiops-revision` (default `11.0.1`) - `khiops-python-tutorial-revision` (default: `11.0.0.0`) - `khiops-samples-revision` (default: `11.0.0`) - - `image-tag` (default: `latest`) — the dev Docker image tag -The workflow uses a concurrency group (`pages`) so only one deployment runs at a -time — queued runs are skipped but in-progress runs are never cancelled. +**Build job** -**Build job** — runs inside the -`ghcr.io/khiopsml/khiops-python/khiopspydev-ubuntu22.04:` Docker -image: - -1. Installs the khiops-python package itself (`pip install .`) +1. Installs the khiops-core package (`uv pip install khiops-core`) and + khiops-python package itself (`uv pip install .`) 2. Downloads sample datasets via `kh-download-datasets` -3. Installs doc Python requirements from `doc/requirements.txt` -4. Runs `./create-doc -t -d -g ` +3. Installs doc Python requirements from `doc/util/requirements.txt` +4. Runs `doc/util/create-doc -t -d -g ` 5. Uploads the built HTML as a `api-docs` artifact -**Release job** (tag pushes only) — downloads the artifact, zips it, and -uploads the zip to GitHub Releases. - -## Sphinx Setup - -- **Engine**: Sphinx with the [Furo](https://pradyunsg.me/furo/) theme - (Orange-branded colors and Helvetica Neue font) -- **Docstring format**: [NumPy style](https://numpydoc.readthedocs.io/en/latest/format.html) parsed by the `numpydoc` extension - (`numpydoc_show_class_members = False`) -- **Extensions**: `autodoc`, `autosummary`, `intersphinx`, `numpydoc`, - `sphinx_copybutton` -- **Intersphinx targets**: Python, pandas, scikit-learn, NumPy, SciPy -- **Custom templates**: `_templates/autosummary/` provides templates for - `class.rst`, `function.rst`, `method.rst`, `module.rst` -- **Strict mode**: `nitpicky = True` — broken references are errors -- **Default role**: `obj` (configured as `default_role = "obj"` in `conf.py`) — - allows cross-referencing without explicit `:class:`/`:func:` qualifiers in - most cases -- **Warning suppression**: `conf.py` defines a `suppress_sklearn_warnings` - callback that silences known false-positive missing-reference warnings for - sklearn variables (`X`, `y`) and tutorial literals -- Sphinx warnings **should not be ignored** — they almost always indicate - rendering errors +Note: the production API docs are built by the +[khiops-doc](https://github.com/KhiopsML/khiops-doc) CI, which clones this +repository at the version tag and builds the docs natively using mkdocstrings. + +## Zensical Setup + +- **Engine**: Zensical with the [Material](https://squidfunk.github.io/mkdocs-material/) + theme (Orange-branded colors and Helvetica Neue font) +- **Docstring format**: [NumPy style](https://numpydoc.readthedocs.io/en/latest/format.html) + parsed by the `mkdocstrings[python]` plugin +- **Plugins**: `mkdocstrings[python]`, `autorefs`, `search` +- **Intersphinx-equivalent**: mkdocstrings `import` option loads inventory files + from Python, pandas, scikit-learn, NumPy, SciPy +- **Cross-references**: Use `[display text][fully.qualified.name]` or + `[fully.qualified.name][]` syntax for linking to documented objects +- **Custom CSS**: `doc/site/_static/css/custom.css` provides Orange branding via CSS + custom properties (Material theme variables) ## Docstring Conventions @@ -166,18 +164,18 @@ Keep concise — use `list of ` for simple cases. For complex containers, ### Type Referencing -Use type referencing (backtick cross-references) only for complex types and -Exceptions. Do not use it for built-in types like `str` or `int`. +Use cross-references only for complex types and Exceptions. Do not use them for +built-in types like `str` or `int`. ``` -# No — str and int link to the Python docs unnecessarily: -some_string : `str` -some_int : `int` +# No — str and int do not need cross-references: +some_string : str +some_int : int # Yes — Khiops internal class: -dictionary : `.Dictionary` +dictionary : `Dictionary` -# Yes — Pandas project class (via intersphinx): +# Yes — Pandas project class (via intersphinx inventory): df : `pandas.DataFrame` # Yes — Exception: @@ -187,22 +185,17 @@ Raises When something wrong happens. ``` -### Cross-References +### Cross-References in Markdown -```rst -`~khiops.core.api.train_predictor` # shows "train_predictor" (short form) -`khiops.core.api.train_predictor` # shows full path -`.train_predictor` # wildcard — works if unambiguous -`train_predictor` # within the same module +```markdown +[train_predictor][khiops.core.api.train_predictor] # shows "train_predictor" +[khiops.core.api.train_predictor][] # shows full path ``` -Use explicit `:func:`, `:class:` domains only for complex types and Exceptions. -The `default_role = "obj"` setting handles most cases. - -## reStructuredText Pitfalls +Use mkdocstrings `:::` directives for API documentation blocks: -The docstrings use **reST, not Markdown**. Key differences: - -- **Lists** require an empty line before the first item -- **Code blocks** use `::` (with empty line + indentation) or `.. code-block:: python` -- **Links**: `` `Link text `_ `` instead of `[text](url)` +```markdown +::: khiops.core.api + options: + heading_level: 3 +``` diff --git a/.github/instructions/docker-changes.instructions.md b/.github/instructions/docker-changes.instructions.md index 4054409c..d80347e3 100644 --- a/.github/instructions/docker-changes.instructions.md +++ b/.github/instructions/docker-changes.instructions.md @@ -106,7 +106,7 @@ containers. ### `api-docs.yml` (Consumer) -Builds the Sphinx documentation inside the `khiopspydev-ubuntu22.04` container. +Builds the Zensical documentation inside the `khiopspydev-ubuntu22.04` container. ## Editing Rules diff --git a/.github/instructions/python-changes.instructions.md b/.github/instructions/python-changes.instructions.md index 6845d824..b536d5e0 100644 --- a/.github/instructions/python-changes.instructions.md +++ b/.github/instructions/python-changes.instructions.md @@ -33,8 +33,8 @@ from `.github/copilot-instructions.md` first, then these Python-specific rules. `line-too-long`, `unused-variable`, and `unused-import`. The overall pylint score must stay at or above `9.9` (`fail-under`). Other pylint warnings are lower priority. -- Pylint is **not run** on `doc/convert_samples.py` and `doc/conf.py` (excluded - in `.pre-commit-config.yaml`). +- Pylint is **not run** on `doc/util/convert_samples.py` (excluded in + `.pre-commit-config.yaml`). - All code and comments must be in English. - `pylint: disable=invalid-name` is used in `khiops/sklearn/estimators.py` to permit scikit-learn's `X`, `y` naming convention. Do not add this suppression @@ -79,7 +79,7 @@ The `.pre-commit-config.yaml` runs the following hooks on Python files: | Hook | Scope | Notes | |---|---|---| | **black** | All `.py` files | Code formatting | -| **pylint** | All `.py` except `doc/convert_samples.py`, `doc/conf.py` | Linting | +| **pylint** | All `.py` except `doc/util/convert_samples.py` | Linting | | **isort** | All `.py` except samples scripts | Import sorting (Black profile) | | **isort-samples** | `khiops/samples/samples.py`, `samples_sklearn.py` | Import sorting with `--no-sections` | -| **samples-generation** | Triggered by changes to samples scripts | Runs `doc/convert-samples-hook` to regenerate reST pages and notebooks | +| **samples-generation** | Triggered by changes to samples scripts | Runs `doc/util/convert-samples-hook` to regenerate Markdown pages and notebooks | diff --git a/.github/workflows/api-docs.yml b/.github/workflows/api-docs.yml index 8603f171..640dc7fb 100644 --- a/.github/workflows/api-docs.yml +++ b/.github/workflows/api-docs.yml @@ -1,54 +1,35 @@ --- name: API Docs env: + DEFAULT_KHIOPS_REVISION: 11.0.1 DEFAULT_KHIOPS_PYTHON_TUTORIAL_REVISION: 11.0.0.0 DEFAULT_KHIOPS_SAMPLES_REVISION: 11.0.0 on: workflow_dispatch: inputs: + khiops-revision: + description: Khiops core version + default: 11.0.1 khiops-python-tutorial-revision: default: 11.0.0.0 description: khiops-python-tutorial repo revision khiops-samples-revision: default: 11.0.0 description: khiops-samples repo revision - image-tag: - default: latest - description: Development Docker Image Tag pull_request: paths: - - doc/**.rst - - doc/create-doc - - doc/clean-doc - - doc/*.py + - doc/site/**.md + - doc/util/create-doc + - doc/util/clean-doc + - doc/util/*.py + - zensical.toml - khiops/**.py - .github/workflows/api-docs.yml - push: - tags: ['*'] -# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages permissions: contents: read - id-token: write - packages: read - pages: write -# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest -# queued. However, do NOT cancel in-progress runs as we want to allow these production deployments -# to complete. -concurrency: - group: pages - cancel-in-progress: false jobs: build: - runs-on: ubuntu-22.04 - container: - # 'latest' default image tag cannot be set as an environment variable, - # because the `env` context is only accessible at the step level; - # hence, it is hard-coded - image: |- - ghcr.io/khiopsml/khiops-python/khiopspydev-ubuntu22.04:${{ inputs.image-tag || 'latest' }} - # Use the 'runner' user (1001) from github so checkout actions work properly - # https://github.com/actions/runner/issues/2033#issuecomment-1598547465 - options: --user 1001 + runs-on: ubuntu-24.04 steps: - name: Checkout khiops-python uses: actions/checkout@v4 @@ -56,64 +37,52 @@ jobs: fetch-depth: 0 - name: Add pip scripts directory to path run: echo PATH="$PATH:/github/home/.local/bin" >> "$GITHUB_ENV" + - name: Setup uv + uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + cache-dependency-glob: uv.lock + - name: Install Khiops core + run: | + # Create the .venv virtual environment and activate it + uv venv + . .venv/bin/activate + + # Building the Python API docs always executes khiops-python's own + # tutorial notebooks (create-doc -t -d), so a real Khiops core + # install is required unconditionally. + uv pip install khiops-core==${{ inputs.khiops-revision || env.DEFAULT_KHIOPS_REVISION }} - name: Install doc build requirements run: | # Install package itself to install the samples datasets - python -m pip install --upgrade pip # Add homogeneous TOML support (Python >= 3.12 has standard tomllib) - python -m pip install tomli + uv pip install tomli # First, install all dependencies except khiops-core and khiops-drivers-* - python scripts/extract_dependencies_from_pyproject_toml.py -f "pyproject.toml" --exclude-khiops-family > requires-no-khiops.txt - python -m pip install --user `cat requires-no-khiops.txt` + uv run scripts/extract_dependencies_from_pyproject_toml.py -f "pyproject.toml" --exclude-khiops-family > requires-no-khiops.txt + uv pip install `cat requires-no-khiops.txt` # khiops-core and khiops-drivers-* must always be installed from TestPyPI in order to avoid distorting usage statistics - python scripts/extract_dependencies_from_pyproject_toml.py -f "pyproject.toml" --khiops-family-only > requires-khiops.txt - python -m pip install --user --index-url https://test.pypi.org/simple `cat requires-khiops.txt` + uv run scripts/extract_dependencies_from_pyproject_toml.py -f "pyproject.toml" --khiops-family-only > requires-khiops.txt + uv pip install --index-url https://test.pypi.org/simple `cat requires-khiops.txt` rm -f requires-khiops.txt requires-no-khiops.txt # Lastly, install khiops-python - python -m pip install --user . - kh-download-datasets --force-overwrite --version ${{ inputs.khiops-samples-revision || env.DEFAULT_KHIOPS_SAMPLES_REVISION }} - kh-status + uv pip install . + uv run kh-download-datasets --force-overwrite --version ${{ inputs.khiops-samples-revision || env.DEFAULT_KHIOPS_SAMPLES_REVISION }} + + # Discard error as this is expected (Uv is not yet officially + # supported by khiops, but this should pose no issue in practice) + # TODO: Revert as soon as issue + # https://github.com/KhiopsML/khiops-python/issues/602 is tackled + uv run kh-status || true # Install the doc python requirements - cd doc - python -m pip install -U -r requirements.txt + uv pip install -U -r doc/util/requirements.txt # Clone the Khiops Python tutorial repository while building the documentation - - name: Build Sphinx Documentation + - name: Build Zensical Documentation run: | - cd doc - ./create-doc -t -d -g \ + doc/util/create-doc -t -d -g \ ${{ inputs.khiops-python-tutorial-revision || env.DEFAULT_KHIOPS_PYTHON_TUTORIAL_REVISION }} - name: Upload the docs as an artifact uses: actions/upload-artifact@v4 with: name: api-docs - path: ./doc/_build/html/ - # Release on Git tag - release: - if: github.ref_type == 'tag' - needs: build - runs-on: ubuntu-22.04 - permissions: - contents: write - steps: - - name: Download docs artifact - uses: actions/download-artifact@v4 - with: - name: api-docs - path: ./doc/_build/html/ - - name: Create docs release zip archive - uses: thedoctor0/zip-release@0.7.6 - with: - type: zip - path: ./doc/_build/html/ - filename: khiops-api-docs-${{ github.ref_name }}.zip - - name: Release the docs zip archive - uses: ncipollo/release-action@v1.15.0 - with: - allowUpdates: true - artifacts: ./khiops-api-docs-${{ github.ref_name }}.zip - body: '**For testing purposes only**' - draft: false - makeLatest: false - prerelease: true - updateOnlyUnreleased: true + path: ./doc/build/html/ diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml index 7f5733a6..50301007 100644 --- a/.github/workflows/pip.yml +++ b/.github/workflows/pip.yml @@ -54,7 +54,7 @@ jobs: python -m build --sdist # Get built package version and make it accessible to subsequent jobs - PACKAGE_VERSION=$(python -m build --metadata | jq ".version" | tr -d '"') + PACKAGE_VERSION=$(python -m build --metadata | grep -w "version" | cut -d':' -f 2 | sed 's/[ ",]//g') echo "PACKAGE_VERSION=$PACKAGE_VERSION" >> "$GITHUB_OUTPUT" - name: Upload the package as artifact uses: actions/upload-artifact@v4 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9939c5e5..9de94338 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -231,6 +231,14 @@ jobs: # Get Git tags so that versioneer can function correctly # See issue https://github.com/actions/checkout/issues/701 fetch-depth: 0 + # We move SAMPLES_REVISION to the environment so that we can use + # them in both push and workflow_dispatch events + - name: Set SAMPLES_REVISION on 'pull_request' events + if: github.event_name == 'pull_request' + run: echo "SAMPLES_REVISION=${DEFAULT_SAMPLES_REVISION}" >> "$GITHUB_ENV" + - name: Set SAMPLES_REVISION on 'workflow_dispatch' events + if: github.event_name == 'workflow_dispatch' + run: echo "SAMPLES_REVISION=${{ inputs.samples-revision }}" >> "$GITHUB_ENV" - name: Checkout Khiops samples uses: actions/checkout@v4 with: @@ -363,6 +371,14 @@ jobs: # Get Git tags so that versioneer can function correctly # See issue https://github.com/actions/checkout/issues/701 fetch-depth: 0 + # We move SAMPLES_REVISION to the environment so that we can use + # them in both push and workflow_dispatch events + - name: Set SAMPLES_REVISION on 'pull_request' events + if: github.event_name == 'pull_request' + run: echo "SAMPLES_REVISION=${DEFAULT_SAMPLES_REVISION}" >> "$GITHUB_ENV" + - name: Set SAMPLES_REVISION on 'workflow_dispatch' events + if: github.event_name == 'workflow_dispatch' + run: echo "SAMPLES_REVISION=${{ inputs.samples-revision }}" >> "$GITHUB_ENV" - name: Checkout Khiops samples uses: actions/checkout@v4 with: diff --git a/.gitignore b/.gitignore index 50f7890f..2b5d9c6a 100644 --- a/.gitignore +++ b/.gitignore @@ -52,14 +52,14 @@ tests/resources/*/copy_output_kdic # File generated by documentation scripts/notebooks kh_samples -doc/tutorials/ -!doc/tutorials/index.rst -doc/samples/*.txt -doc/samples/kh_samples -doc/samples/my_classifier -doc/core/generated -doc/sklearn/generated -doc/internal/generated -doc/tools/generated -doc/_build/ -doc/khiops-python-tutorial +doc/site/tutorials +!doc/site/tutorials/index.md +doc/site/samples/*.txt +doc/site/samples/kh_samples +doc/site/samples/my_classifier +doc/site/samples/samples.ipynb +doc/site/samples/samples.py +doc/site/samples/samples_sklearn.ipynb +doc/site/samples/samples_sklearn.py +doc/build +doc/util/khiops-python-tutorial diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cd322130..d9dd362d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,7 +10,7 @@ repos: hooks: - id: pylint language_version: python3 - exclude: doc/convert_samples.py|doc/conf.py + exclude: doc/util/convert_samples.py - repo: https://github.com/pycqa/isort rev: 5.13.2 hooks: @@ -47,7 +47,7 @@ repos: hooks: - id: samples-generation name: samples-generation - entry: bash doc/convert-samples-hook + entry: bash doc/util/convert-samples-hook language: system files: khiops/samples/samples.py|khiops/samples/samples_sklearn.py pass_filenames: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 60d73f76..3aa117f9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -232,8 +232,8 @@ Note that these 2 packages already have a sizable number of dependencies. We sho thoroughly the pros and cons of any new external package dependency before adding it. ### Development/Build dependencies -For development dependencies (eg. `black`, `isort`, `sphinx`, `wrapt`, `furo`) we can be more -carefree while still trying not to add too many dependencies. +For development dependencies (eg. `black`, `isort`, `zensical`, `wrapt`) we can +be more carefree while still trying not to add too many dependencies. ### Python Support Policy Our Python support policy is the following: diff --git a/doc/README.md b/doc/README.md index 62086e55..4bc09004 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,4 +1,4 @@ -# Khiops Python Library Documentation² +# Khiops Python Library Documentation The documentation of the documentation. Below you'll find the tools and practices related to the documentation of the @@ -6,54 +6,53 @@ Khiops Python library. ## Build the documentation ```bash -# Working dir = khiops/doc +# Working dir = khiops-python (repository root) -# You'll need the python packages in the requirements.txt file in this directory -# Warning: If you create a virtualenv, do not place it within the khiops/doc directory. -# The installed packages contain reST files and Sphinx will process them! -# pip install -U -r requirements.txt - -# You'll also need a system-wide installation of pandoc (https://pandoc.org) +# You'll need the python packages in the doc/util/requirements.txt file +# Warning: If you create a virtualenv, do not place it within the doc/util +directory. The installed packages may contain .md files and Zensical will +process them! # Execute this if there were non committed updates to samples.py or samples_sklearn.py: -# ./convert-samples-hook +# doc/util/convert-samples-hook # To clean the html documentation -# ./clean-doc - +# doc/util/clean-doc -# Create the HTML documentation ( +# Create the HTML documentation: # - Downloads the khiops-python-tutorial resources -# - Generates the reST version of the tutorials -# - Executes Sphinx (output: ./_build/html) -./create-doc -d -t +# - Generates the Markdown version of the tutorials +# - Executes Zensical (output: doc/build/html) +doc/util/create-doc -d -t -# To only execute Sphinx on updated reST resources -# sphinx-build -M html . _build/ -``` +# To only execute Zensical on updated Markdown resources +# zensical build -## Sphinx -We use [Sphinx](https://www.sphinx-doc.org/en/master/) to generate the documentation and -the [Numpy docstring format](https://numpydoc.readthedocs.io/en/latest/format.html). +# To serve locally for development (with live reload) +# zensical serve +``` -Within Sphinx we use the following extensions: -- `numpydoc`: Parses the numpydoc docstrings **and** creates compact reST output. -- `sphinx.ext.autdoc`: Automatically creates the documentation from docstrings. -- `sphinx.ext.autosummary`: Automatically creates summaries from the module structure. It depends - on `autodoc`. -- `sphinx.ext.intersphinx`: Creates links to other Sphinx-generated sites (eg. Python doc, Pandas - doc). -- `sphinx_copybutton`: Puts a copy button in all code snippets within the documentation. +## Zensical +We use [Zensical](https://www.zensical.org/) with the +[Material](https://squidfunk.github.io/mkdocs-material/) theme to generate the +documentation and the +[NumPy docstring format](https://numpydoc.readthedocs.io/en/latest/format.html). -Any of these extensions can be the culprit of bogus or invalid output. Note that the Sphinx -documentation for some of these extensions is not complete and even answers in StackOverflow are -not up to date. +The Zensical configuration file `zensical.toml` lives at the repository root. -Warnings emitted by Sphinx **should not be ignored** as there are most likely rendering errors. +The following MkDocs plugins and extensions are used in Zensical through +Zensical's MkDocs compatibility layer: +- `mkdocstrings[python]`: Automatically creates API documentation from Python docstrings + (NumPy format). Replaces Sphinx's `autodoc`, `autosummary`, and `numpydoc`. +- `autorefs`: Enables cross-references to documented objects across pages. +- `search`: Built-in search functionality. +- `pymdownx.superfences`, `pymdownx.highlight`: Fenced code blocks with syntax highlighting + and a copy button. +- `admonition`, `pymdownx.details`: Note/warning/tip admonition blocks. -[reStructuredText](https://docutils.sourceforge.io/rst.html) or *reST* is the input format of -Sphinx. One important thing that while very similar **it is not Markdown**, reST is not identical -to it. See below [reSTructuredText Common Problems](#restructuredtext-common-problems). +Cross-references to external projects (Python, pandas, scikit-learn, NumPy, SciPy) are +handled via `objects.inv` inventory files configured in the `mkdocstrings` handler's +`import` option. ## Khiops Python Docstring Patterns @@ -122,7 +121,7 @@ some_parameter : str, optional ``` ### Verbatim -Use verbatim (double backticks ` `` `) in mid-sentence for: +Use verbatim (backticks `` ` ``) in mid-sentence for: - Common Python constants (`True`, `None`) - File names and extensions - Parameter names @@ -133,7 +132,7 @@ Do not use verbatim for ``` # No: -some_string : ``AValue`` or ``AnotherValue`` +some_string : "AValue" or "AnotherValue" some_boolean : optional, default "True" @@ -141,18 +140,18 @@ dictionary_file : str With extension ".kdic" some_parameter : int - When greater than ``0`` affects "other_parameter" + When greater than `0` affects "other_parameter" # Yes: some_string : "AValue" or "AnotherValue" -some_boolean : bool, default ``True`` +some_boolean : bool, default `True` dictionary_file : str - A file with extension ``.kdic`` + A file with extension `.kdic` some_parameter : int - When greater than 0 affects ``other_parameter`` + When greater than 0 affects `other_parameter` ``` ### Container Types @@ -178,149 +177,58 @@ For container types such as `dict` describe the keys and the values types in the Use type referencing only for complex types and Exceptions ```python # No: -# int and str point to the Python docs (via intersphinx) -some_string : `str` +# int and str do not need cross-references +some_string : str a string -some_int : `int` +some_int : int an int # Yes: # Khiops internal class -dictionary : `.Dictionary` +dictionary : `Dictionary` A Khiops dictionary. -# Pandas project class (via intersphinx) +# Pandas project class (via intersphinx inventory) df : `pandas.DataFrame` A dataframe. -# Python project class (via intersphinx) +# Exception Raises ------ `ValueError` When something wrong happens. ``` -## reStructuredText Common Problems +## Cross-References in Markdown -There are three common cases where the differences may pose problems: lists, monospaced blocks and -links. +The documentation pages use Markdown, not reST. Cross-references use the +mkdocstrings/autorefs syntax: -### Lists -In reST lists *must* have an empty line before and when nesting. So the following Markdown list: -``` -These are some letters: -- A -- B -- C -- D -The end -``` -must be written in the following way: -``` -These are some letters: +```markdown +# Link showing "train_predictor" +[train_predictor][khiops.core.api.train_predictor] -- A -- B -- C -- D -The end -``` -Now, because Sphinx makes many transformations (docstring -> reST -> HTML) *sometimes* it is -possible to get away without the extra spaces or by indenting the lists. But this is context -dependent and one must check if the output is the desired one (no warnings is a good sign). - -### Monospaced Blocks -Consider the following monospaced block in Markdown -```` -The following is a monospaced text: -``` -Some -|- monospaced -|- text +# Link showing the full path +[khiops.core.api.train_predictor][] ``` -Nice figure -```` -In reST there are at least two ways -``` -The following is a monospaced text: -:: - Some - |- monospaced - |- text +For API documentation blocks, use the `:::` directive: -Nice figure -``` -or the more compact +```markdown +::: khiops.core.api + options: + heading_level: 3 ``` -The following is a monospaced text:: - Some - |- monospaced - |- text +### Admonitions +Notes and warnings use the `!!!` syntax: -Nice figure -``` -Note that is necessary -- an empty line after the `::` operator and after the monospaced text -- an indentation of the monospaced text. - -A third way allows to specify programming languages -``` -Some python code -.. code-block:: python +```markdown +!!! note + This is a note. - import pprint - pprint.print("hola") - -That was a nice snippet. -``` -Note again that indentation and empty lines are necessary. -### Links and Cross References -The external URL link in Markdown -``` -[Python website](https://www.python.org) -``` -can be expressed in reST as +!!! warning + This is a warning. ``` -`Python website `_ -``` -or -``` -`Python website`_ - -.. _`Python website`: https://www.python.org -``` - -For internal URL links the Sphinx semantics called *domains* help to reference diverse -elements of the module. For example the `train_predictor` function of the core API -belongs to the `:func:` domain so we can reference it as: -``` -:func:`khiops.core.api.train_predictor` -``` -This will show the long link `khiops.core.api.train_predictor`. Adding a `~` before -the path makes the link show only the last component `train_predictor`. -``` -:func:`~khiops.core.api.train_predictor` -``` -Additionally, we configured Sphinx with -```python -# In conf.py file -default_object = 'obj' -``` -which allows to not use the *domain* most of the time. So the link can be further shortened to -``` -`~khiops.core.api.train_predictor` -``` -and if we were referencing within the `khiops.core.api` module one can simply write -``` -`train_predictor` -``` -Outside the module there is a compact way to reference it with a _wildcard_ -``` -`.train_predictor` -``` -but be careful about name collisions. - -See the Sphinx documentation for more information about referencing. +See the [Zensical admonitions docs](https://zensical.org/docs/authoring/admonitions/) for more details. diff --git a/doc/_static/css/custom.css b/doc/_static/css/custom.css deleted file mode 100644 index 9868fd38..00000000 --- a/doc/_static/css/custom.css +++ /dev/null @@ -1,24 +0,0 @@ -/* Custom header sizes */ -h1 { - font-size: 1.85em; -} - -h2 { - font-size: 1.55em; -} - -h3 { - font-size: 1.30em; -} - -h4 { - font-size: 1.10em; -} - -h5 { - font-size: 0.90em; -} - -img.sidebar-logo { - width: 40px; -} diff --git a/doc/_templates/autosummary/class.rst b/doc/_templates/autosummary/class.rst deleted file mode 100644 index 154ffb7e..00000000 --- a/doc/_templates/autosummary/class.rst +++ /dev/null @@ -1,29 +0,0 @@ -{{ name | escape | underline}} -Class in module ``{{ module }}`` - -.. currentmodule:: {{ module }} - -.. autoclass:: {{ objname }} - - {% block methods %} - {% if methods %} - .. rubric:: {{ _('Methods') }} - .. autosummary:: - :nosignatures: - {% for item in methods %} - {% if item != "__init__" %} - ~{{ item }} - {% endif %} - {%- endfor %} - {% endif %} - {% endblock %} - - {% block attributes %} - {% if attributes %} - .. rubric:: {{ _('Attributes') }} - .. autosummary:: - {% for item in attributes %} - ~{{ name }}.{{ item }} - {%- endfor %} - {% endif %} - {% endblock %} diff --git a/doc/_templates/autosummary/function.rst b/doc/_templates/autosummary/function.rst deleted file mode 100644 index 5536fa10..00000000 --- a/doc/_templates/autosummary/function.rst +++ /dev/null @@ -1,5 +0,0 @@ -{{ name | escape | underline}} - -.. currentmodule:: {{ module }} - -.. auto{{ objtype }}:: {{ objname }} diff --git a/doc/_templates/autosummary/method.rst b/doc/_templates/autosummary/method.rst deleted file mode 100644 index 45f9c962..00000000 --- a/doc/_templates/autosummary/method.rst +++ /dev/null @@ -1,5 +0,0 @@ -{{ "EU + " name | escape | underline}} - -.. currentmodule:: {{ module }} - -.. auto{{ objtype }}:: {{ objname }} diff --git a/doc/_templates/autosummary/module.rst b/doc/_templates/autosummary/module.rst deleted file mode 100644 index 9a384637..00000000 --- a/doc/_templates/autosummary/module.rst +++ /dev/null @@ -1,68 +0,0 @@ -{{ fullname | replace("khiops.", "") | escape | underline }} - -Submodule of ``{{ module }}`` - -.. automodule:: {{ fullname }} - - {% block attributes %} - {% if attributes %} - .. rubric:: {{ _('Module Attributes') }} - - .. autosummary:: - {% for item in attributes %} - {{ item }} - {%- endfor %} - {% endif %} - {% endblock %} - - {% block functions %} - {% if functions %} - {{ _('Functions') }} - -------------------- - - .. autosummary:: - :nosignatures: - {% for item in functions %} - {{ item }} - - {%- endfor %} - {% endif %} - {% endblock %} - - {% block classes %} - {% if classes %} - {{ _('Classes') }} - ------------------ - - .. autosummary:: - :nosignatures: - {% for item in classes %} - {{ item }} - {%- endfor %} - {% endif %} - {% endblock %} - - {% block exceptions %} - {% if exceptions %} - .. rubric:: {{ _('Exceptions') }} - - .. autosummary:: - {% for item in exceptions %} - {{ item }} - {%- endfor %} - {% endif %} - {% endblock %} - - {% block modules %} - {% if modules %} - Modules - ------- - - .. autosummary:: - :toctree: - :recursive: - {% for item in modules %} - {{ item }} - {%- endfor %} - {% endif %} - {% endblock %} diff --git a/doc/conf.py b/doc/conf.py deleted file mode 100644 index 7b9cb553..00000000 --- a/doc/conf.py +++ /dev/null @@ -1,148 +0,0 @@ -###################################################################################### -# Copyright (c) 2023-2025 Orange. All rights reserved. # -# This software is distributed under the BSD 3-Clause-clear License, the text of # -# which is available at https://spdx.org/licenses/BSD-3-Clause-Clear.html or # -# see the "LICENSE.md" file for more details. # -###################################################################################### -"""Khiops Python Sphinx configuration file""" - -import os -import sys -from datetime import datetime - -import numpydoc - -# Add the root of the repository and the samples directory to sys.path -# so Sphinx can find both khiops and the samples scripts -sys.path.append("..") -sys.path.append("../khiops/samples") -import khiops - -project = "Khiops Python" -copyright = f"2018-{datetime.today().year}, Orange" -author = "The Khiops Team" - -# The full version, including alpha/beta/rc tags -release = khiops.__version__ - -# Be strict about any broken references -nitpicky = True - -# To avoid using qualifiers like :class: to reference objects within the same context -default_role = "obj" - -# Sphinx extensions -extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.autosummary", - "sphinx.ext.intersphinx", - "numpydoc", - "sphinx_copybutton", -] - -## Numpydoc extension config -numpydoc_show_class_members = False - -## Autodoc extension config -autodoc_default_options = { - "members": True, - "inherited-members": False, - "private-members": False, - "show-inheritance": True, - "special-members": False, -} - -## Intersphinx extension config -intersphinx_mapping = { - "python": ("https://docs.python.org/3", None), - "pandas": ("https://pandas.pydata.org/pandas-docs/dev", None), - "sklearn": ("https://scikit-learn.org/stable", None), - "numpy": ("https://numpy.org/doc/stable", None), - "scipy": ("https://docs.scipy.org/doc/scipy", None), -} - -## Autosummary extension config -templates_path = ["_templates"] - -# List of patterns, relative to source directory, that match files and directories to -# ignore when looking for source files. -# This pattern also affects html_static_path and html_extra_path. -exclude_patterns = [ - "_templates", - "_build", - "Thumbs.db", - ".DS_Store", - "**.ipynb_checkpoints", -] -# HTML Theme -# Theme colors and fonts come from https://brand.orange.com -html_theme = "furo" -html_theme_options = { - "light_css_variables": { - "color-brand-primary": "#FF7900", - "color-brand-content": "#F16E00", - "color-brand-visited": "#FF7900", - "color-sidebar-background": "#FFFFFF", - "color-highlighted-background": "#FFD200", - "color-admonition-title--note": "#FF7900", - "color-admonition-title-background--note": "#FFF0E2", - "font-stack": "Helvetica Neue, Helvetica, sans-serif", - }, - "dark_css_variables": { - "color-brand-primary": "#FF7900", - "color-brand-content": "#F16E00", - "color-brand-visited": "#FF7900", - "color-sidebar-background": "#000000", - "color-highlighted-background": "#FFD200", - "color-admonition-title--note": "#FF7900", - "color-admonition-title-background--note": "#CC6100", - "font-stack": "Helvetica Neue, Helvetica, sans-serif", - }, - # Sets the Github Icon (the SVG is embedded, copied from furo's repo) - "footer_icons": [ - { - "name": "GitHub", - "url": "https://github.com/khiopsml/khiops-python", - "html": """ - - - - """, - "class": "", - }, - ], -} -html_title = f"
{project} {release}
" - -# HTML static assets -html_static_path = ["_static"] -html_logo = "_static/images/orange_small_logo.png" -html_css_files = ["css/custom.css"] - - -# Callback to Suppress warnings: -# - about sklearn code (`X` or `y`) included via intersphinx -# - about some literals included via the tutorials transformation -def suppress_sklearn_warnings(app, env, node, contnode): - def sklearn_not_found_variable(node): - return ( - node.rawsource == "`X`" - or node.rawsource == "`y`" - or node.rawsource == '`"default"`' - or node.rawsource == '`"pandas"`' - ) and node.attributes["py:module"] == "khiops.sklearn.estimators" - - def tutorial_literal(node): - return ( - node.rawsource == "`ProbClassIris-setosa`" - or node.rawsource == "`ProbClassIris-versicolor`" - or node.rawsource == "`ProbClassIris-virginica`" - ) - - if sklearn_not_found_variable(node) or tutorial_literal(node): - return contnode - return None - - -def setup(app): - app.connect("missing-reference", suppress_sklearn_warnings) diff --git a/doc/convert-samples-hook b/doc/convert-samples-hook deleted file mode 100755 index ecaa849d..00000000 --- a/doc/convert-samples-hook +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# Set the documentation and samples directories -DOC_DIR=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) -SAMPLES_DIR="$(dirname "$DOC_DIR")/khiops/samples" - -# Generate the rEST pages -python "$DOC_DIR/convert_samples.py" -f rst "$SAMPLES_DIR" "$DOC_DIR/samples/samples.rst" -python "$DOC_DIR/convert_samples.py" --sklearn -f rst "$SAMPLES_DIR" "$DOC_DIR/samples/samples_sklearn.rst" - -# Generate the notebooks -python "$DOC_DIR/convert_samples.py" "$SAMPLES_DIR" "$SAMPLES_DIR/samples.ipynb" -python "$DOC_DIR/convert_samples.py" --sklearn "$SAMPLES_DIR" "$SAMPLES_DIR/samples_sklearn.ipynb" diff --git a/doc/core/index.rst b/doc/core/index.rst deleted file mode 100644 index 74c33bee..00000000 --- a/doc/core/index.rst +++ /dev/null @@ -1,28 +0,0 @@ -============= -khiops.core -============= -.. automodule:: khiops.core - -.. note:: - Input types in this module are flexible. See :doc:`../notes`. - -.. note:: - For convenience, the public members of the above modules are imported to the ``core`` namespace. - For example the function `~.api.train_predictor` can be used as follows:: - - from khiops import core as kh - kh.train_predictor(...) - -Main Modules -============ -.. autosummary:: - :toctree: generated - :recursive: - :nosignatures: - - api - dictionary - analysis_results - coclustering_results - exceptions - helpers diff --git a/doc/index.rst b/doc/index.rst deleted file mode 100644 index 42c0f3f2..00000000 --- a/doc/index.rst +++ /dev/null @@ -1,77 +0,0 @@ - -Khiops Python API Docs -====================== - -Welcome to the Khiops Python API documentation page. - -Installation ------------- -Khiops can be installed in a Python virtual environment using ``pip``, -under Linux and macOS (in a ``bash`` shell): - -.. code-block:: bash - - python -m venv khiops-venv - source khiops-venv/bin/activate - pip install -U khiops - -under Windows (in a ``powershell`` shell) : - -.. code-block:: powershell - - python -m venv khiops-venv - khiops-venv\Scripts\activate - pip install -U khiops - -Alternatively, you can install Khiops with the `Conda package manager `_ - -.. code-block:: bash - - conda create -n khiops-env - conda activate khiops-env - conda install -c conda-forge khiops - -More details and other installation methods are documented at the `Khiops website -`_. - - -Main Submodules ---------------- -This package contains the following main submodules. - -``sklearn`` submodule -~~~~~~~~~~~~~~~~~~~~~ -The :doc:`sklearn/index` module is a `Scikit-learn `_ based interface to -Khiops. Use it if you are just started using Khiops and are familiar with the Scikit-learn workflow -based on dataframes and estimator classes. - -``core`` submodule -~~~~~~~~~~~~~~~~~~ -The :doc:`core/index` module is a pure Python library exposing all Khiops functionalities. Use it if -you are familiar with the Khiops workflow based on plain-text tabular data files and dictionary -files (``.kdic``). - -.. toctree:: - :caption: API Reference - :hidden: - - sklearn - core - tools - internals - -.. toctree:: - :caption: Tutorials and Code Samples - :hidden: - - Tutorials - samples/samples_sklearn - Samples core - -.. toctree:: - :caption: Other Topics - :hidden: - - multi_table_primer - notes - diff --git a/doc/internal/index.rst b/doc/internal/index.rst deleted file mode 100644 index 1531433b..00000000 --- a/doc/internal/index.rst +++ /dev/null @@ -1,25 +0,0 @@ -Internals -========= -These are internal modules with no "data science" functionality. Their documentation is available -for completeness. - -.. currentmodule:: khiops.sklearn -.. autosummary:: - :nosignatures: - :toctree: generated - - dataset - -.. currentmodule:: khiops.core.internals -.. autosummary:: - :nosignatures: - :toctree: generated - - common - filesystems - io - runner - scenario - task - types - version diff --git a/doc/multi_table_primer.rst b/doc/multi_table_primer.rst deleted file mode 100644 index 1633f329..00000000 --- a/doc/multi_table_primer.rst +++ /dev/null @@ -1,360 +0,0 @@ -=========================== -Multi-Table Learning Primer -=========================== - -Machine learning on tabular data is traditionally performed on a single table containing a record -for each statistical object of the sample. However, data is usually stored in databases with -multiple tables whose relationships are specified through a schema. Thus, before training -a predictor, a preprocessing is necessary to flatten the relational tables into a single one -containing all relevant information for the learning task. - -This preprocessing (which belongs to the feature engineering process) is often very time consuming. -One of the main Khiops features is to automate this process by natively learning predictors from -multi-table datasets. Specifically, Khiops automatically: - -- generates features (aggregates) from the multi-table schema -- evaluates the predictive value of the generated features -- selects a small subset of the generated features to build a predictor - -Supported Multi-Table Schemas -============================= -Khiops allows to train an estimator on the two most common dataset schemas: *star* and *snowflake*. -More complex schemas are supported only by the ``core`` library at the moment. - -Star Schema ------------ -In this schema there is one main table containing the identifier of the statistical object and its -basic properties. The main table points to one or more secondary ones, each having or more records -associated to a given object. - -Here is a simple example of a star schema (for brevity we do not show the tables' columns): - -.. code-block:: text - - Customer(id_customer) - | - +---1:1--- Address(id_customer) - | - +---1:n--- Service(id_customer, id_product) - -The statistical object in this schema is a ``Customer`` that is associated to a unique ``Address`` -and to one or more ``Services``. In parentheses we show the key columns of each table that act as -`foreign keys `_ to associate the records of each table. - -You can find more information about the star schema in its `Wikipedia article -`_. - -Snowflake Schema ----------------- -The snowflake schema generalizes the star schema by allowing each secondary table to be in a star -schema by itself, forming a tree whose root is the main table. - -Here we extend the previous star schema to a snowflake schema - -.. code-block:: text - - Customer(id_customer) - | - +---1:1--- Address(id_customer) - | - +---1:n--- Service(id_customer, id_product) - | - +---1:n--- Usage(id_customer, id_product) - -Again, the statistical object in this schema is a ``Customer`` that is associated to a unique -``Address`` and to one or more ``Services``. But additionally, each ``Service`` is associated to one -or more ``Usages``. - -You can find more information about the snowflake schema in `its Wikipedia article -`_. - -Multi-Table Learning with Scikit-Learn Estimators -================================================= - -The supervised estimators in :doc:`sklearn/index` handle multi-table datasets with a special input -feature object ``X``. Specifically, instead of a `pandas.DataFrame`, ``X`` must be a ``dict`` that -specifies the dataset schema in the following way:: - - X = { - "main_table": (, ), - "additional_data_tables" : { - : ( - , [], - ), - : ( - , [], - ), - ... - } - } - -The three fields of this dictionary are: - -- ``main_table``: a 2-tuple containing the following fields: - - The `pandas.DataFrame` object of the main table. - - The key columns' names: A list of strings. - . -- ``additional_data_tables``: A dictionary indexed by the data paths to the secondary - tables. Each data path is associated to a 2-tuple containing the following fields: - - - The `pandas.DataFrame` object of the secondary table. - - The key columns' names : A list of strings. - - optionally, a flag which indicates if the secondary table is in - a ``1:1`` relationship to its parent table. - -.. note:: - - With respect to Khiops, Khiops Python sklearn estimators have some limitations. They currently do not - support external data tables. - - This feature will be available in upcoming releases. If you need to use it, you can use the `khiops.core` - sub-module (see below). - -Examples --------- - -Star Schema -~~~~~~~~~~~ -For the ``AccidentsSummary`` dataset above where tables are related through the following *star* -schema: - -.. code-block:: text - - Accident(AccidentId) - | - +---1:n--- Vehicle(AccidentId, VehicleId) - -We build the input ``X`` as follows:: - - accidents_df = pd.read_csv(f"{kh.get_samples_dir()}/AccidentsSummary/Accidents.txt", sep="\t") - vehicles_df = pd.read_csv(f"{kh.get_samples_dir()}/AccidentsSummary/Vehicles.txt", sep="\t") - X = { - "main_table" : (accidents_df.drop("Gravity", axis=1), ["AccidentId"]), - "additional_data_tables": { - "Vehicle": (vehicles_df, ["AccidentId", "VehicleId"]) - } - } - - -Snowflake Schema -~~~~~~~~~~~~~~~~ - -For the ``Accidents`` dataset (an extension of ``AccidentsSummary``) where tables are related -through the following *snowflake* schema - -.. code-block:: text - - Accident(AccidentId) - | - +--- 1:n --- Vehicle(AccidentId, VehicleId) - | | - | +--- 1:n --- User(AccidentId, VehicleId) - | - +--- 1:1 --- Place(AccidentId) - -We build the input ``X`` as follows:: - - accidents_df = pd.read_csv(f"{kh.get_samples_dir()}/Accidents/Accidents.txt", sep="\t") - vehicles_df = pd.read_csv(f"{kh.get_samples_dir()}/Accidents/Vehicles.txt", sep="\t") - users_df = pd.read_csv(f"{kh.get_samples_dir()}/Accidents/Users.txt", sep="\t") - places_df = pd.read_csv(f"{kh.get_samples_dir()}/Accidents/Places.txt", sep="\t") - - X = { - "main_table": (accidents_df.drop("Gravity", axis=1), ["AccidentId"]), - "additional_data_tables": { - "Vehicles": (vehicles_df, ["AccidentId", "VehicleId"]), - "Vehicles/Users": (users_df, ["AccidentId", "VehicleId"]), - "Places": (places_df, ["AccidentId"], True), - }, - } - -Both datasets can be found in the Khiops samples directory. - -Multi-table learning with the Core API -====================================== - -The functions in `khiops.core` that allow using multi-table datasets have the optional parameter -``additional_data_tables``. This dictionary links the secondary tables to their data file paths and -it's indexed by their **data paths** which are specified as the regular expression:: - - (/external_entity_table_name)?(/table_variable_name)* - -Specifically: - -- the data path for an *external entity* table (see below) is its name, preceded by a forward slash -- the data path for a secondary table is composed of the data path of its source root table if it - is an external entity table followed by the chain of *table variable* names leading to it. - The path parts are separated by a forward slash ``/``. - -Types of secondary tables include: - -- ``Table`` type: sub-tables in a 0:n relationship - - - Example: A "Customers" main table with a "Services" secondary table describing the services that - each customer has subscribed to. So a customer can have zero services (inactive customer) or one - or many. - -- ``Entity`` table: sub-tables in a 0:1 relationship - - - Example: A "Customers" main table with a "Address" secondary table describing the address of - a customer with fields such as "Street", "StreetNumber", etc. In this setting a customer can - have at most one address. - -- External data tables: Another table set (with a ``Root`` table) that is entirely loaded in - memory - - - Example: The "Address" sub-table in the example above can point to a table "City" containing - information about the city where the address is located. The number of cities is much smaller - than the number of addresses so it may make sense to load it entirely in memory for efficiency - reasons. - - .. note:: In `.khiops.core.api.evaluate_predictor`, the initial name of the - external table must be used, which is the same as the one used for - training the predictor. - For example: - - .. code-block:: c - - additional_data_tables: { - "/City": "/path/to/Cities.csv" - } - - .. note:: In `.khiops.core.api.deploy_model`, model dictionary data paths must be used for - external tables. For example, when deploying a classification or a regression - model: - - .. code-block:: c - - additional_data_tables: { - "/SNB_City": "/path/to/Cities.csv" - } - - -Note that besides the root table names the components of a data path are **table variable names** -and not *table names*. For further details about the multi-table capabilities of Khiops refer to the -documentation at `the Khiops site `_. - -The class `.DictionaryDomain` provides the helper method `.extract_data_paths` that extracts the -data paths from a given root dictionary. - -.. note:: - To execute multi-table tasks, Khiops requires the data table files **to be sorted** by their key - columns. You may use the `~.api.sort_data_table` function to preprocess your data files before - executing these tasks. - -Examples --------- - -Star Schema -~~~~~~~~~~~ - -Let's consider the following Khiops dictionary file for the ``AccidentsSummary`` dataset -found in Khiops samples. Note that tables in this dataset are related through a *star* schema. - -.. code-block:: c - - # samples/AccidentsSummary/Accidents.kdic - Root Dictionary Accident(AccidentId) - { - Categorical AccidentId; - Categorical Gravity; - // - Table(Vehicle) Vehicles; // This is a table variable (type Table) - }; - - Dictionary Vehicle(AccidentId, VehicleId) - { - Categorical AccidentId; - Categorical VehicleId; - Categorical Direction; - Categorical Category; - // - }; - -This dictionary represents the following relational schema: - -.. code-block:: text - - Accident(AccidentId) - | - +---1:n--- Vehicle(AccidentId, VehicleId) - - -In this case the ``additional_data_tables`` argument consists of only one path: that of the -secondary table ``Vehicle``. Since it is pointed by the main table ``Accident`` via the table -variable ``Vehicle`` the ``additional_data_tables`` parameter should be set as:: - - additional_data_tables = {"Vehicles": f"{kh.get_samples_dir()}/Vehicles.txt"} - - -Snowflake Schema -~~~~~~~~~~~~~~~~ - -Let's now consider the dictionary file for the ``Accidents`` dataset where tables are related -through a *snowflake* schema. - -.. code-block:: c - - # samples/Accidents/Accidents.kdic - Root Dictionary Accident(AccidentId) - { - Categorical AccidentId; - // The target "Gravity" is calculated from a sub-table - // See: https://khiops.org/setup/KhiopsGuide.pdf#page=58 - Categorical Gravity = IfC( - G(TableSum(Vehicles, TableCount(TableSelection(Users, EQc(Gravity, "Death")))), 0), - "Lethal", "NonLethal"); - // - Entity(Place) Place; // This is a table variable type Entity: 1-1 relation) - Table(Vehicle) Vehicles; // This is a table variable (type Table) - }; - - Dictionary Place(AccidentId) - { - Categorical AccidentId; - Categorical RoadType; - // - Categorical SchoolNear; - }; - - - Dictionary Vehicle(AccidentId, VehicleId) - { - Categorical AccidentId; - Categorical VehicleId; - // - Table(User) Users; // This is a table variable (type Table) - }; - - Dictionary User(AccidentId, VehicleId) { - Categorical AccidentId; - Categorical VehicleId; - Categorical Seat; - Categorical Category; - Unused Categorical Gravity; // Must be disabled since the target is a function of it - // - Numerical BirthYear; - }; - - -This time, the relational schema is as follows: - -.. code-block:: text - - Accident(AccidentId) - | - +--- 1:n --- Vehicle(AccidentId, VehicleId) - | | - | +--- 1:n --- User(AccidentId, VehicleId) - | - +--- 1:1 --- Place(AccidentId) - - -The ``additional_data_tables`` parameter must be set as:: - - additional_data_tables = { - "Place": "/path/to/Places.txt", - "Vehicles": "/path/to/Vehicles.txt", - "Vehicles/Users": "/path/to/Users.txt" - } - diff --git a/doc/notes.rst b/doc/notes.rst deleted file mode 100644 index 339d7940..00000000 --- a/doc/notes.rst +++ /dev/null @@ -1,172 +0,0 @@ -===== -Notes -===== - -Khiops API Notes -================ - -.. _core-api-common-params: - -Common Parameters ------------------ -The functions in the `khiops.core.api` have the following common parameters. - -log_file_path : str, default "" - Path of the log file for the Khiops process (command line option ``-e`` of the desktop app). If - equal to "" then it writes no log file. -output_scenario_path : str, default "" - Path of the output Khiops scenario file (command line option ``-o`` of the desktop app). If - the empty string is specified no output scenario file is generated. -task_file_path : str, default "" - Path of the task file for the Khiops process (command line option ``-p`` of the desktop app). If - equal to "" then it writes no task file. -trace : bool, default ``False`` - If True prints the command line executed of the process and does not delete any temporary files - created. -stdout_file_path : str, default "" - *Advanced* Path to a file where the Khiops process writes its stdout stream. Normally Khiops - should not write to this stream but MPI, filesystems plugins or debug versions may do it. The - stream is captured with a UTF-8 encoding and replacing encoding errors. If equal to "" then it - writes no file. -stderr_file_path : str, default "" - *Advanced* Path to a file where the Khiops process writes its stderr stream. Normally Khiops - should not write to this stream but MPI, filesystems plugins or debug versions may do it. The - stream is captured with a UTF-8 encoding and replacing encoding errors. If equal to "" then it - writes no file. -max_cores: int, optional - *Advanced* Maximum number of cores for Khiops executions. If not set, then Khiops uses all - available CPU cores in the system. -memory_limit_mb: int, optional - *Advanced* Maximum amount of memory (in MB) for Khiops executions. If not set, then Khiops - uses all available system memory. -temp_dir: str, default "" - *Advanced* Temporary directory for Khiops executions. If set to "", then Khiops uses the - system's temporary directory. -scenario_prologue: str, default "" - *Advanced* Prologue to prepend to all Khiops execution scenarios. -force_ansi_scenario : bool, default ``False`` - *Advanced* If True the internal scenario generated by Khiops will force characters such as - accentuated ones to be decoded with the UTF8->ANSI khiops transformation. - -.. _core-api-input-types: - -Input Types ------------ - -The types accepted in most methods and classes of `khiops.core` are flexible: - -- ``str`` can be replaced by ``bytes`` - - - This adds flexibility for file paths and automatically created variable names (data-dependent). - -- `list` can be replaced by any class implementing the `collections.abc.Sequence` interface except - ``str`` and ``bytes``. -- `dict` can be replaced by any class implementing the `collections.abc.Mapping` interface. - -.. _core-api-sampling-mode: - -Database Sampling ------------------ - -Several `khiops.core.api` functions can operate on dataset *samples* instead of the full datasets. -This sampling behavior is fully customizable by the user: one can specify that the function -operates on the specified sample or on its *complement*. - -The sampling behavior is controlled with two parameters: - -- ``sample_percentage``: A real number between 0 and 100 specifying the percentage of the data to be - used as sample. - -- ``sampling_mode``: A string specifying the sampling operation mode: - - - "Include sample": The sample consist on ``sample_percentage`` percent of the individuals in the - dataset. - - - "Exclude sample": The sample consist on ``100 - sample_percentage`` percent of the individuals - in the dataset. The sample is exactly the *complement* of that obtained with "Include sample". - - -In the case of the `~khiops.core.api.train_predictor` function the additional boolean parameter -``use_complement_as_test`` specifies whether the complement of the selected sample is used to -evaluate the trained predictor. - -An Example -~~~~~~~~~~ -If in the `~khiops.core.api.train_predictor` call we set: - -- ``sample_percentage`` to 20 -- ``sampling_mode`` to "Exclude sample" -- ``use_complement_as_test`` to ``True`` - -specifies a 20-80 split of the dataset. Since ``sample_mode`` is "Exclude sample" the predictor will -be trained on the 80 % part. The remaining 20 % will be used to evaluate the predictor's performance -because ``use_complement_as_test`` is ``True``. - -.. _core-api-env-samples-dir: - -Samples Directory Customization -------------------------------- - -The samples directory usable by the Khiops Python library can be customized via -the ``KHIOPS_SAMPLES_DIR`` environment variable, which specifies the path to the Khiops sample -datasets directory. - -Khiops JSON Files -================= - -Generalities ------------- - -The structure of the Khiops JSON files is self-documented: - -- Most of the information is available as key-value pairs, where the keys resemble the labels used - in Khiops' classic report files (tab-separated plain-text files with extension ``.xls``) or - dictionary files. -- In order to be human-readable the files are *beautified* with a comfortable spacing and - indentation. - -Structure and Performance -------------------------- - -The Khiops JSON files may be large (tens of MB) when analyzing datasets with many columns, or when -specifying the creation of thousands of variables in the multi-table case. To handle these -situations, the report attributes in the JSON file are sorted by increasing size, thus easing the -use of streaming parsers. - -Furthermore, memory-scalable parsing techniques can be implemented. For example, the heavier parts -of the file can be separated and split into chunks. Then, these chunks can be indexed using the -information found at the top of the report, allowing the on-demand access to the detailed parts of -the report. - -Khiops Report Files Structure (.khj) ------------------------------------- - -At the top level the order is as follows: - -- Modeling report -- Evaluation report(s) -- Preparation report(s) - -The preparation reports are at the end because they can be very large when many -variables are analyzed. - -Each report field is organized in three sections: - -- Summary: General (short) information about the report -- A list of report items: - - - Variable statistics (preparation), trained predictor (modeling) and predictor - performance (evaluation) - - Each item has a "Rank" - - - Example: The second most informative variable has the categorical rank "R02" - - - Each item is described by a few summary attributes - -- A dictionary of detailed report items. The keys of this dictionary are the - previously mentioned "Rank" attributes. Note that: - - - Not all report items are detailed - - The detailed information may be large (example: data grid). - - diff --git a/doc/requirements.txt b/doc/requirements.txt deleted file mode 100644 index 74f7acf4..00000000 --- a/doc/requirements.txt +++ /dev/null @@ -1,9 +0,0 @@ -sphinx>=6.1.0 -furo>=2022.12.7 -ipykernel>=6.9.1 -nbconvert==6.4.4 -nbformat==5.3.0 -numpydoc>=1.5.0 -pandas>=2.3.3,<4.0.0 -scikit-learn>=1.7.2,<1.9.0 -sphinx-copybutton>=0.5.0 diff --git a/doc/samples/samples.rst b/doc/samples/samples.rst deleted file mode 100644 index 6c1d7a5e..00000000 --- a/doc/samples/samples.rst +++ /dev/null @@ -1,1837 +0,0 @@ -:orphan: - -.. currentmodule:: samples - -Samples core -============ - -The code snippets on this page demonstrate the basic use of the :py:mod:`khiops.core` module. - -Script and Jupyter notebook ---------------------------- -The samples in this page are also available as: - -- :download:`Python script <../../khiops/samples/samples.py>` -- :download:`Jupyter notebook <../../khiops/samples/samples.ipynb>` - -Setup ------ -First make sure you have installed the sample datasets. In a configured -conda shell (ex. *Anaconda Prompt* in Windows) execute: - -.. code-block:: shell - - kh-download-datasets - -If that doesn't work open a python console and execute: - -.. code-block:: python - - from khiops.tools import download_datasets - download_datasets() - - -Samples -------- - -.. autofunction:: get_khiops_version -.. code-block:: python - - from khiops import core as kh - - print(f"Khiops version: {kh.get_khiops_version()}") -.. autofunction:: build_dictionary_from_data_table -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - dictionary_name = "AutoAdult" - dictionary_file_path = os.path.join( - "kh_samples", "build_dictionary_from_data_table", "AutoAdult.kdic" - ) - - # Create the dictionary from the data table - kh.build_dictionary_from_data_table( - data_table_path, dictionary_name, dictionary_file_path - ) -.. autofunction:: create_dictionary_domain -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Create a Root dictionary - root_dictionary = kh.Dictionary( - json_data={"name": "dict_from_scratch", "root": True, "key": ["Id"]} - ) - - # Start with simple variables to declare - simple_variables = [ - {"name": "Id", "type": "Categorical"}, - {"name": "Num", "type": "Numerical"}, - {"name": "text", "type": "Text"}, - {"name": "hour", "type": "Time"}, - {"name": "date", "type": "Date"}, - {"name": "ambiguous_ts", "type": "Timestamp"}, - {"name": "ts", "type": "TimestampTZ"}, - ] - for var_spec in simple_variables: - root_dictionary.add_variable_from_spec(name=var_spec["name"], type=var_spec["type"]) - - # Create a second dictionary - second_dictionary = kh.Dictionary( - json_data={"name": "Service", "key": ["Id", "id_product"]} - ) - second_dictionary.add_variable_from_spec(name="Id", type="Categorical") - second_dictionary.add_variable_from_spec(name="id_product", type="Categorical") - - # Create a third dictionary - third_dictionary = kh.Dictionary(json_data={"name": "Address", "key": ["Id"]}) - third_dictionary.add_variable_from_spec(name="StreetNumber", type="Numerical") - third_dictionary.add_variable_from_spec(name="StreetName", type="Categorical") - third_dictionary.add_variable_from_spec(name="id_city", type="Categorical") - # Add a variable with a rule - third_dictionary.add_variable_from_spec( - name="computed", - type="Numerical", - rule=str(kh.Rule("Ceil", kh.Rule("Product", 3, kh.Rule("Random")))), - ) - - # Add the variables used in a multi-table context in the first dictionary. - # They link the root dictionary to the additional ones - root_dictionary.add_variable_from_spec( - name="Services", type="Table", object_type="Service" - ) - root_dictionary.add_variable_from_spec( - name="Address", type="Entity", object_type="Address" - ) - - # Create a DictionaryDomain (set of dictionaries) - dictionary_domain = kh.DictionaryDomain() - dictionary_domain.add_dictionary(root_dictionary) - dictionary_domain.add_dictionary(second_dictionary) - dictionary_domain.add_dictionary(third_dictionary) - - output_dir = os.path.join("kh_samples", "create_dictionary_domain") - dictionary_file_path = os.path.join(output_dir, "dict_from_scratch.kdic") - - # Create the output directory if needed - if not os.path.isdir(output_dir): - os.mkdir(output_dir) - - # Write the dictionary domain to a file - dictionary_domain.export_khiops_dictionary_file(dictionary_file_path) -.. autofunction:: detect_data_table_format -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - output_dir = os.path.join("kh_samples", "detect_data_table_format") - transformed_data_table_path = os.path.join(output_dir, "AdultWithAnotherFormat.txt") - - # Create the output directory - if not os.path.isdir(output_dir): - os.mkdir(output_dir) - - # Detect the format of the table - format_spec = kh.detect_data_table_format(data_table_path) - print("Format specification (header_line, field_separator)") - print("Format detected on original table:", format_spec) - - # Make a deployment to change the format of the data table - kh.deploy_model( - dictionary_file_path, - "Adult", - data_table_path, - transformed_data_table_path, - output_header_line=False, - output_field_separator=",", - ) - - # Detect the new format of the table without a dictionary file - format_spec = kh.detect_data_table_format(transformed_data_table_path) - print("Format detected on reformatted table:", format_spec) - - # Detect the new format of the table with a dictionary file - format_spec = kh.detect_data_table_format( - transformed_data_table_path, - dictionary_file_path_or_domain=dictionary_file_path, - dictionary_name="Adult", - ) - print("Format detected (with dictionary file) on reformatted table:", format_spec) -.. autofunction:: check_database -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - log_file = os.path.join("kh_samples", "check_database", "check_database.log") - - # Check the database - kh.check_database( - dictionary_file_path, - "Adult", - data_table_path, - log_file_path=log_file, - max_messages=50, - ) -.. autofunction:: export_dictionary_files -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - output_dir = os.path.join("kh_samples", "export_dictionary_files") - output_dictionary_file_path = os.path.join(output_dir, "ModifiedAdult.kdic") - output_dictionary_json_path = os.path.join(output_dir, "ModifiedAdult.kdicj") - alt_output_dictionary_json_path = os.path.join(output_dir, "AltModifiedAdult.kdicj") - - # Load the dictionary domain from initial dictionary file - # Then obtain the "Adult" dictionary within - domain = kh.read_dictionary_file(dictionary_file_path) - dictionary = domain.get_dictionary("Adult") - - # Set some of its variables to unused - fnlwgt_variable = dictionary.get_variable("fnlwgt") - fnlwgt_variable.used = False - label_variable = dictionary.get_variable("Label") - label_variable.used = False - - # Create output directory if necessary - if not os.path.exists("kh_samples"): - os.mkdir("kh_samples") - os.mkdir(output_dir) - else: - if not os.path.exists(output_dir): - os.mkdir(output_dir) - - # Export to kdic - domain.export_khiops_dictionary_file(output_dictionary_file_path) - - # Export to kdicj either from the domain or from a kdic file - # Requires a Khiops execution, that's why it is not a method of DictionaryDomain - kh.export_dictionary_as_json(domain, output_dictionary_json_path) - kh.export_dictionary_as_json( - output_dictionary_file_path, alt_output_dictionary_json_path - ) -.. autofunction:: train_predictor -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - analysis_report_file_path = os.path.join( - "kh_samples", "train_predictor", "AnalysisReport.khj" - ) - - # Train the predictor - kh.train_predictor( - dictionary_file_path, - "Adult", - data_table_path, - "class", - analysis_report_file_path, - max_trees=0, - ) -.. autofunction:: train_predictor_file_paths -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - report_file_path = os.path.join( - "kh_samples", "train_predictor_file_paths", "AnalysisResults.khj" - ) - - # Train the predictor - _, modeling_dictionary_file_path = kh.train_predictor( - dictionary_file_path, - "Adult", - data_table_path, - "class", - report_file_path, - max_trees=0, - ) - print("Reports file available at " + report_file_path) - print("Modeling dictionary file available at " + modeling_dictionary_file_path) - - # If you have Khiops Visualization installed you may open the report as follows - # kh.visualize_report(report_file_path) -.. autofunction:: train_predictor_text -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join( - kh.get_samples_dir(), "NegativeAirlineTweets", "NegativeAirlineTweets.kdic" - ) - data_table_path = os.path.join( - kh.get_samples_dir(), "NegativeAirlineTweets", "NegativeAirlineTweets.txt" - ) - report_file_path = os.path.join( - "kh_samples", "train_predictor_text", "AnalysisResults.khj" - ) - - # Train the predictor - kh.train_predictor( - dictionary_file_path, - "NegativeAirlineTweets", - data_table_path, - "negativereason", - report_file_path, - max_trees=5, - max_text_features=1000, - text_features="words", - ) -.. autofunction:: train_predictor_error_handling -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths with a nonexistent dictionary file - dictionary_file_path = "NONEXISTENT_DICTIONARY_FILE.kdic" - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - output_dir = os.path.join("kh_samples", "train_predictor_error_handling") - report_file_path = os.path.join(output_dir, "AnalysisResults.khj") - log_file_path = os.path.join(output_dir, "khiops.log") - scenario_path = os.path.join(output_dir, "scenario._kh") - - # Train the predictor and handle the error - try: - kh.train_predictor( - dictionary_file_path, - "Adult", - data_table_path, - "class", - report_file_path, - trace=True, - log_file_path=log_file_path, - output_scenario_path=scenario_path, - ) - except kh.KhiopsRuntimeError as error: - print("Khiops training failed! Below the KhiopsRuntimeError message:") - print(error) - - print("\nFull log contents:") - print("------------------") - with open(log_file_path) as log_file: - for line in log_file: - print(line, end="") - - print("\nExecuted scenario") - print("-----------------") - with open(scenario_path) as scenario_file: - for line in scenario_file: - print(line, end="") -.. autofunction:: train_predictor_mt -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - accidents_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") - dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") - accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") - vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") - report_file_path = os.path.join( - "kh_samples", "train_predictor_mt", "AnalysisResults.khj" - ) - - # Train the predictor. Besides the mandatory parameters, we specify: - # - A python dictionary linking data paths to file paths for non-root tables - # - To not construct any decision tree - # The default number of automatic features is 100 - kh.train_predictor( - dictionary_file_path, - "Accident", - accidents_table_path, - "Gravity", - report_file_path, - additional_data_tables={"Vehicles": vehicles_table_path}, - max_trees=0, - ) -.. autofunction:: train_predictor_mt_with_specific_rules -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - accidents_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") - dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") - accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") - vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") - report_file_path = os.path.join( - "kh_samples", - "train_predictor_mt_with_specific_rules", - "AnalysisResults.khj", - ) - - # Train the predictor. Besides the mandatory parameters, it is specified: - # - A python dictionary linking data paths to file paths for non-root tables - # - The maximum number of aggregate variables to construct (1000) - # - The construction rules allowed to automatically create aggregates - # - To not construct any decision tree - kh.train_predictor( - dictionary_file_path, - "Accident", - accidents_table_path, - "Gravity", - report_file_path, - additional_data_tables={"Vehicles": vehicles_table_path}, - max_constructed_variables=1000, - construction_rules=["TableMode", "TableSelection"], - max_trees=0, - ) -.. autofunction:: train_predictor_mt_snowflake -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - accidents_dir = os.path.join(kh.get_samples_dir(), "Accidents") - dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") - accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") - vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") - users_table_path = os.path.join(accidents_dir, "Users.txt") - places_table_path = os.path.join(accidents_dir, "Places.txt") - report_file_path = os.path.join( - "kh_samples", "train_predictor_mt_snowflake", "AnalysisResults.khj" - ) - - # Train the predictor. Besides the mandatory parameters, we specify: - # - A python dictionary linking data paths to file paths for non-root tables - # - To not construct any decision tree - # The default number of automatic features is 100 - kh.train_predictor( - dictionary_file_path, - "Accident", - accidents_table_path, - "Gravity", - report_file_path, - additional_data_tables={ - "Vehicles": vehicles_table_path, - "Vehicles/Users": users_table_path, - "Place": places_table_path, - }, - max_trees=0, - ) -.. autofunction:: train_predictor_with_train_percentage -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - report_file_path = os.path.join( - "kh_samples", - "train_predictor_with_train_percentage", - "P90_AnalysisResults.khj", - ) - - # Train the predictor. Besides the mandatory parameters, it is specified: - # - A 90% sampling rate for the training dataset - # - Set the test dataset as the complement of the training dataset (10%) - # - No trees - kh.train_predictor( - dictionary_file_path, - "Adult", - data_table_path, - "class", - report_file_path, - sample_percentage=90, - use_complement_as_test=True, - max_trees=0, - ) -.. autofunction:: train_predictor_with_trees -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Letter", "Letter.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Letter", "Letter.txt") - report_file_path = os.path.join( - "kh_samples", "train_predictor_with_trees", "P80_AnalysisResults.khj" - ) - - # Train the predictor with at most 15 trees (default 10) - kh.train_predictor( - dictionary_file_path, - "Letter", - data_table_path, - "lettr", - report_file_path, - sample_percentage=80, - use_complement_as_test=True, - max_trees=15, - ) -.. autofunction:: train_predictor_with_pairs -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - report_file_path = os.path.join( - "kh_samples", "train_predictor_with_pairs", "AnalysisResults.khj" - ) - - # Train the predictor with at most 10 pairs as follows: - # - Include pairs age-race and capital_gain-capital_loss - # - Include all possible pairs having relationship as component - kh.train_predictor( - dictionary_file_path, - "Adult", - data_table_path, - "class", - report_file_path, - use_complement_as_test=True, - max_trees=0, - max_pairs=10, - specific_pairs=[ - ("age", "race"), - ("capital_gain", "capital_loss"), - ("relationship", ""), - ], - ) -.. autofunction:: train_predictor_with_multiple_parameters -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - output_dir = os.path.join("kh_samples", "train_predictor_with_multiple_parameters") - report_file_path = os.path.join(output_dir, "AnalysisResults.khj") - output_script_path = os.path.join(output_dir, "output_scenario._kh") - log_path = os.path.join(output_dir, "log.txt") - - # Train the predictor. Besides the mandatory parameters, we specify: - # - The value "more" as main target value - # - The output Khiops script file location (generic) - # - The log file location (generic) - # - The maximum memory used, set to 1000 MB - # - To show the debug trace (generic) - kh.train_predictor( - dictionary_file_path, - "Adult", - data_table_path, - "class", - report_file_path, - main_target_value="more", - output_scenario_path=output_script_path, - log_file_path=log_path, - memory_limit_mb=1000, - trace=True, - ) -.. autofunction:: train_predictor_detect_format -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Iris", "Iris.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Iris", "Iris.txt") - output_dir = os.path.join("kh_samples", "train_predictor_detect_format") - transformed_data_table_path = os.path.join(output_dir, "TransformedIris.txt") - report_file_path = os.path.join(output_dir, "AnalysisResults.khj") - - # Transform the database format from header_line=True and field_separator=TAB - # to header_line=False and field_separator="," - # See the deploy_model examples below for more details - kh.deploy_model( - dictionary_file_path, - "Iris", - data_table_path, - transformed_data_table_path, - output_header_line=False, - output_field_separator=",", - ) - - # Try to learn with the old format - try: - kh.train_predictor( - dictionary_file_path, - "Iris", - transformed_data_table_path, - "Class", - report_file_path, - header_line=True, - field_separator="", - ) - except kh.KhiopsRuntimeError as error: - print( - "This failed because of a bad data table format spec. " - + "Below the KhiopsRuntimeError message" - ) - print(error) - - # Train without specifyng the format (detect_format is True by default) - kh.train_predictor( - dictionary_file_path, - "Iris", - transformed_data_table_path, - "Class", - report_file_path, - ) -.. autofunction:: train_predictor_with_cross_validation -.. code-block:: python - - # Imports - import math - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - output_dir = os.path.join("kh_samples", "train_predictor_with_cross_validation") - fold_dictionary_file_path = os.path.join(output_dir, "AdultWithFolding.kdic") - - # Create the output directory - if not os.path.isdir(output_dir): - os.mkdir(output_dir) - - # Load the learning dictionary object - domain = kh.read_dictionary_file(dictionary_file_path) - dictionary = domain.get_dictionary("Adult") - - # Add a random fold index variable to the learning dictionary - fold_number = 5 - dictionary.add_variable_from_spec(name="FoldIndex", type="Numerical", used=False) - - # Create fold indexing rule and set it on `fold_index_variable` - fold_index_variable = dictionary.get_variable("FoldIndex") - fold_index_variable.rule = str( - kh.Rule("Ceil", kh.Rule("Product", fold_number, kh.Rule("Random"))), - ) - - # Add variables that indicate if the instance is in the train dataset: - for fold_index in range(1, fold_number + 1): - name = "IsInTrainDataset" + str(fold_index) - dictionary.add_variable_from_spec(name=name, type="Numerical", used=False) - dictionary.get_variable(name).rule = str( - kh.Rule("NEQ", fold_index_variable, fold_index), - ) - - # Print dictionary with fold variables - print("Dictionary file with fold variables") - domain.export_khiops_dictionary_file(fold_dictionary_file_path) - with open(fold_dictionary_file_path) as fold_dictionary_file: - for line in fold_dictionary_file: - print(line, end="") - - # For each fold k: - print("Training Adult with " + str(fold_number) + " folds") - print("\tfold\ttrain auc\ttest auc") - train_aucs = [] - test_aucs = [] - for fold_index in range(1, fold_number + 1): - analysis_report_file_path = os.path.join( - output_dir, "Fold" + str(fold_index) + "AnalysisResults.khj" - ) - # Train a model from the sub-dataset where IsInTrainDataset is 1 - _, modeling_dictionary_file_path = kh.train_predictor( - domain, - "Adult", - data_table_path, - "class", - analysis_report_file_path, - sample_percentage=100, - selection_variable="IsInTrainDataset" + str(fold_index), - selection_value=1, - max_trees=0, - ) - - evaluation_report_file_path = os.path.join( - output_dir, "Fold" + str(fold_index) + "AdultEvaluationResults.khj" - ) - # Evaluate the resulting model in the subsets where IsInTrainDataset is 0 - test_evaluation_report_path = kh.evaluate_predictor( - modeling_dictionary_file_path, - "SNB_Adult", - data_table_path, - evaluation_report_file_path, - sample_percentage=100, - selection_variable="IsInTrainDataset" + str(fold_index), - selection_value=0, - ) - - # Obtain the train AUC from the train report and the test AUC from the - # evaluation report and print them - train_results = kh.read_analysis_results_file(analysis_report_file_path) - test_evaluation_results = kh.read_analysis_results_file(test_evaluation_report_path) - train_auc = train_results.train_evaluation_report.get_snb_performance().auc - test_auc = test_evaluation_results.evaluation_report.get_snb_performance().auc - print("\t" + str(fold_index) + "\t" + str(train_auc) + "\t" + str(test_auc)) - - # Store the train and test AUCs in arrays - train_aucs.append(train_auc) - test_aucs.append(test_auc) - - # Print the mean +- error aucs for both train and test - mean_train_auc = sum(train_aucs) / fold_number - squared_error_train_aucs = [(auc - mean_train_auc) ** 2 for auc in train_aucs] - sd_train_auc = math.sqrt(sum(squared_error_train_aucs) / (fold_number - 1)) - - mean_test_auc = sum(test_aucs) / fold_number - squared_error_test_aucs = [(auc - mean_test_auc) ** 2 for auc in test_aucs] - sd_test_auc = math.sqrt(sum(squared_error_test_aucs) / (fold_number - 1)) - - print("final auc") - print("train auc: " + str(mean_train_auc) + " +- " + str(sd_train_auc)) - print("test auc: " + str(mean_test_auc) + " +- " + str(sd_test_auc)) -.. autofunction:: interpret_predictor -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - output_dir = os.path.join("kh_samples", "interpret_predictor") - analysis_report_file_path = os.path.join(output_dir, "AnalysisResults.khj") - interpretor_file_path = os.path.join(output_dir, "InterpretationModel.kdic") - - # Build prediction model - _, predictor_file_path = kh.train_predictor( - dictionary_file_path, - "Adult", - data_table_path, - "class", - analysis_report_file_path, - ) - - # Build interpretation model - kh.interpret_predictor(predictor_file_path, "SNB_Adult", interpretor_file_path) - - print(f"The interpretation model is '{interpretor_file_path}'") -.. autofunction:: reinforce_predictor -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - output_dir = os.path.join("kh_samples", "reinforce_predictor") - analysis_report_file_path = os.path.join(output_dir, "AnalysisResults.khj") - reinforced_predictor_file_path = os.path.join(output_dir, "ReinforcedAdultModel.kdic") - - # Build prediction model - _, predictor_file_path = kh.train_predictor( - dictionary_file_path, - "Adult", - data_table_path, - "class", - analysis_report_file_path, - ) - - # Build reinforced predictor - kh.reinforce_predictor( - predictor_file_path, - "SNB_Adult", - reinforced_predictor_file_path, - reinforcement_lever_variables=["occupation"], - ) - - print(f"The reinforced predictor is '{reinforced_predictor_file_path}'") -.. autofunction:: multiple_train_predictor -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - - def display_test_results(json_result_file_path): - """Display some of the training results""" - results = kh.read_analysis_results_file(json_result_file_path) - train_performance = results.train_evaluation_report.get_snb_performance() - test_performance = results.test_evaluation_report.get_snb_performance() - print( - "\t" - + str(len(results.preparation_report.variables_statistics)) - + "\t" - + str(train_performance.auc) - + "\t" - + str(test_performance.auc) - ) - - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - output_dir = os.path.join("kh_samples", "multiple_train_predictor") - report_file_path = os.path.join(output_dir, "AnalysisResults.khj") - - # Read the dictionary file to obtain an instance of class Dictionary - dictionary_domain = kh.read_dictionary_file(dictionary_file_path) - dictionary = dictionary_domain.get_dictionary("Adult") - - # Train a SNB model using all the variables - print("\t#vars\ttrain auc\ttest auc") - kh.train_predictor( - dictionary_file_path, - "Adult", - data_table_path, - "class", - report_file_path, - sample_percentage=70, - use_complement_as_test=True, - max_trees=0, - ) - display_test_results(report_file_path) - - # Read results to obtain the variables sorted by decreasing Level - analysis_results = kh.read_analysis_results_file(report_file_path) - preparation_results = analysis_results.preparation_report - - # Train a sequence of models with a decreasing number of variables - # We disable variables one-by-one in increasing level (predictive power) order - variable_number = len(preparation_results.variables_statistics) - for i in reversed(range(variable_number)): - # Search the next variable - variable = preparation_results.variables_statistics[i] - - # Disable this variable and save the dictionary with the Khiops format - dictionary.get_variable(variable.name).used = False - - # Train the model with this dictionary domain object - report_file_path = os.path.join( - output_dir, f"V{variable_number - 1 - i}_AnalysisResults.khj" - ) - kh.train_predictor( - dictionary_domain, - "Adult", - data_table_path, - "class", - report_file_path, - sample_percentage=70, - use_complement_as_test=True, - max_trees=0, - ) - - # Show a preview of the results - display_test_results(report_file_path) -.. autofunction:: evaluate_predictor -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - output_dir = os.path.join("kh_samples", "evaluate_predictor") - analysis_report_file_path = os.path.join(output_dir, "AnalysisResults.khj") - - # Train the predictor - _, model_dictionary_file_path = kh.train_predictor( - dictionary_file_path, - "Adult", - data_table_path, - "class", - analysis_report_file_path, - max_trees=0, - ) - - evaluation_report_file_path = os.path.join(output_dir, "AdultEvaluationResults.khj") - - # Evaluate the predictor - kh.evaluate_predictor( - model_dictionary_file_path, - "SNB_Adult", - data_table_path, - evaluation_report_file_path, - ) - print("Evaluation report available at " + evaluation_report_file_path) -.. autofunction:: access_predictor_evaluation_report -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - report_file_path = os.path.join( - "kh_samples", "access_predictor_evaluation_report", "AdultAnalysisReport.khj" - ) - - # Train the SNB predictor and some univariate predictors - # Note: Evaluation in test is 30% by default - kh.train_predictor( - dictionary_file_path, - "Adult", - data_table_path, - "class", - report_file_path, - max_trees=0, - ) - - # Obtain the evaluation results - results = kh.read_analysis_results_file(report_file_path) - evaluation_report = results.test_evaluation_report - snb_performance = evaluation_report.get_snb_performance() - - # Print univariate metrics for the SNB - print("\nperformance metrics for " + snb_performance.name) - for metric_name in snb_performance.get_metric_names(): - print(metric_name + ": " + str(snb_performance.get_metric(metric_name))) - - # Print the confusion matrix - print("\nconfusion matrix:") - confusion_matrix = snb_performance.confusion_matrix - - for target_value in confusion_matrix.values: - print("\t" + target_value, end="") - print("") - - for i, target_value in enumerate(confusion_matrix.values): - observed_frequencies = confusion_matrix.matrix[i] - print(target_value, end="") - for frequency in observed_frequencies: - print("\t" + str(frequency), end="") - print("") - - # Print the head of the lift curves for the 'more' modality - print("\nfirst five values of the lift curves for 'more'") - - snb_lift_curve = evaluation_report.get_snb_lift_curve("more") - optimal_lift_curve = evaluation_report.get_classifier_lift_curve("Optimal", "more") - random_lift_curve = evaluation_report.get_classifier_lift_curve("Random", "more") - - for i in range(5): - print( - str(snb_lift_curve.values[i]) - + "\t" - + str(optimal_lift_curve.values[i]) - + "\t" - + str(random_lift_curve.values[i]) - ) - - # Print metrics for an SNB predictor - predictor_performance = evaluation_report.get_predictor_performance( - "Selective Naive Bayes" - ) - print("\n\nperformance metrics for " + predictor_performance.name) - for metric_name in predictor_performance.get_metric_names(): - print(metric_name + ": " + str(predictor_performance.get_metric(metric_name))) -.. autofunction:: train_recoder -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - report_file_path = os.path.join("kh_samples", "train_recoder", "AnalysisResults.khj") - - # Train the recoder model - kh.train_recoder( - dictionary_file_path, "Adult", data_table_path, "class", report_file_path - ) -.. autofunction:: train_recoder_with_multiple_parameters -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - report_file_path = os.path.join( - "kh_samples", - "train_recoder_with_multiple_parameters", - "AnalysisResults.khj", - ) - - # Train the recoder model - kh.train_recoder( - dictionary_file_path, - "Adult", - data_table_path, - "class", - report_file_path, - max_pairs=10, - categorical_recoding_method="part label", - numerical_recoding_method="part label", - ) -.. autofunction:: train_recoder_mt_flatten -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - accidents_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") - dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") - accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") - vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") - report_file_path = os.path.join( - "kh_samples", "train_recoder_mt_flatten", "AnalysisResults.khj" - ) - - # Train the recoder. Besides the mandatory parameters, it is specified: - # - A python dictionary linking data paths to file paths for non-root tables - # - The maximum number of aggregate variables to construct (1000) - # - To keep all the created variables independently of their informativeness (level) - # - To not recode the variables values - kh.train_recoder( - dictionary_file_path, - "Accident", - accidents_table_path, - "Gravity", - report_file_path, - additional_data_tables={"Vehicles": vehicles_table_path}, - max_constructed_variables=1000, - informative_variables_only=False, - categorical_recoding_method="none", - numerical_recoding_method="none", - keep_initial_categorical_variables=True, - keep_initial_numerical_variables=True, - ) -.. autofunction:: deploy_model -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - output_dir = os.path.join("kh_samples", "deploy_model") - report_file_path = os.path.join(output_dir, "AnalysisResults.khj") - output_data_table_path = os.path.join(output_dir, "ScoresAdult.txt") - - # Train the predictor - _, model_dictionary_file_path = kh.train_predictor( - dictionary_file_path, - "Adult", - data_table_path, - "class", - report_file_path, - max_trees=0, - ) - - # Deploy the model on the database - # It will score it according to the trained predictor - kh.deploy_model( - model_dictionary_file_path, "SNB_Adult", data_table_path, output_data_table_path - ) -.. autofunction:: deploy_model_text -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join( - kh.get_samples_dir(), "NegativeAirlineTweets", "NegativeAirlineTweets.kdic" - ) - data_table_path = os.path.join( - kh.get_samples_dir(), "NegativeAirlineTweets", "NegativeAirlineTweets.txt" - ) - output_dir = os.path.join("kh_samples", "deploy_model_text") - report_file_path = os.path.join(output_dir, "AnalysisResults.khj") - output_data_table_path = os.path.join(output_dir, "ScoresNegativeAirlineTweets.txt") - - # Train the predictor - _, model_dictionary_file_path = kh.train_predictor( - dictionary_file_path, - "NegativeAirlineTweets", - data_table_path, - "negativereason", - report_file_path, - max_trees=5, - max_text_features=1000, - text_features="words", - ) - - # Deploy the model on the database - # It will score it according to the trained predictor - kh.deploy_model( - model_dictionary_file_path, - "SNB_NegativeAirlineTweets", - data_table_path, - output_data_table_path, - ) -.. autofunction:: deploy_model_mt -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - accidents_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") - dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") - accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") - vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") - output_dir = os.path.join("kh_samples", "deploy_model_mt") - report_file_path = os.path.join(output_dir, "AnalysisResults.khj") - output_data_table_path = os.path.join(output_dir, "TransferredAccidents.txt") - - # Train the predictor (see train_predictor_mt for details) - _, model_dictionary_file_path = kh.train_predictor( - dictionary_file_path, - "Accident", - accidents_table_path, - "Gravity", - report_file_path, - additional_data_tables={"Vehicles": vehicles_table_path}, - max_trees=0, - ) - - # Deploy the model on the database - # Besides the mandatory parameters, it is specified: - # - A python dictionary linking data paths to file paths for non-root tables - kh.deploy_model( - model_dictionary_file_path, - "SNB_Accident", - accidents_table_path, - output_data_table_path, - additional_data_tables={"Vehicles": vehicles_table_path}, - ) -.. autofunction:: deploy_model_mt_with_interpretation -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - accidents_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") - dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") - accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") - vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") - output_dir = os.path.join("kh_samples", "deploy_model_mt_with_interpretation") - report_file_path = os.path.join(output_dir, "AnalysisResults.khj") - interpretor_file_path = os.path.join(output_dir, "InterpretationModel.kdic") - output_data_table_path = os.path.join(output_dir, "InterpretedAccidents.txt") - - # Train the predictor (see train_predictor_mt for details) - # Add max_evaluated_variables so that an interpretation model can be built - # (see https://github.com/KhiopsML/khiops/issues/577) - _, model_dictionary_file_path = kh.train_predictor( - dictionary_file_path, - "Accident", - accidents_table_path, - "Gravity", - report_file_path, - additional_data_tables={"Vehicles": vehicles_table_path}, - max_trees=0, - max_evaluated_variables=10, - ) - - # Interpret the predictor - kh.interpret_predictor( - model_dictionary_file_path, - "SNB_Accident", - interpretor_file_path, - max_variable_importances=3, - importance_ranking="Individual", - ) - - # Deploy the interpretation model on the database - # Besides the mandatory parameters, it is specified: - # - A python dictionary linking data paths to file paths for non-root tables - kh.deploy_model( - interpretor_file_path, - "Interpretation_SNB_Accident", - accidents_table_path, - output_data_table_path, - additional_data_tables={"Vehicles": vehicles_table_path}, - ) -.. autofunction:: deploy_reinforced_model_mt -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - accidents_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") - dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") - accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") - vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") - output_dir = os.path.join("kh_samples", "deploy_reinforced_model_mt") - report_file_path = os.path.join(output_dir, "AnalysisResults.khj") - reinforced_predictor_file_path = os.path.join(output_dir, "ReinforcedModel.kdic") - output_data_table_path = os.path.join(output_dir, "ReinforcedAccidents.txt") - - # Train the predictor (see train_predictor_mt for details) - _, model_dictionary_file_path = kh.train_predictor( - dictionary_file_path, - "Accident", - accidents_table_path, - "Gravity", - report_file_path, - additional_data_tables={"Vehicles": vehicles_table_path}, - max_trees=0, - ) - - # Reinforce the predictor - kh.reinforce_predictor( - model_dictionary_file_path, - "SNB_Accident", - reinforced_predictor_file_path, - reinforcement_target_value="NonLethal", - reinforcement_lever_variables=["InAgglomeration", "CollisionType"], - ) - - # Deploy the reinforced model on the database - # Besides the mandatory parameters, it is specified: - # - A python dictionary linking data paths to file paths for non-root tables - kh.deploy_model( - reinforced_predictor_file_path, - "Reinforcement_SNB_Accident", - accidents_table_path, - output_data_table_path, - additional_data_tables={"Vehicles": vehicles_table_path}, - ) -.. autofunction:: deploy_model_mt_snowflake -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - accidents_dir = os.path.join(kh.get_samples_dir(), "Accidents") - dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") - accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") - vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") - users_table_path = os.path.join(accidents_dir, "Users.txt") - places_table_path = os.path.join(accidents_dir, "Places.txt") - output_dir = os.path.join("kh_samples", "deploy_model_mt_snowflake") - report_file_path = os.path.join(output_dir, "AnalysisResults.khj") - output_data_table_path = os.path.join(output_dir, "TransferredAccidents.txt") - - # Train the predictor. Besides the mandatory parameters, we specify: - # - A python dictionary linking data paths to file paths for non-root tables - # - To not construct any decision tree - # The default number of automatic features is 100 - _, model_dictionary_file_path = kh.train_predictor( - dictionary_file_path, - "Accident", - accidents_table_path, - "Gravity", - report_file_path, - additional_data_tables={ - "Vehicles": vehicles_table_path, - "Vehicles/Users": users_table_path, - "Place": places_table_path, - }, - max_trees=0, - ) - - # Deploy the model on the database - # Besides the mandatory parameters, it is specified: - # - A python dictionary linking data paths to file paths for non-root tables - kh.deploy_model( - model_dictionary_file_path, - "SNB_Accident", - accidents_table_path, - output_data_table_path, - additional_data_tables={ - "Vehicles": vehicles_table_path, - "Vehicles/Users": users_table_path, - "Place": places_table_path, - }, - ) -.. autofunction:: deploy_model_expert -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - output_dir = os.path.join("kh_samples", "deploy_model_expert") - report_file_path = os.path.join(output_dir, "AnalysisResults.khj") - output_data_table_path = os.path.join(output_dir, "ScoresAdult.txt") - - # Train the predictor - _, model_dictionary_file_path = kh.train_predictor( - dictionary_file_path, - "Adult", - data_table_path, - "class", - report_file_path, - max_trees=0, - ) - - # Read the dictionary file to obtain an instance of class Dictionary - model_domain = kh.read_dictionary_file(model_dictionary_file_path) - snb_dictionary = model_domain.get_dictionary("SNB_Adult") - - # Select Label (identifier) - snb_dictionary.get_variable("Label").used = True - - # Select the variables containing the probabilities for each class - for variable in snb_dictionary.variables: - # The variable must have a meta data with key that start with "target_prob" - for key in variable.meta_data.keys: - if key.startswith("TargetProb"): - variable.used = True - - # Deploy the model. Besides the mandatory parameters, it is specified: - # - A DictionaryDomain object to use instead of the mandatory dictionary file - kh.deploy_model(model_domain, "SNB_Adult", data_table_path, output_data_table_path) -.. autofunction:: deploy_classifier_for_metrics -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - output_dir = os.path.join("kh_samples", "deploy_classifier_for_metrics") - report_file_path = os.path.join(output_dir, "AnalysisResults.khj") - output_data_table_path = os.path.join(output_dir, "ScoresAdult.txt") - - # Train the classifier for the target "class" - _, modeling_dictionary_file_path = kh.train_predictor( - dictionary_file_path, - "Adult", - data_table_path, - "class", - report_file_path, - max_trees=0, - ) - # Obtain the scores of the SNB on the test dataset to calculate the PR curve - kh.deploy_predictor_for_metrics( - modeling_dictionary_file_path, - "SNB_Adult", - data_table_path, - output_data_table_path, - sampling_mode="Exclude sample", - output_header_line=False, - ) - - # We estimate the precision/recall for the class "more" and increasing thresholds - # Note: Normally one would do this with a package (eg. sklearn.metrics) - thresholds = [0.1, 0.3, 0.5, 0.7, 0.9] - true_positives = {thres: 0 for thres in thresholds} - false_positives = {thres: 0 for thres in thresholds} - false_negatives = {thres: 0 for thres in thresholds} - with open(output_data_table_path) as output_data_table: - for line in output_data_table: - fields = line.split("\t") - true_target = fields[0] - proba_more = float(fields[3]) - for thres in thresholds: - if true_target == "more" and proba_more >= thres: - true_positives[thres] += 1 - elif true_target == "more" and proba_more < thres: - false_negatives[thres] += 1 - elif true_target == "less" and proba_more >= thres: - false_positives[thres] += 1 - - precision = { - thres: true_positives[thres] / (true_positives[thres] + false_positives[thres]) - for thres in thresholds - } - recall = { - thres: true_positives[thres] / (true_positives[thres] + false_negatives[thres]) - for thres in thresholds - } - - # Print the curve at the selected points - print("Precision and Recall for class 'more'") - print("threshold\trecall\tprecision") - thresholds.reverse() - for thres in thresholds: - print(str(thres) + "\t" + str(recall[thres]) + "\t" + str(precision[thres])) -.. autofunction:: deploy_regressor_for_metrics -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - output_dir = os.path.join("kh_samples", "deploy_regressor_for_metrics") - report_file_path = os.path.join(output_dir, "AnalysisResults.khj") - output_data_table_path = os.path.join(output_dir, "TrueAndPredictedAges.txt") - - # Train the regressor for the target "age" (with 20% train to be quick) - _, modeling_dictionary_file_path = kh.train_predictor( - dictionary_file_path, - "Adult", - data_table_path, - "age", - report_file_path, - sample_percentage=20, - max_trees=0, - ) - - # Obtain the predicted regression values of the SNB on the test dataset estimate R2 - kh.deploy_predictor_for_metrics( - modeling_dictionary_file_path, - "SNB_Adult", - data_table_path, - output_data_table_path, - sample_percentage=20, - sampling_mode="Exclude sample", - output_header_line=False, - ) - # Estimate R2 - # Note: Normally one would do this with a package (eg. sklearn.metrics) - # First pass to estimate sums of residuals and the mean - ss_res = 0 - mean = 0 - n_instances = 0 - with open(output_data_table_path) as output_data_table: - for line in output_data_table: - fields = line.split("\t") - true_target = float(fields[0]) - predicted_target = float(fields[1]) - ss_res += (true_target - predicted_target) ** 2 - mean += true_target - n_instances += 1 - mean /= n_instances - - # Second pass to estimate the total sums of squares and finish the R2 estimation - ss_tot = 0 - with open(output_data_table_path) as output_data_table: - for line in output_data_table: - fields = line.split("\t") - true_target = float(fields[0]) - ss_tot += (true_target - mean) ** 2 - r2_score = 1 - ss_res / ss_tot - - # Print results - print("Adult 'age' regression (30% train)") - print(f"R2 (explained variance) = {r2_score}") -.. autofunction:: sort_data_table -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - accidents_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") - dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") - accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") - output_data_table_path = os.path.join( - "kh_samples", - "sort_data_table", - "SortedAccidents.txt", - ) - - # Sort table - kh.sort_data_table( - dictionary_file_path, "Accident", accidents_table_path, output_data_table_path - ) -.. autofunction:: sort_data_table_expert -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - accidents_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") - dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") - vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") - output_data_table_path = os.path.join( - "kh_samples", "sort_data_table_expert", "SortedVehicles.txt" - ) - - # Sort table. Besides the mandatory parameters, it is specified: - # - A list containing the sorting fields - kh.sort_data_table( - dictionary_file_path, - "Vehicle", - vehicles_table_path, - output_data_table_path, - sort_variables=["AccidentId", "VehicleId"], - ) -.. autofunction:: extract_keys_from_data_table -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - splice_dir = os.path.join(kh.get_samples_dir(), "SpliceJunction") - dictionary_file_path = os.path.join(splice_dir, "SpliceJunction.kdic") - data_table_path = os.path.join(splice_dir, "SpliceJunctionDNA.txt") - output_data_table_path = os.path.join( - "kh_samples", - "extract_keys_from_data_table", - "KeysSpliceJunction.txt", - ) - - # Extract keys from table "SpliceJunctionDNA" to the output table - kh.extract_keys_from_data_table( - dictionary_file_path, - "SpliceJunctionDNA", - data_table_path, - output_data_table_path, - ) -.. autofunction:: train_coclustering -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - splice_dir = os.path.join(kh.get_samples_dir(), "SpliceJunction") - dictionary_file_path = os.path.join(splice_dir, "SpliceJunction.kdic") - data_table_path = os.path.join(splice_dir, "SpliceJunctionDNA.txt") - coclustering_report_path = os.path.join( - "kh_samples", "train_coclustering", "CoclusteringResults.khcj" - ) - - # Train a coclustering model for variables "SampleId" and "Char" - kh.train_coclustering( - dictionary_file_path, - "SpliceJunctionDNA", - data_table_path, - ["SampleId", "Char"], - coclustering_report_path, - ) - print(f"Coclustering report file available at {coclustering_report_path}") - - # If you have Khiops Co-Visualization installed you may open the report as follows - # kh.visualize_report(coclustering_report_path) -.. autofunction:: train_instance_variable_coclustering -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - iris_dir = os.path.join(kh.get_samples_dir(), "Iris") - dictionary_file_path = os.path.join(iris_dir, "Iris.kdic") - data_table_path = os.path.join(iris_dir, "Iris.txt") - coclustering_report_path = os.path.join( - "kh_samples", - "train_instance_variable_coclustering", - "CoclusteringResults.khcj", - ) - - # Train a coclustering model for variables "SampleId" and "Char" - kh.train_instance_variable_coclustering( - dictionary_file_path, - "Iris", - data_table_path, - coclustering_report_path, - ) - print( - "Instance-variable coclustering report file available " - f"at {coclustering_report_path}" - ) - - # If you have Khiops Co-Visualization installed you may open the report as follows - # kh.visualize_report(coclustering_report_path) -.. autofunction:: simplify_coclustering -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - splice_dir = os.path.join(kh.get_samples_dir(), "SpliceJunction") - dictionary_file_path = os.path.join(splice_dir, "SpliceJunction.kdic") - data_table_path = os.path.join(splice_dir, "SpliceJunctionDNA.txt") - output_dir = os.path.join("kh_samples", "simplify_coclustering") - coclustering_file_path = os.path.join(output_dir, "Coclustering.khcj") - simplified_coclustering_file_path = os.path.join( - output_dir, "simplified_coclustering.khcj" - ) - - # Train coclustering model for variables "SampleId" and "Char" - kh.train_coclustering( - dictionary_file_path, - "SpliceJunctionDNA", - data_table_path, - ["SampleId", "Char"], - coclustering_file_path, - ) - - # Simplify the trained coclustering with the constraints - # - maximum information preserved: 80% - # - maximum total parts number: 4 - kh.simplify_coclustering( - coclustering_file_path, - simplified_coclustering_file_path, - max_preserved_information=80, - max_total_parts=4, - ) -.. autofunction:: extract_clusters -.. code-block:: python - - # Set the file paths - import os - from khiops import core as kh - - splice_dir = os.path.join(kh.get_samples_dir(), "SpliceJunction") - dictionary_file_path = os.path.join(splice_dir, "SpliceJunction.kdic") - data_table_path = os.path.join(splice_dir, "SpliceJunctionDNA.txt") - output_dir = os.path.join("kh_samples", "extract_clusters") - coclustering_file_path = os.path.join(output_dir, "Coclustering.khcj") - clusters_file_path = os.path.join(output_dir, "extracted_clusters.txt") - - # Train a coclustering model for variables "SampleId" and "Char" - kh.train_coclustering( - dictionary_file_path, - "SpliceJunctionDNA", - data_table_path, - ["SampleId", "Char"], - coclustering_file_path, - ) - - # Extract clusters - kh.extract_clusters(coclustering_file_path, "Char", clusters_file_path) -.. autofunction:: deploy_coclustering -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the initial file paths - splice_dir = os.path.join(kh.get_samples_dir(), "SpliceJunction") - data_table_path = os.path.join(splice_dir, "SpliceJunctionDNA.txt") - dictionary_file_path = os.path.join(splice_dir, "SpliceJunction.kdic") - output_dir = os.path.join("kh_samples", "deploy_coclustering") - coclustering_file_path = os.path.join(output_dir, "Coclustering.khcj") - coclustering_dictionary_file_path = os.path.join(output_dir, "Coclustering.kdic") - output_data_table_path = os.path.join(output_dir, "DeployedSpliceJunctionDNA.txt") - - # Train a coclustering model for variables "SampleId" and "Char" - kh.train_coclustering( - dictionary_file_path, - "SpliceJunctionDNA", - data_table_path, - ["SampleId", "Char"], - coclustering_file_path, - ) - - # Deploy "Char" clusters in the training database - kh.deploy_coclustering( - dictionary_file_path, - "SpliceJunctionDNA", - data_table_path, - coclustering_file_path, - ["SampleId"], - "Char", - coclustering_dictionary_file_path, - output_data_table_path, - header_line=True, - ) -.. autofunction:: deploy_coclustering_expert -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the initial file paths - splice_dir = os.path.join(kh.get_samples_dir(), "SpliceJunction") - dictionary_file_path = os.path.join(splice_dir, "SpliceJunction.kdic") - data_table_path = os.path.join(splice_dir, "SpliceJunction.txt") - secondary_data_table_path = os.path.join(splice_dir, "SpliceJunctionDNA.txt") - output_dir = os.path.join("kh_samples", "deploy_coclustering_expert") - coclustering_file_path = os.path.join(output_dir, "Coclustering.khcj") - - # Train a coclustering model for variables "SampleId" and "Char" - print("train coclustering on SpliceJunctionDNA") - kh.train_coclustering( - dictionary_file_path, - "SpliceJunctionDNA", - secondary_data_table_path, - ["SampleId", "Char"], - coclustering_file_path, - ) - - print("prepare_coclustering_deployment") - # The input dictionary is extended with new coclustering based variables - augmented_dictionary_file_path = os.path.join(output_dir, "Coclustering.kdic") - kh.prepare_coclustering_deployment( - dictionary_file_path, - "SpliceJunction", - coclustering_file_path, - "DNA", - "SampleId", - augmented_dictionary_file_path, - ) - - print("prepare_coclustering_deployment with at most two clusters") - # Extend the already extended dictionary with the new variables from a simplified CC - reaugmented_dictionary_file_path = os.path.join( - output_dir, "ReaugmentedCoclustering.kdic" - ) - kh.prepare_coclustering_deployment( - augmented_dictionary_file_path, - "SpliceJunction", - coclustering_file_path, - "DNA", - "SampleId", - reaugmented_dictionary_file_path, - variables_prefix="C2_", - max_part_numbers={"SampleId": 2}, - ) - - output_data_table_path = os.path.join(output_dir, "TransferredSpliceJunction.txt") - - # Deploy the coclustering with the extended dictionary - print("deploy_model with the new coclustering based variables") - kh.deploy_model( - reaugmented_dictionary_file_path, - "SpliceJunction", - data_table_path, - output_data_table_path, - additional_data_tables={"DNA": secondary_data_table_path}, - ) - - deployed_dictionary_file_path = os.path.join( - output_dir, "Transferred_Coclustering.kdic" - ) - print("build_deployed_dictionary to get the new dictionary") - kh.build_deployed_dictionary( - reaugmented_dictionary_file_path, - "SpliceJunction", - deployed_dictionary_file_path, - ) -.. autofunction:: scenario_prologue -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - report_file_path = os.path.join( - "kh_samples", "scenario_prologue", "AnalysisResults.khj" - ) - - # Set the maximum memory "by hand" with an scenario prologue - scenario_prologue = """ - // Max memory 2000 mb - AnalysisSpec.SystemParameters.MemoryLimit 2000 - """ - - # Train the predictor - kh.train_predictor( - dictionary_file_path, - "Adult", - data_table_path, - "class", - report_file_path, - max_trees=0, - scenario_prologue=scenario_prologue, - ) -.. autofunction:: build_deployed_dictionary -.. code-block:: python - - # Imports - import os - from khiops import core as kh - - # Set the file paths - dictionary_file_path = os.path.join(kh.get_samples_dir(), "Iris", "Iris.kdic") - data_table_path = os.path.join(kh.get_samples_dir(), "Iris", "Iris.txt") - output_dir = os.path.join("kh_samples", "build_deployed_dictionary") - deployed_dictionary_file_path = os.path.join(output_dir, "SNB_Iris_deployed.kdic") - report_file_path = os.path.join(output_dir, "AnalysisResults.khj") - - # Train the predictor - _, modeling_dictionary_file_path = kh.train_predictor( - dictionary_file_path, - "Iris", - data_table_path, - "Class", - report_file_path, - max_trees=0, - ) - - # Build the dictionary to read the output of the predictor dictionary file - # It will contain the columns of the table generated by deploying the model - kh.build_deployed_dictionary( - modeling_dictionary_file_path, - "SNB_Iris", - deployed_dictionary_file_path, - ) - - # Print the deployed dictionary - with open(deployed_dictionary_file_path) as deployed_dictionary_file: - for line in deployed_dictionary_file: - print(line, end="") diff --git a/doc/samples/samples_sklearn.rst b/doc/samples/samples_sklearn.rst deleted file mode 100644 index bc1f7b3b..00000000 --- a/doc/samples/samples_sklearn.rst +++ /dev/null @@ -1,815 +0,0 @@ -:orphan: - -.. currentmodule:: samples_sklearn - -Samples sklearn -=============== - -The code snippets on this page demonstrate the basic use of the :py:mod:`khiops.sklearn ` module. - -Script and Jupyter notebook ---------------------------- -The samples in this page are also available as: - -- :download:`Python script <../../khiops/samples/samples_sklearn.py>` -- :download:`Jupyter notebook <../../khiops/samples/samples_sklearn.ipynb>` - -Setup ------ -First make sure you have installed the sample datasets. In a configured -conda shell (ex. *Anaconda Prompt* in Windows) execute: - -.. code-block:: shell - - kh-download-datasets - -If that doesn't work open a python console and execute: - -.. code-block:: python - - from khiops.tools import download_datasets - download_datasets() - - -Samples -------- - -.. autofunction:: khiops_classifier -.. code-block:: python - - # Imports - import os - import pandas as pd - from khiops import core as kh - from khiops.sklearn import KhiopsClassifier - from sklearn import metrics - from sklearn.model_selection import train_test_split - - # Load the dataset into a pandas dataframe - adult_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - adult_df = pd.read_csv(adult_path, sep="\t") - - # Split the whole dataframe into train and test (70%-30%) - adult_train_df, adult_test_df = train_test_split( - adult_df, test_size=0.3, random_state=1 - ) - - # Split the dataset into: - # - the X feature table - # - the y target vector ("class" column) - X_train = adult_train_df.drop("class", axis=1) - X_test = adult_test_df.drop("class", axis=1) - y_train = adult_train_df["class"] - y_test = adult_test_df["class"] - - # Create the classifier object - khc = KhiopsClassifier() - - # Train the classifier - khc.fit(X_train, y_train) - - # Predict the classes on the test dataset - y_test_pred = khc.predict(X_test) - print("Predicted classes (first 10):") - print(y_test_pred[0:10]) - print("---") - - # Predict the class probabilities on the test dataset - y_test_probas = khc.predict_proba(X_test) - print(f"Class order: {khc.classes_}") - print("Predicted class probabilities (first 10):") - print(y_test_probas[0:10]) - print("---") - - # Evaluate accuracy and auc metrics on the test dataset - test_accuracy = metrics.accuracy_score(y_test, y_test_pred) - test_auc = metrics.roc_auc_score(y_test, y_test_probas[:, 1]) - print(f"Test accuracy = {test_accuracy}") - print(f"Test auc = {test_auc}") - - # If you have Khiops Visualization installed you may open the report as follows - # khc.export_report_file("report.khj") - # kh.visualize_report("report.khj") -.. autofunction:: khiops_classifier_multiclass -.. code-block:: python - - # Imports - import os - import pandas as pd - from khiops import core as kh - from khiops.sklearn import KhiopsClassifier - from sklearn import metrics - from sklearn.model_selection import train_test_split - - # Load the dataset into a pandas dataframe - iris_path = os.path.join(kh.get_samples_dir(), "Iris", "Iris.txt") - iris_df = pd.read_csv(iris_path, sep="\t") - - # Split the whole dataframe into train and test (70%-30%) - iris_train_df, iris_test_df = train_test_split(iris_df, test_size=0.3, random_state=1) - - # Split the dataset into: - # - the X feature table - # - the y target vector ("Class" column) - X_train = iris_train_df.drop("Class", axis=1) - X_test = iris_test_df.drop("Class", axis=1) - y_train = iris_train_df["Class"] - y_test = iris_test_df["Class"] - - # Create the classifier object - khc = KhiopsClassifier() - - # Train the classifier - khc.fit(X_train, y_train) - - # Predict the classes on the test dataset - y_test_pred = khc.predict(X_test) - print("Predicted classes (first 10):") - print(y_test_pred[:10]) - print("---") - - # Predict the class probabilities on the test datasets - y_test_probas = khc.predict_proba(X_test) - print(f"Class order: {khc.classes_}") - print("Predicted class probabilities (first 10):") - print(y_test_probas[:10]) - print("---") - - # Evaluate accuracy and auc metrics on the test dataset - test_accuracy = metrics.accuracy_score(y_test, y_test_pred) - test_auc = metrics.roc_auc_score(y_test, y_test_probas, multi_class="ovr") - print(f"Test accuracy = {test_accuracy}") - print(f"Test auc = {test_auc}") -.. autofunction:: khiops_classifier_text -.. code-block:: python - - # Imports - import os - import pandas as pd - from khiops import core as kh - from khiops.sklearn import KhiopsClassifier - from sklearn import metrics - from sklearn.model_selection import train_test_split - - # Load the dataset into a pandas dataframe - data_table_path = os.path.join( - kh.get_samples_dir(), "NegativeAirlineTweets", "NegativeAirlineTweets.txt" - ) - data_df = pd.read_csv(data_table_path, sep="\t") - - # Split the whole dataframe into train and test (70%-30%) - data_train_df, data_test_df = train_test_split(data_df, test_size=0.3, random_state=1) - - # Split the dataset into: - # - the X feature table - # - the y target vector ("negativereason" column) - X_train = data_train_df.drop("negativereason", axis=1) - X_test = data_test_df.drop("negativereason", axis=1) - y_train = data_train_df["negativereason"] - y_test = data_test_df["negativereason"] - - # Set Pandas StringDType on the "text" column - X_train["text"] = X_train["text"].astype("string") - X_test["text"] = X_test["text"].astype("string") - - # Create the classifier object - khc = KhiopsClassifier() - - # Train the classifier - khc.fit(X_train, y_train) - - # Predict the classes on the test dataset - y_test_pred = khc.predict(X_test) - print("Predicted classes (first 10):") - print(y_test_pred[0:10]) - print("---") - - # Predict the class probabilities on the test dataset - y_test_probas = khc.predict_proba(X_test) - print(f"Class order: {khc.classes_}") - print("Predicted class probabilities (first 10):") - print(y_test_probas[0:10]) - print("---") - - # Evaluate the accuracy metric on the test dataset - test_accuracy = metrics.accuracy_score(y_test, y_test_pred) - print(f"Test accuracy = {test_accuracy}") - - # If you have Khiops Visualization installed you may open the report as follows - # khc.export_report_file("report.khj") - # kh.visualize_report("report.khj") -.. autofunction:: khiops_classifier_multitable_star -.. code-block:: python - - # Imports - import os - import pandas as pd - from khiops import core as kh - from khiops.sklearn import KhiopsClassifier, train_test_split_dataset - from sklearn import metrics - - # Load the dataset into pandas dataframes - accidents_data_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") - accidents_df = pd.read_csv( - os.path.join(accidents_data_dir, "Accidents.txt"), - sep="\t", - ) - vehicles_df = pd.read_csv(os.path.join(accidents_data_dir, "Vehicles.txt"), sep="\t") - - # Create the dataset spec and the target - X = { - "main_table": (accidents_df.drop("Gravity", axis=1), ["AccidentId"]), - "additional_data_tables": { - "Vehicles": (vehicles_df, ["AccidentId", "VehicleId"]), - }, - } - y = accidents_df["Gravity"] - - # Split the dataset into train and test - X_train, X_test, y_train, y_test = train_test_split_dataset( - X, y, test_size=0.3, random_state=1 - ) - - # Train the classifier (by default it analyzes 100 multi-table features) - khc = KhiopsClassifier() - khc.fit(X_train, y_train) - - # Predict the class on the test dataset - y_test_pred = khc.predict(X_test) - print("Predicted classes (first 10):") - print(y_test_pred[:10]) - print("---") - - # Predict the class probability on the test dataset - y_test_probas = khc.predict_proba(X_test) - print(f"Class order: {khc.classes_}") - print("Predicted class probabilities (first 10):") - print(y_test_probas[:10]) - print("---") - - # Evaluate accuracy and auc metrics on the test dataset - test_accuracy = metrics.accuracy_score(y_test, y_test_pred) - test_auc = metrics.roc_auc_score(y_test, y_test_probas[:, 1]) - print(f"Test accuracy = {test_accuracy}") - print(f"Test auc = {test_auc}") -.. autofunction:: khiops_classifier_multitable_snowflake -.. code-block:: python - - # Imports - import os - import pandas as pd - from khiops import core as kh - from khiops.sklearn import KhiopsClassifier, train_test_split_dataset - from sklearn import metrics - - # Load the dataset tables into dataframes - accidents_data_dir = os.path.join(kh.get_samples_dir(), "Accidents") - accidents_df = pd.read_csv(os.path.join(accidents_data_dir, "Accidents.txt"), sep="\t") - users_df = pd.read_csv(os.path.join(accidents_data_dir, "Users.txt"), sep="\t") - vehicles_df = pd.read_csv(os.path.join(accidents_data_dir, "Vehicles.txt"), sep="\t") - places_df = pd.read_csv( - os.path.join(accidents_data_dir, "Places.txt"), sep="\t", low_memory=False - ) - - # Build the multi-table dataset spec (drop the target column "Gravity") - X = { - "main_table": (accidents_df.drop("Gravity", axis=1), ["AccidentId"]), - "additional_data_tables": { - "Vehicles": (vehicles_df, ["AccidentId", "VehicleId"]), - "Vehicles/Users": (users_df, ["AccidentId", "VehicleId"]), - "Places": (places_df, ["AccidentId"], True), - }, - } - - # Load the target variable "Gravity" - y = accidents_df["Gravity"] - - # Split into train and test datasets - X_train, X_test, y_train, y_test = train_test_split_dataset(X, y) - - # Train the classifier (by default it creates 1000 multi-table features) - khc = KhiopsClassifier(n_trees=0) - khc.fit(X_train, y_train) - - # Predict the class on the test dataset - y_test_pred = khc.predict(X_test) - print("Predicted classes (first 10):") - print(y_test_pred[:10]) - print("---") - - # Predict the class probability on the test dataset - y_test_probas = khc.predict_proba(X_test) - print(f"Class order: {khc.classes_}") - print("Predicted class probabilities (first 10):") - print(y_test_probas[:10]) - print("---") - - # Evaluate accuracy and auc metrics on the test dataset - test_accuracy = metrics.accuracy_score(y_test_pred, y_test) - test_auc = metrics.roc_auc_score(y_test, y_test_probas[:, 1]) - print(f"Test accuracy = {test_accuracy}") - print(f"Test auc = {test_auc}") -.. autofunction:: khiops_classifier_sparse -.. code-block:: python - - # Imports - from khiops.sklearn import KhiopsClassifier - from sklearn import metrics - from sklearn.datasets import fetch_20newsgroups - from sklearn.feature_extraction.text import HashingVectorizer - - # Load 3 classes of the 20newsgroups dataset - categories = ["comp.graphics", "sci.space", "misc.forsale"] - data_train, y_train = fetch_20newsgroups( - subset="train", - categories=categories, - return_X_y=True, - ) - data_test, y_test = fetch_20newsgroups( - subset="test", - categories=categories, - return_X_y=True, - ) - - # Extract features from the training data using a sparse vectorizer - vectorizer = HashingVectorizer(n_features=2**10, stop_words="english") - X_train = vectorizer.fit_transform(data_train) - - # Extract features from the test data using the same vectorizer - X_test = vectorizer.transform(data_test) - - # Create the classifier object - khc = KhiopsClassifier() - - # Train the classifier - khc.fit(X_train, y_train) - - # Predict the classes on the test dataset - y_test_pred = khc.predict(X_test) - print("Predicted classes (first 10):") - print(y_test_pred[0:10]) - print("---") - - # Predict the class probabilities on the test dataset - y_test_probas = khc.predict_proba(X_test) - print(f"Class order: {khc.classes_}") - print("Predicted class probabilities (first 10):") - print(y_test_probas[0:10]) - print("---") - - # Evaluate accuracy and auc metrics on the test dataset - test_accuracy = metrics.accuracy_score(y_test, y_test_pred) - test_auc = metrics.roc_auc_score(y_test, y_test_probas, multi_class="ovr") - print(f"Test accuracy = {test_accuracy}") - print(f"Test auc = {test_auc}") -.. autofunction:: khiops_classifier_pickle -.. code-block:: python - - # Imports - import os - import pandas as pd - import pickle - from khiops import core as kh - from khiops.sklearn import KhiopsClassifier - - # Create/clean the output directory - results_dir = os.path.join("kh_samples", "khiops_classifier_pickle") - khc_pickle_path = os.path.join(results_dir, "khiops_classifier.pkl") - if os.path.exists(khc_pickle_path): - os.remove(khc_pickle_path) - else: - os.makedirs(results_dir, exist_ok=True) - - # Load the "Iris" dataset - iris_path = os.path.join(kh.get_samples_dir(), "Iris", "Iris.txt") - iris_df = pd.read_csv(iris_path, sep="\t") - X = iris_df.drop("Class", axis=1) - y = iris_df["Class"] - - # Train the model with the Iris dataset - khc = KhiopsClassifier() - khc.fit(X, y) - - # Pickle its content to a file - with open(khc_pickle_path, "wb") as khc_pickle_output_file: - pickle.dump(khc, khc_pickle_output_file) - - # Unpickle it - with open(khc_pickle_path, "rb") as khc_pickle_file: - new_khc = pickle.load(khc_pickle_file) - - # Make some predictions on the training dataset with the unpickled classifier - new_khc.predict(X) - y_predicted = new_khc.predict(X) - print("Predicted classes (first 10):") - print(y_predicted[:10]) - print("---") -.. autofunction:: khiops_classifier_with_hyperparameters -.. code-block:: python - - # Imports - import os - import pandas as pd - from khiops import core as kh - from khiops.sklearn import KhiopsClassifier - from sklearn import metrics - from sklearn.model_selection import train_test_split - - # Load the root table of the dataset into a pandas dataframe - accidents_dataset_path = os.path.join(kh.get_samples_dir(), "AccidentsSummary") - accidents_df = pd.read_csv( - os.path.join(accidents_dataset_path, "Accidents.txt"), - sep="\t", - ) - - # Split the root dataframe into train and test - accidents_train_df, accidents_test_df = train_test_split( - accidents_df, test_size=0.3, random_state=1 - ) - - # Obtain the main X feature table and the y target vector ("Class" column) - y_train = accidents_train_df["Gravity"] - y_test = accidents_test_df["Gravity"] - X_train_main = accidents_train_df.drop("Gravity", axis=1) - X_test_main = accidents_test_df.drop("Gravity", axis=1) - - # Load the secondary table of the dataset into a pandas dataframe - vehicles_df = pd.read_csv( - os.path.join(accidents_dataset_path, "Vehicles.txt"), sep="\t" - ) - - # Split the secondary dataframe with the keys of the split root dataframe - X_train_ids = X_train_main["AccidentId"].to_frame() - X_test_ids = X_test_main["AccidentId"].to_frame() - X_train_secondary = X_train_ids.merge(vehicles_df, on="AccidentId") - X_test_secondary = X_test_ids.merge(vehicles_df, on="AccidentId") - - # Create the dataset multitable specification for the train/test split - # We specify each table with a name and a tuple (dataframe, key_columns) - X_train = { - "main_table": (X_train_main, ["AccidentId"]), - "additional_data_tables": { - "Vehicles": (X_train_secondary, ["AccidentId", "VehicleId"]), - }, - } - X_test = { - "main_table": (X_test_main, ["AccidentId"]), - "additional_data_tables": { - "Vehicles": (X_test_secondary, ["AccidentId", "VehicleId"]), - }, - } - # Train the classifier (by default it analyzes 100 multi-table features) - khc = KhiopsClassifier( - n_features=20, - n_pairs=5, - n_trees=5, - n_selected_features=10, - n_evaluated_features=15, - specific_pairs=[("Light", "Weather"), ("Light", "IntersectionType")], - all_possible_pairs=True, - construction_rules=["TableMode", "TableSelection"], - group_target_value=False, - ) - khc.fit(X_train, y_train) - - # Predict the class on the test dataset - y_test_pred = khc.predict(X_test) - print("Predicted classes (first 10):") - print(y_test_pred[:10]) - print("---") - - # Predict the class probability on the test dataset - y_test_probas = khc.predict_proba(X_test) - print(f"Class order: {khc.classes_}") - print("Predicted class probabilities (first 10):") - print(y_test_probas[:10]) - print("---") - - # Evaluate accuracy and auc metrics on the test dataset - test_accuracy = metrics.accuracy_score(y_test, y_test_pred) - test_auc = metrics.roc_auc_score(y_test, y_test_probas[:, 1]) - print(f"Test accuracy = {test_accuracy}") - print(f"Test auc = {test_auc}") -.. autofunction:: khiops_regressor -.. code-block:: python - - # Imports - import os - import pandas as pd - from khiops import core as kh - from khiops.sklearn import KhiopsRegressor - from sklearn import metrics - from sklearn.model_selection import train_test_split - - # Load the "Adult" dataset and set the target to the "age" column - adult_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - adult_df = pd.read_csv(adult_path, sep="\t") - X = adult_df.drop("age", axis=1) - y = adult_df["age"] - - # Split the whole dataframe into train and test (40%-60% for speed) - X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=1) - - # Create the regressor object - khr = KhiopsRegressor() - - # Train the regressor - khr.fit(X_train, y_train) - - # Predict the values on the test dataset - y_test_pred = khr.predict(X_test) - print("Predicted values for 'age' (first 10):") - print(y_test_pred[:10]) - print("---") - - # Evaluate R2 and MAE metrics on the test dataset - test_r2 = metrics.r2_score(y_test, y_test_pred) - test_mae = metrics.mean_absolute_error(y_test, y_test_pred) - print(f"Test R2 = {test_r2}") - print(f"Test MAE = {test_mae}") - - # If you have Khiops Visualization installed you may open the report as follows - # khr.export_report_file("report.khj") - # kh.visualize_report("report.khj") -.. autofunction:: khiops_encoder -.. code-block:: python - - # Imports - import os - import pandas as pd - from khiops import core as kh - from khiops.sklearn import KhiopsEncoder - - # Load the dataset - iris_path = os.path.join(kh.get_samples_dir(), "Iris", "Iris.txt") - iris_df = pd.read_csv(iris_path, sep="\t") - X = iris_df.drop("Class", axis=1) - y = iris_df["Class"] - - # Create the encoder object - khe = KhiopsEncoder(transform_type_numerical="part_label") - khe.fit(X, y) - - # Transform the training dataset - X_transformed = khe.transform(X) - - # Print both the original and transformed features - print("Original:") - print(X[:10]) - print("---") - print("Encoded feature names:") - print(khe.feature_names_out_) - print("Encoded data:") - print(X_transformed[:10]) - print("---") - - # If you have Khiops Visualization installed you may open the report as follows - # khe.export_report_file("report.khj") - # kh.visualize_report("report.khj") -.. autofunction:: khiops_encoder_multitable_star -.. code-block:: python - - # Imports - import os - import pandas as pd - from khiops import core as kh - from khiops.sklearn import KhiopsEncoder - - # Load the dataset tables into dataframe - accidents_data_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") - accidents_df = pd.read_csv( - os.path.join(accidents_data_dir, "Accidents.txt"), - sep="\t", - ) - vehicles_df = pd.read_csv(os.path.join(accidents_data_dir, "Vehicles.txt"), sep="\t") - - # Build the multi-table dataset spec (drop the target column "Gravity") - X = { - "main_table": (accidents_df.drop("Gravity", axis=1), ["AccidentId"]), - "additional_data_tables": { - "Vehicles": (vehicles_df, ["AccidentId", "VehicleId"]), - }, - } - - # Load the target variable "Gravity" - y = accidents_df["Gravity"] - - # Create the KhiopsEncoder with 5 multitable features and fit it - khe = KhiopsEncoder(n_features=10) - khe.fit(X, y) - - # Transform the train dataset - print("Encoded feature names:") - print(khe.feature_names_out_) - print("Encoded data:") - print(khe.transform(X)[:10]) -.. autofunction:: khiops_encoder_multitable_snowflake -.. code-block:: python - - # Imports - import os - import pandas as pd - from khiops import core as kh - from khiops.sklearn import KhiopsEncoder - - # Load the tables into dataframes - accidents_data_dir = os.path.join(kh.get_samples_dir(), "Accidents") - accidents_df = pd.read_csv(os.path.join(accidents_data_dir, "Accidents.txt"), sep="\t") - users_df = pd.read_csv(os.path.join(accidents_data_dir, "Users.txt"), sep="\t") - vehicles_df = pd.read_csv(os.path.join(accidents_data_dir, "Vehicles.txt"), sep="\t") - places_df = pd.read_csv( - os.path.join(accidents_data_dir, "Places.txt"), sep="\t", low_memory=False - ) - - # Build the multi-table dataset spec (drop the target column "Gravity") - X = { - "main_table": (accidents_df.drop("Gravity", axis=1), ["AccidentId"]), - "additional_data_tables": { - "Vehicles": (vehicles_df, ["AccidentId", "VehicleId"]), - "Vehicles/Users": (users_df, ["AccidentId", "VehicleId"]), - "Places": (places_df, ["AccidentId"], True), - }, - } - - # Load the target variable "Gravity" - y = accidents_df["Gravity"] - - # Create the KhiopsEncoder with 10 additional multitable features and fit it - khe = KhiopsEncoder(n_features=10) - khe.fit(X, y) - - # Transform the train dataset - print("Encoded feature names:") - print(khe.feature_names_out_) - print("Encoded data:") - print(khe.transform(X)[:10]) -.. autofunction:: khiops_encoder_pipeline_with_hgbc -.. code-block:: python - - # Imports - import os - import pandas as pd - from khiops import core as kh - from khiops.sklearn import KhiopsEncoder - from sklearn import metrics - from sklearn.compose import ColumnTransformer - from sklearn.ensemble import HistGradientBoostingClassifier - from sklearn.model_selection import train_test_split - from sklearn.pipeline import Pipeline - from sklearn.preprocessing import OneHotEncoder - - # Load the dataset into dataframes - adult_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") - adult_df = pd.read_csv(adult_path, sep="\t") - X = adult_df.drop("class", axis=1) - y = adult_df["class"] - - # Split the dataset into train and test (70%-30%) - X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1) - - # Create the pipeline and fit it. Steps: - # - The khiops supervised column encoder, generates a full-categorical table - # - One hot encoder in all columns - # - Train the HGB classifier - pipe_steps = [ - ("khiops_enc", KhiopsEncoder()), - ( - "onehot_enc", - ColumnTransformer([], remainder=OneHotEncoder(sparse_output=False)), - ), - ("hgb_clf", HistGradientBoostingClassifier()), - ] - pipe = Pipeline(pipe_steps) - pipe.fit(X_train, y_train) - - # Predict the classes on the test dataset - y_test_pred = pipe.predict(X_test) - print("Predicted classes (first 10):") - print(y_test_pred[:10]) - print("---") - - # Predict the class probabilities on the test dataset - y_test_probas = pipe.predict_proba(X_test) - print("Predicted class probabilities (first 10):") - print(y_test_probas[:10]) - print("---") - - # Evaluate accuracy and auc metrics on the test dataset - test_accuracy = metrics.accuracy_score(y_test, y_test_pred) - test_auc = metrics.roc_auc_score(y_test, y_test_probas[:, 1]) - print(f"Test accuracy = {test_accuracy}") - print(f"Test auc = {test_auc}") -.. autofunction:: khiops_encoder_with_hyperparameters -.. code-block:: python - - # Imports - import os - import pandas as pd - from khiops import core as kh - from khiops.sklearn import KhiopsEncoder - - # Load the tables into dataframes - accidents_data_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") - accidents_df = pd.read_csv(os.path.join(accidents_data_dir, "Accidents.txt"), sep="\t") - vehicles_df = pd.read_csv(os.path.join(accidents_data_dir, "Vehicles.txt"), sep="\t") - - # Build the multi-table dataset spec (drop the target column "Gravity") - X = { - "main_table": (accidents_df.drop("Gravity", axis=1), ["AccidentId"]), - "additional_data_tables": { - "Vehicles": (vehicles_df, ["AccidentId", "VehicleId"]), - }, - } - - # Load the target variable "Gravity" - y = accidents_df["Gravity"] - - # Create the KhiopsEncoder with 10 additional multitable features and fit it - khe = KhiopsEncoder( - n_features=20, - n_pairs=5, - n_trees=5, - specific_pairs=[("Light", "Weather"), ("Light", "IntersectionType")], - all_possible_pairs=True, - construction_rules=["TableMode", "TableSelection"], - group_target_value=False, - informative_features_only=True, - keep_initial_variables=True, - transform_type_categorical="part_id", - transform_type_numerical="part_id", - transform_type_pairs="part_id", - ) - khe.fit(X, y) - - # Transform the train dataset - print("Encoded feature names:") - print(khe.feature_names_out_) - print("Encoded data:") - print(khe.transform(X)[:10]) -.. autofunction:: khiops_coclustering -.. code-block:: python - - # Imports - import os - import pandas as pd - from khiops import core as kh - from khiops.sklearn import KhiopsCoclustering - from sklearn.model_selection import train_test_split - - # Load the secondary table of the dataset into a pandas dataframe - splice_data_dir = os.path.join(kh.get_samples_dir(), "SpliceJunction") - splice_dna_df = pd.read_csv( - os.path.join(splice_data_dir, "SpliceJunctionDNA.txt"), sep="\t" - ) - - # Train with only 70% of data (for speed in this example) - X, _ = train_test_split(splice_dna_df, test_size=0.3, random_state=1) - - # Create the KhiopsCoclustering instance - khcc = KhiopsCoclustering() - - # Train the model with the whole dataset - khcc.fit(X, id_column="SampleId") - - # Predict the clusters in some instances - X_clusters = khcc.predict(X) - print("Predicted clusters (first 10)") - print(X_clusters[:10]) - print("---") - - # If you have Khiops Co-Visualization installed you may open the report as follows - # khcc.export_report_file("report.khcj") - # kh.visualize_report("report.khcj") -.. autofunction:: khiops_coclustering_simplify -.. code-block:: python - - # Imports - import os - import pandas as pd - from khiops import core as kh - from khiops.sklearn import KhiopsCoclustering - from sklearn.model_selection import train_test_split - - # Load the secondary table of the dataset into a pandas dataframe - splice_data_dir = os.path.join(kh.get_samples_dir(), "SpliceJunction") - splice_dna_X = pd.read_csv( - os.path.join(splice_data_dir, "SpliceJunctionDNA.txt"), sep="\t" - ) - - # Train with only 70% of data (for speed in this example) - X, _ = train_test_split(splice_dna_X, test_size=0.3, random_state=1) - - # Create the KhiopsCoclustering instance - khcc = KhiopsCoclustering() - - # Train the model with the whole dataset - khcc.fit(X, id_column="SampleId") - - # Simplify coclustering along the individual ID dimension - simplified_khcc = khcc.simplify(max_part_numbers={"SampleId": 3}) - - # Predict the clusters using the simplified model - X_clusters = simplified_khcc.predict(X) - print("Predicted clusters (only three at most)") - print(X_clusters) - print("---") diff --git a/doc/site/_static/css/custom.css b/doc/site/_static/css/custom.css new file mode 100644 index 00000000..472373dc --- /dev/null +++ b/doc/site/_static/css/custom.css @@ -0,0 +1,46 @@ +/* Orange brand colors — https://brand.orange.com */ +:root { + --md-primary-fg-color: #FF7900; + --md-primary-fg-color--light: #FFD200; + --md-primary-fg-color--dark: #F16E00; + --md-accent-fg-color: #F16E00; + --md-typeset-a-color: #F16E00; +} + +[data-md-color-scheme="slate"] { + --md-primary-fg-color: #FF7900; + --md-primary-fg-color--light: #FFD200; + --md-primary-fg-color--dark: #F16E00; + --md-accent-fg-color: #F16E00; + --md-typeset-a-color: #FF7900; +} + +/* Custom header sizes */ +h1 { + font-size: 1.85em; +} + +h2 { + font-size: 1.55em; +} + +h3 { + font-size: 1.30em; +} + +h4 { + font-size: 1.10em; +} + +h5 { + font-size: 0.90em; +} + +/* Body font */ +:root { + --md-text-font: "Helvetica Neue", Helvetica, sans-serif; +} + +img.sidebar-logo { + width: 40px; +} diff --git a/doc/_static/images/khiops_logo.png b/doc/site/_static/images/khiops_logo.png similarity index 100% rename from doc/_static/images/khiops_logo.png rename to doc/site/_static/images/khiops_logo.png diff --git a/doc/_static/images/new_khiops_logo.svg b/doc/site/_static/images/new_khiops_logo.svg similarity index 100% rename from doc/_static/images/new_khiops_logo.svg rename to doc/site/_static/images/new_khiops_logo.svg diff --git a/doc/_static/images/new_khiops_logo_with_border.svg b/doc/site/_static/images/new_khiops_logo_with_border.svg similarity index 100% rename from doc/_static/images/new_khiops_logo_with_border.svg rename to doc/site/_static/images/new_khiops_logo_with_border.svg diff --git a/doc/_static/images/orange_small_logo.png b/doc/site/_static/images/orange_small_logo.png similarity index 100% rename from doc/_static/images/orange_small_logo.png rename to doc/site/_static/images/orange_small_logo.png diff --git a/doc/site/_templates/python/material/children.html.jinja b/doc/site/_templates/python/material/children.html.jinja new file mode 100644 index 00000000..33250506 --- /dev/null +++ b/doc/site/_templates/python/material/children.html.jinja @@ -0,0 +1,206 @@ +{#- Template for members (children) of an object. + +Override: inserts per-category summary tables after each category heading. +Uses hardcoded English strings instead of lang.t() to avoid template resolution issues. + +Context: + obj (griffe.Object): The object to render. + config (dict): The configuration options. + root_members (bool): Whether the object is the root object. + heading_level (int): The HTML heading level to use. +-#} + +{% if obj.all_members %} + {{ log.debug("Rendering children of " + obj.path) }} + +
+ + {% if root_members %} + {% set members_list = config.members %} + {% else %} + {% set members_list = none %} + {% endif %} + + {% if config.group_by_category %} + + {% with %} + + {% if config.show_category_heading %} + {% set extra_level = 1 %} + {% else %} + {% set extra_level = 0 %} + {% endif %} + + {% with attributes = obj.attributes|filter_objects( + filters=config.filters, + members_list=members_list, + inherited_members=config.inherited_members, + keep_no_docstrings=config.show_if_no_docstring, + )|rejectattr("is_imported")|list %} + {% if attributes %} + {% if config.show_category_heading %} + {% filter heading(heading_level, id=html_id ~ "-attributes", skip_inventory=config.skip_local_inventory) %}Attributes{% endfilter %} + {% endif %} + {% if config.summary %} + {% include "summary/attributes.html.jinja" with context %} + {% endif %} + {% with heading_level = heading_level + extra_level %} + {% for attribute in attributes|order_members(config.members_order, members_list) %} + {% if config.filters == "public" or members_list is not none or (not attribute.is_imported or attribute.is_public) %} + {% include attribute|get_template with context %} + {% endif %} + {% endfor %} + {% endwith %} + {% endif %} + {% endwith %} + + {% with type_aliases = obj.type_aliases|filter_objects( + filters=config.filters, + members_list=members_list, + inherited_members=config.inherited_members, + keep_no_docstrings=config.show_if_no_docstring, + )|rejectattr("is_imported")|list %} + {% if type_aliases %} + {% if config.show_category_heading %} + {% filter heading(heading_level, id=html_id ~ "-type_aliases") %}Type Aliases{% endfilter %} + {% endif %} + {% if config.summary %} + {% include "summary/type_aliases.html.jinja" with context %} + {% endif %} + {% with heading_level = heading_level + extra_level %} + {% for type_alias in type_aliases|order_members(config.members_order, members_list) %} + {% if config.filters == "public" or members_list is not none or (not type_alias.is_imported or type_alias.is_public) %} + {% include type_alias|get_template with context %} + {% endif %} + {% endfor %} + {% endwith %} + {% endif %} + {% endwith %} + + {% with classes = obj.classes|filter_objects( + filters=config.filters, + members_list=members_list, + inherited_members=config.inherited_members, + keep_no_docstrings=config.show_if_no_docstring, + )|rejectattr("is_imported")|list %} + {% if classes %} + {% if config.show_category_heading %} + {% filter heading(heading_level, id=html_id ~ "-classes", skip_inventory=config.skip_local_inventory) %}Classes{% endfilter %} + {% endif %} + {% if config.summary %} + {% include "summary/classes.html.jinja" with context %} + {% endif %} + {% with heading_level = heading_level + extra_level %} + {% for class in classes|order_members(config.members_order, members_list) %} + {% if config.filters == "public" or members_list is not none or (not class.is_imported or class.is_public) %} + {% include class|get_template with context %} + {% endif %} + {% endfor %} + {% endwith %} + {% endif %} + {% endwith %} + + {% with functions = obj.functions|filter_objects( + filters=config.filters, + members_list=members_list, + inherited_members=config.inherited_members, + keep_no_docstrings=config.show_if_no_docstring, + )|rejectattr("is_imported")|list %} + {% if functions %} + {% if config.show_category_heading %} + {% filter heading(heading_level, id=html_id ~ "-functions", skip_inventory=config.skip_local_inventory) %}Functions{% endfilter %} + {% endif %} + {% if config.summary %} + {% include "summary/functions.html.jinja" with context %} + {% endif %} + {% with heading_level = heading_level + extra_level %} + {% for function in functions|order_members(config.members_order, members_list) %} + {% if not (obj.kind.value == "class" and function.name == "__init__" and config.merge_init_into_class) %} + {% if config.filters == "public" or members_list is not none or (not function.is_imported or function.is_public) %} + {% include function|get_template with context %} + {% endif %} + {% endif %} + {% endfor %} + {% endwith %} + {% endif %} + {% endwith %} + + {% if config.show_submodules %} + {% with modules = obj.modules|filter_objects( + filters=config.filters, + members_list=members_list, + inherited_members=config.inherited_members, + keep_no_docstrings=config.show_if_no_docstring, + )|rejectattr("is_imported")|list %} + {% if modules %} + {% if config.show_category_heading %} + {% filter heading(heading_level, id=html_id ~ "-modules", skip_inventory=config.skip_local_inventory) %}Modules{% endfilter %} + {% endif %} + {% if config.summary %} + {% include "summary/modules.html.jinja" with context %} + {% endif %} + {% with heading_level = heading_level + extra_level %} + {% for module in modules|order_members("alphabetical", members_list) %} + {% if config.filters == "public" or members_list is not none or (not module.is_alias or module.is_public) %} + {% include module|get_template with context %} + {% endif %} + {% endfor %} + {% endwith %} + {% endif %} + {% endwith %} + {% endif %} + + {% endwith %} + + {% else %} + + {% for child in obj.all_members + |filter_objects( + filters=config.filters, + members_list=members_list, + inherited_members=config.inherited_members, + keep_no_docstrings=config.show_if_no_docstring, + ) + |order_members(config.members_order, members_list) + %} + + {% if not (obj.is_class and child.name == "__init__" and config.merge_init_into_class) %} + + {% if config.filters == "public" or members_list is not none or (not child.is_imported or child.is_public) %} + {% if child.is_attribute %} + {% with attribute = child %} + {% include attribute|get_template with context %} + {% endwith %} + + {% elif child.is_type_alias %} + {% with type_alias = child %} + {% include type_alias|get_template with context %} + {% endwith %} + + {% elif child.is_class %} + {% with class = child %} + {% include class|get_template with context %} + {% endwith %} + + {% elif child.is_function %} + {% with function = child %} + {% include function|get_template with context %} + {% endwith %} + + {% elif child.is_module and config.show_submodules %} + {% with module = child %} + {% include module|get_template with context %} + {% endwith %} + + {% endif %} + {% endif %} + + {% endif %} + + {% endfor %} + + {% endif %} + +
+ +{% endif %} diff --git a/doc/site/_templates/python/material/module.html.jinja b/doc/site/_templates/python/material/module.html.jinja new file mode 100644 index 00000000..306b3bde --- /dev/null +++ b/doc/site/_templates/python/material/module.html.jinja @@ -0,0 +1,4 @@ +{#- Override: suppress top-level summary; per-category summaries are in children.html.jinja -#} +{% extends "_base/module.html.jinja" %} + +{% block summary scoped %}{% endblock summary %} diff --git a/doc/site/core/analysis_results.md b/doc/site/core/analysis_results.md new file mode 100644 index 00000000..2a315808 --- /dev/null +++ b/doc/site/core/analysis_results.md @@ -0,0 +1,8 @@ +# khiops.core.analysis_results + +*Submodule of khiops.core* + +::: khiops.core.analysis_results + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/site/core/api.md b/doc/site/core/api.md new file mode 100644 index 00000000..1b6876af --- /dev/null +++ b/doc/site/core/api.md @@ -0,0 +1,8 @@ +# khiops.core.api + +*Submodule of khiops.core* + +::: khiops.core.api + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/site/core/coclustering_results.md b/doc/site/core/coclustering_results.md new file mode 100644 index 00000000..4a6c9468 --- /dev/null +++ b/doc/site/core/coclustering_results.md @@ -0,0 +1,8 @@ +# khiops.core.coclustering_results + +*Submodule of khiops.core* + +::: khiops.core.coclustering_results + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/site/core/dictionary.md b/doc/site/core/dictionary.md new file mode 100644 index 00000000..2b5950a3 --- /dev/null +++ b/doc/site/core/dictionary.md @@ -0,0 +1,8 @@ +# khiops.core.dictionary + +*Submodule of khiops.core* + +::: khiops.core.dictionary + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/site/core/exceptions.md b/doc/site/core/exceptions.md new file mode 100644 index 00000000..8a19fb95 --- /dev/null +++ b/doc/site/core/exceptions.md @@ -0,0 +1,8 @@ +# khiops.core.exceptions + +*Submodule of khiops.core* + +::: khiops.core.exceptions + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/site/core/helpers.md b/doc/site/core/helpers.md new file mode 100644 index 00000000..f85ec003 --- /dev/null +++ b/doc/site/core/helpers.md @@ -0,0 +1,8 @@ +# khiops.core.helpers + +*Submodule of khiops.core* + +::: khiops.core.helpers + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/site/core/index.md b/doc/site/core/index.md new file mode 100644 index 00000000..bcb9bf69 --- /dev/null +++ b/doc/site/core/index.md @@ -0,0 +1,29 @@ +# khiops.core + +::: khiops.core + options: + members: false + show_root_heading: false + +!!! note + + Input types in this module are flexible. See [Notes](../notes.md#core-api-input-types). + +!!! note + + For convenience, the public members of the above modules are imported to the `core` namespace. + For example the function [train_predictor][khiops.core.api.train_predictor] can be used as follows: + + ```python + from khiops import core as kh + kh.train_predictor(...) + ``` + +## Modules + +- [api](api.md) - Main functions for training models and deploying predictors +- [dictionary](dictionary.md) - Data classes for Khiops dictionary files +- [analysis_results](analysis_results.md) - Data classes for Khiops report files +- [coclustering_results](coclustering_results.md) - Data classes for coclustering report files +- [exceptions](exceptions.md) - Exception classes +- [helpers](helpers.md) - Helper functions diff --git a/doc/site/index.md b/doc/site/index.md new file mode 100644 index 00000000..2dc1e795 --- /dev/null +++ b/doc/site/index.md @@ -0,0 +1,48 @@ +# Khiops Python API Docs + +Welcome to the Khiops Python API documentation page. + +## Installation + +Khiops can be installed in a Python virtual environment using `pip`, +under Linux and macOS (in a `bash` shell): + +```bash +python -m venv khiops-venv +source khiops-venv/bin/activate +pip install -U khiops +``` + +under Windows (in a `powershell` shell): + +```powershell +python -m venv khiops-venv +khiops-venv\Scripts\activate +pip install -U khiops +``` + +Alternatively, you can install Khiops with the [Conda package manager](https://docs.conda.io/en/latest/): + +```bash +conda create -n khiops-env +conda activate khiops-env +conda install -c conda-forge khiops +``` + +More details and other installation methods are documented at the [Khiops website](https://www.khiops.org/setup). + +## Main Submodules + +This package contains the following main submodules. + +### `sklearn` submodule + +The [sklearn](sklearn/index.md) module is a [Scikit-learn](https://scikit-learn.org) based interface to +Khiops. Use it if you are just started using Khiops and are familiar with the Scikit-learn workflow +based on dataframes and estimator classes. + +### `core` submodule + +The [core](core/index.md) module is a pure Python library exposing all Khiops functionalities. Use it if +you are familiar with the Khiops workflow based on plain-text tabular data files and dictionary +files (`.kdic`). diff --git a/doc/site/internal/common.md b/doc/site/internal/common.md new file mode 100644 index 00000000..c30d03fb --- /dev/null +++ b/doc/site/internal/common.md @@ -0,0 +1,8 @@ +# khiops.core.internals.common + +*Submodule of khiops.core.internals* + +::: khiops.core.internals.common + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/site/internal/dataset.md b/doc/site/internal/dataset.md new file mode 100644 index 00000000..c8222184 --- /dev/null +++ b/doc/site/internal/dataset.md @@ -0,0 +1,8 @@ +# khiops.sklearn.dataset + +*Submodule of khiops.sklearn* + +::: khiops.sklearn.dataset + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/site/internal/filesystems.md b/doc/site/internal/filesystems.md new file mode 100644 index 00000000..59da3b8f --- /dev/null +++ b/doc/site/internal/filesystems.md @@ -0,0 +1,8 @@ +# khiops.core.internals.filesystems + +*Submodule of khiops.core.internals* + +::: khiops.core.internals.filesystems + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/site/internal/index.md b/doc/site/internal/index.md new file mode 100644 index 00000000..d1fb4e66 --- /dev/null +++ b/doc/site/internal/index.md @@ -0,0 +1,16 @@ +# Internals + +These are internal modules with no "data science" functionality. Their documentation is available +for completeness. + +## Modules + +- [dataset](dataset.md) - `khiops.sklearn.dataset` +- [common](common.md) - `khiops.core.internals.common` +- [filesystems](filesystems.md) - `khiops.core.internals.filesystems` +- [io](io.md) - `khiops.core.internals.io` +- [runner](runner.md) - `khiops.core.internals.runner` +- [scenario](scenario.md) - `khiops.core.internals.scenario` +- [task](task.md) - `khiops.core.internals.task` +- [types](types.md) - `khiops.core.internals.types` +- [version](version.md) - `khiops.core.internals.version` diff --git a/doc/site/internal/io.md b/doc/site/internal/io.md new file mode 100644 index 00000000..401fd462 --- /dev/null +++ b/doc/site/internal/io.md @@ -0,0 +1,8 @@ +# khiops.core.internals.io + +*Submodule of khiops.core.internals* + +::: khiops.core.internals.io + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/site/internal/runner.md b/doc/site/internal/runner.md new file mode 100644 index 00000000..97d83e16 --- /dev/null +++ b/doc/site/internal/runner.md @@ -0,0 +1,8 @@ +# khiops.core.internals.runner + +*Submodule of khiops.core.internals* + +::: khiops.core.internals.runner + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/site/internal/scenario.md b/doc/site/internal/scenario.md new file mode 100644 index 00000000..a2b4763e --- /dev/null +++ b/doc/site/internal/scenario.md @@ -0,0 +1,8 @@ +# khiops.core.internals.scenario + +*Submodule of khiops.core.internals* + +::: khiops.core.internals.scenario + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/site/internal/task.md b/doc/site/internal/task.md new file mode 100644 index 00000000..3bca37c2 --- /dev/null +++ b/doc/site/internal/task.md @@ -0,0 +1,8 @@ +# khiops.core.internals.task + +*Submodule of khiops.core.internals* + +::: khiops.core.internals.task + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/site/internal/types.md b/doc/site/internal/types.md new file mode 100644 index 00000000..fb84f6b2 --- /dev/null +++ b/doc/site/internal/types.md @@ -0,0 +1,8 @@ +# khiops.core.internals.types + +*Submodule of khiops.core.internals* + +::: khiops.core.internals.types + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/site/internal/version.md b/doc/site/internal/version.md new file mode 100644 index 00000000..eca720e3 --- /dev/null +++ b/doc/site/internal/version.md @@ -0,0 +1,8 @@ +# khiops.core.internals.version + +*Submodule of khiops.core.internals* + +::: khiops.core.internals.version + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/site/multi_table_primer.md b/doc/site/multi_table_primer.md new file mode 100644 index 00000000..4f49de9f --- /dev/null +++ b/doc/site/multi_table_primer.md @@ -0,0 +1,335 @@ +# Multi-Table Learning Primer + +Machine learning on tabular data is traditionally performed on a single table containing a record +for each statistical object of the sample. However, data is usually stored in databases with +multiple tables whose relationships are specified through a schema. Thus, before training +a predictor, a preprocessing is necessary to flatten the relational tables into a single one +containing all relevant information for the learning task. + +This preprocessing (which belongs to the feature engineering process) is often very time consuming. +One of the main Khiops features is to automate this process by natively learning predictors from +multi-table datasets. Specifically, Khiops automatically: + +- generates features (aggregates) from the multi-table schema +- evaluates the predictive value of the generated features +- selects a small subset of the generated features to build a predictor + +## Supported Multi-Table Schemas + +Khiops allows to train an estimator on the two most common dataset schemas: *star* and *snowflake*. +More complex schemas are supported only by the `core` library at the moment. + +### Star Schema + +In this schema there is one main table containing the identifier of the statistical object and its +basic properties. The main table points to one or more secondary ones, each having or more records +associated to a given object. + +Here is a simple example of a star schema (for brevity we do not show the tables' columns): + +```text +Customer(id_customer) +| ++---1:1--- Address(id_customer) +| ++---1:n--- Service(id_customer, id_product) +``` + +The statistical object in this schema is a `Customer` that is associated to a unique `Address` +and to one or more `Services`. In parentheses we show the key columns of each table that act as +[foreign keys](https://en.wikipedia.org/wiki/Foreign_key) to associate the records of each table. + +You can find more information about the star schema in its [Wikipedia article](https://en.wikipedia.org/wiki/Star_schema). + +### Snowflake Schema + +The snowflake schema generalizes the star schema by allowing each secondary table to be in a star +schema by itself, forming a tree whose root is the main table. + +Here we extend the previous star schema to a snowflake schema + +```text +Customer(id_customer) +| ++---1:1--- Address(id_customer) +| ++---1:n--- Service(id_customer, id_product) + | + +---1:n--- Usage(id_customer, id_product) +``` + +Again, the statistical object in this schema is a `Customer` that is associated to a unique +`Address` and to one or more `Services`. But additionally, each `Service` is associated to one +or more `Usages`. + +You can find more information about the snowflake schema in [its Wikipedia article](https://en.wikipedia.org/wiki/Snowflake_schema). + +## Multi-Table Learning with Scikit-Learn Estimators + +The supervised estimators in [sklearn](sklearn/index.md) handle multi-table datasets with a special input +feature object `X`. Specifically, instead of a [pandas.DataFrame][], `X` must be a `dict` that +specifies the dataset schema in the following way: + +```python +X = { + "main_table": (, ), + "additional_data_tables" : { + : ( + , [], + ), + : ( + , [], + ), + ... + } +} +``` + +The three fields of this dictionary are: + +- `main_table`: a 2-tuple containing the following fields: + - The [pandas.DataFrame][] object of the main table. + - The key columns' names: A list of strings. + +- `additional_data_tables`: A dictionary indexed by the data paths to the secondary + tables. Each data path is associated to a 2-tuple containing the following fields: + + - The [pandas.DataFrame][] object of the secondary table. + - The key columns' names : A list of strings. + - optionally, a flag which indicates if the secondary table is in + a `1:1` relationship to its parent table. + +!!! note + + With respect to Khiops, Khiops Python sklearn estimators have some limitations. They currently do not + support external data tables. + + This feature will be available in upcoming releases. If you need to use it, you can use the `khiops.core` + sub-module (see below). + +### Examples + +#### Star Schema + +For the `AccidentsSummary` dataset above where tables are related through the following *star* +schema: + +```text +Accident(AccidentId) +| ++---1:n--- Vehicle(AccidentId, VehicleId) +``` + +We build the input `X` as follows: + +```python +accidents_df = pd.read_csv(f"{kh.get_samples_dir()}/AccidentsSummary/Accidents.txt", sep="\t") +vehicles_df = pd.read_csv(f"{kh.get_samples_dir()}/AccidentsSummary/Vehicles.txt", sep="\t") +X = { + "main_table" : (accidents_df.drop("Gravity", axis=1), ["AccidentId"]), + "additional_data_tables": { + "Vehicle": (vehicles_df, ["AccidentId", "VehicleId"]) + } + } +``` + +#### Snowflake Schema + +For the `Accidents` dataset (an extension of `AccidentsSummary`) where tables are related +through the following *snowflake* schema + +```text +Accident(AccidentId) +| ++--- 1:n --- Vehicle(AccidentId, VehicleId) +| | +| +--- 1:n --- User(AccidentId, VehicleId) +| ++--- 1:1 --- Place(AccidentId) +``` + +We build the input `X` as follows: + +```python +accidents_df = pd.read_csv(f"{kh.get_samples_dir()}/Accidents/Accidents.txt", sep="\t") +vehicles_df = pd.read_csv(f"{kh.get_samples_dir()}/Accidents/Vehicles.txt", sep="\t") +users_df = pd.read_csv(f"{kh.get_samples_dir()}/Accidents/Users.txt", sep="\t") +places_df = pd.read_csv(f"{kh.get_samples_dir()}/Accidents/Places.txt", sep="\t") + +X = { + "main_table": (accidents_df.drop("Gravity", axis=1), ["AccidentId"]), + "additional_data_tables": { + "Vehicles": (vehicles_df, ["AccidentId", "VehicleId"]), + "Vehicles/Users": (users_df, ["AccidentId", "VehicleId"]), + "Places": (places_df, ["AccidentId"], True), + }, +} +``` + +Both datasets can be found in the Khiops samples directory. + +## Multi-table learning with the Core API + +The functions in [khiops.core][] that allow using multi-table datasets have the optional parameter +`additional_data_tables`. This dictionary links the secondary tables to their data file paths and +it's indexed by their **data paths** which are specified as the regular expression: + +```text +(/external_entity_table_name)?(/table_variable_name)* +``` + +Specifically: + +- the data path for an *external entity* table (see below) is its name, preceded by a forward slash +- the data path for a secondary table is composed of the data path of its source root table if it + is an external entity table followed by the chain of *table variable* names leading to it. + The path parts are separated by a forward slash `/`. + +Types of secondary tables include: + +- `Table` type: sub-tables in a 0:n relationship + + - Example: A "Customers" main table with a "Services" secondary table describing the services that + each customer has subscribed to. So a customer can have zero services (inactive customer) or one + or many. + +- `Entity` table: sub-tables in a 0:1 relationship + + - Example: A "Customers" main table with a "Address" secondary table describing the address of + a customer with fields such as "Street", "StreetNumber", etc. In this setting a customer can + have at most one address. + +- External data tables: Another table set (with a `Root` table) that is entirely loaded in + memory + + - Example: The "Address" sub-table in the example above can point to a table "City" containing + information about the city where the address is located. The number of cities is much smaller + than the number of addresses so it may make sense to load it entirely in memory for efficiency + reasons. + +Note that besides the root table names the components of a data path are **table variable names** +and not *table names*. For further details about the multi-table capabilities of Khiops refer to the +documentation at [the Khiops site](https://khiops.org/ui-docs/khiops/). + +The class [DictionaryDomain][khiops.core.dictionary.DictionaryDomain] provides the helper method +[extract_data_paths][khiops.core.dictionary.DictionaryDomain.extract_data_paths] that extracts the +data paths from a given root dictionary. + +!!! note + + To execute multi-table tasks, Khiops requires the data table files **to be sorted** by their key + columns. You may use the [sort_data_table][khiops.core.api.sort_data_table] function to + preprocess your data files before executing these tasks. + +### Examples + +#### Star Schema + +Let's consider the following Khiops dictionary file for the `AccidentsSummary` dataset +found in Khiops samples. Note that tables in this dataset are related through a *star* schema. + +```c +// samples/AccidentsSummary/Accidents.kdic +Root Dictionary Accident(AccidentId) +{ + Categorical AccidentId; + Categorical Gravity; + // + Table(Vehicle) Vehicles; // This is a table variable (type Table) +}; + +Dictionary Vehicle(AccidentId, VehicleId) +{ + Categorical AccidentId; + Categorical VehicleId; + Categorical Direction; + Categorical Category; + // +}; +``` + +This dictionary represents the following relational schema: + +```text +Accident(AccidentId) +| ++---1:n--- Vehicle(AccidentId, VehicleId) +``` + +In this case the `additional_data_tables` argument consists of only one path: that of the +secondary table `Vehicle`. Since it is pointed by the main table `Accident` via the table +variable `Vehicle` the `additional_data_tables` parameter should be set as: + +```python +additional_data_tables = {"Vehicles": f"{kh.get_samples_dir()}/Vehicles.txt"} +``` + +#### Snowflake Schema + +Let's now consider the dictionary file for the `Accidents` dataset where tables are related +through a *snowflake* schema. + +```c +// samples/Accidents/Accidents.kdic +Root Dictionary Accident(AccidentId) +{ + Categorical AccidentId; + // The target "Gravity" is calculated from a sub-table + // See: https://khiops.org/setup/KhiopsGuide.pdf#page=58 + Categorical Gravity = IfC( + G(TableSum(Vehicles, TableCount(TableSelection(Users, EQc(Gravity, "Death")))), 0), + "Lethal", "NonLethal"); + // + Entity(Place) Place; // This is a table variable type Entity: 1-1 relation) + Table(Vehicle) Vehicles; // This is a table variable (type Table) +}; + +Dictionary Place(AccidentId) +{ + Categorical AccidentId; + Categorical RoadType; + // + Categorical SchoolNear; +}; + + +Dictionary Vehicle(AccidentId, VehicleId) +{ + Categorical AccidentId; + Categorical VehicleId; + // + Table(User) Users; // This is a table variable (type Table) +}; + +Dictionary User(AccidentId, VehicleId) { + Categorical AccidentId; + Categorical VehicleId; + Categorical Seat; + Categorical Category; + Unused Categorical Gravity; // Must be disabled since the target is a function of it + // + Numerical BirthYear; +}; +``` + +This time, the relational schema is as follows: + +```text +Accident(AccidentId) +| ++--- 1:n --- Vehicle(AccidentId, VehicleId) +| | +| +--- 1:n --- User(AccidentId, VehicleId) +| ++--- 1:1 --- Place(AccidentId) +``` + +The `additional_data_tables` parameter must be set as: + +```python +additional_data_tables = { + "Place": "/path/to/Places.txt", + "Vehicles": "/path/to/Vehicles.txt", + "Vehicles/Users": "/path/to/Users.txt" +} +``` diff --git a/doc/site/notes.md b/doc/site/notes.md new file mode 100644 index 00000000..533009ae --- /dev/null +++ b/doc/site/notes.md @@ -0,0 +1,179 @@ +# Notes + +## Khiops API Notes + +### Common Parameters { #core-api-common-params } + +The functions in the [khiops.core.api][] have the following common parameters. + +#### Standard Parameters + +`log_file_path` : str, default `""` +: Path of the log file for the Khiops process (command line option `-e` of the desktop app). If + equal to `""` then it writes no log file. + +`output_scenario_path` : str, default `""` +: Path of the output Khiops scenario file (command line option `-o` of the desktop app). If + the empty string is specified no output scenario file is generated. + +`task_file_path` : str, default `""` +: Path of the task file for the Khiops process (command line option `-p` of the desktop app). If + equal to `""` then it writes no task file. + +`trace` : bool, default `False` +: If `True` prints the command line executed of the process and does not delete any temporary + files created. + +#### Advanced Parameters + +!!! info "Advanced parameters" + The parameters in this section are intended for fine-tuning Khiops process execution and are + seldom needed in everyday usage. + +`stdout_file_path` : str, default `""` +: Path to a file where the Khiops process writes its stdout stream. Normally Khiops should not + write to this stream but MPI, filesystems plugins or debug versions may do it. The stream is + captured with a UTF-8 encoding and replacing encoding errors. If equal to `""` then it writes + no file. + +`stderr_file_path` : str, default `""` +: Path to a file where the Khiops process writes its stderr stream. Normally Khiops should not + write to this stream but MPI, filesystems plugins or debug versions may do it. The stream is + captured with a UTF-8 encoding and replacing encoding errors. If equal to `""` then it writes + no file. + +`max_cores` : int, optional +: Maximum number of cores for Khiops executions. If not set, then Khiops uses all available CPU + cores in the system. + +`memory_limit_mb` : int, optional +: Maximum amount of memory (in MB) for Khiops executions. If not set, then Khiops uses all + available system memory. + +`temp_dir` : str, default `""` +: Temporary directory for Khiops executions. If set to `""`, then Khiops uses the system's + temporary directory. + +`scenario_prologue` : str, default `""` +: Prologue to prepend to all Khiops execution scenarios. + +`force_ansi_scenario` : bool, default `False` +: If `True` the internal scenario generated by Khiops will force characters such as accentuated + ones to be decoded with the UTF8->ANSI khiops transformation. + +### Input Types { #core-api-input-types } + +The types accepted in most methods and classes of [khiops.core][] are flexible: + +- `str` can be replaced by `bytes`. + + - This adds flexibility for file paths and automatically created variable names + (data-dependent). + +- `list` can be replaced by any class implementing the `collections.abc.Sequence` interface except + `str` and `bytes`. +- `dict` can be replaced by any class implementing the `collections.abc.Mapping` interface. + +### Database Sampling { #core-api-sampling-mode } + +Several [khiops.core.api][] functions can operate on dataset *samples* instead of the full +datasets. This sampling behavior is fully customizable by the user: one can specify that the +function operates on the specified sample or on its *complement*. + +The sampling behavior is controlled with two parameters: + +`sample_percentage` : float +: A real number between 0 and 100 specifying the percentage of the data to be used as sample. + +`sampling_mode` : str +: A string specifying the sampling operation mode: + + - `"Include sample"`: The sample consists of `sample_percentage` percent of the individuals in + the dataset. + - `"Exclude sample"`: The sample consists of `100 - sample_percentage` percent of the + individuals in the dataset. The sample is exactly the *complement* of that obtained with + `"Include sample"`. + +In the case of the [train_predictor][khiops.core.api.train_predictor] function the additional +boolean parameter `use_complement_as_test` specifies whether the complement of the selected sample +is used to evaluate the trained predictor. + +#### An Example + +Consider the following [train_predictor][khiops.core.api.train_predictor] parameters: + +| Parameter | Value | +| ------------------------ | ----------------- | +| `sample_percentage` | `20` | +| `sampling_mode` | `"Exclude sample"` | +| `use_complement_as_test` | `True` | + +This specifies a 20-80 split of the dataset. Since `sampling_mode` is `"Exclude sample"` the +predictor will be trained on the 80% part. The remaining 20% will be used to evaluate the +predictor's performance because `use_complement_as_test` is `True`. + +### Samples Directory Customization { #core-api-env-samples-dir } + +!!! tip "Environment variable" + The samples directory usable by the Khiops Python library can be customized via the + `KHIOPS_SAMPLES_DIR` environment variable, which specifies the path to the Khiops sample + datasets directory. + +## Khiops JSON Files + +### Generalities { #json-files-generalities } + +The structure of the Khiops JSON files is self-documented: + +- Most of the information is available as key-value pairs, where the keys resemble the labels used + in Khiops' classic report files (tab-separated plain-text files with extension `.xls`) or + dictionary files. +- In order to be human-readable the files are *beautified* with a comfortable spacing and + indentation. + +### Structure and Performance { #json-files-structure-and-performance } + +!!! warning "Large files" + The Khiops JSON files may be large (tens of MB) when analyzing datasets with many columns, or + when specifying the creation of thousands of variables in the multi-table case. + +To handle these situations, the report attributes in the JSON file are sorted by increasing size, +thus easing the use of streaming parsers. + +Furthermore, memory-scalable parsing techniques can be implemented. For example, the heavier parts +of the file can be separated and split into chunks. Then, these chunks can be indexed using the +information found at the top of the report, allowing the on-demand access to the detailed parts of +the report. + +### Khiops Report Files Structure (.khj) { #json-files-report-structure } + +At the top level the order is as follows: + +1. Modeling report +2. Evaluation report(s) +3. Preparation report(s) + +!!! note + The preparation reports are at the end because they can be very large when many variables are + analyzed. + +Each report field is organized in three sections: + +`Summary` +: General (short) information about the report. + +`List of report items` +: Variable statistics (preparation), trained predictor (modeling) and predictor performance + (evaluation). + + - Each item has a "Rank". + + - Example: The second most informative variable has the categorical rank `"R02"`. + + - Each item is described by a few summary attributes. + +`Dictionary of detailed report items` +: The keys of this dictionary are the previously mentioned "Rank" attributes. Note that: + + - Not all report items are detailed. + - The detailed information may be large (example: data grid). diff --git a/doc/site/samples/samples.md b/doc/site/samples/samples.md new file mode 100644 index 00000000..315a3d0d --- /dev/null +++ b/doc/site/samples/samples.md @@ -0,0 +1,2118 @@ +# Samples core + +The code snippets on this page demonstrate the basic use of the [khiops.core][] module. + +## Script and Jupyter notebook + +The samples in this page are also available as: + +- [Python script](samples.py) +- [Jupyter notebook](samples.ipynb) + +## Setup + +First make sure you have installed the sample datasets. In a configured +conda shell (ex. *Anaconda Prompt* in Windows) execute: + +```shell +kh-download-datasets +``` + +If that doesn't work open a python console and execute: + +```python +from khiops.tools import download_datasets +download_datasets() +``` + +## Samples + +### `get_khiops_version()` + +Shows the Khiops version + +```python +from khiops import core as kh + +print(f"Khiops version: {kh.get_khiops_version()}") +``` +### `build_dictionary_from_data_table()` + +Automatically creates a dictionary file from a data table + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +dictionary_name = "AutoAdult" +dictionary_file_path = os.path.join( + "kh_samples", "build_dictionary_from_data_table", "AutoAdult.kdic" +) + +# Create the dictionary from the data table +kh.build_dictionary_from_data_table( + data_table_path, dictionary_name, dictionary_file_path +) +``` +### `create_dictionary_domain()` + +Creates a dictionary domain from scratch + +This dictionary domain contains a set of dictionaries, +with all possible variable types. + +```python +# Imports +import os +from khiops import core as kh + +# Create a Root dictionary +root_dictionary = kh.Dictionary( + json_data={"name": "dict_from_scratch", "root": True, "key": ["Id"]} +) + +# Start with simple variables to declare +simple_variables = [ + {"name": "Id", "type": "Categorical"}, + {"name": "Num", "type": "Numerical"}, + {"name": "text", "type": "Text"}, + {"name": "hour", "type": "Time"}, + {"name": "date", "type": "Date"}, + {"name": "ambiguous_ts", "type": "Timestamp"}, + {"name": "ts", "type": "TimestampTZ"}, +] +for var_spec in simple_variables: + root_dictionary.add_variable_from_spec(name=var_spec["name"], type=var_spec["type"]) + +# Create a second dictionary +second_dictionary = kh.Dictionary( + json_data={"name": "Service", "key": ["Id", "id_product"]} +) +second_dictionary.add_variable_from_spec(name="Id", type="Categorical") +second_dictionary.add_variable_from_spec(name="id_product", type="Categorical") + +# Create a third dictionary +third_dictionary = kh.Dictionary(json_data={"name": "Address", "key": ["Id"]}) +third_dictionary.add_variable_from_spec(name="StreetNumber", type="Numerical") +third_dictionary.add_variable_from_spec(name="StreetName", type="Categorical") +third_dictionary.add_variable_from_spec(name="id_city", type="Categorical") +# Add a variable with a rule +third_dictionary.add_variable_from_spec( + name="computed", + type="Numerical", + rule=str(kh.Rule("Ceil", kh.Rule("Product", 3, kh.Rule("Random")))), +) + +# Add the variables used in a multi-table context in the first dictionary. +# They link the root dictionary to the additional ones +root_dictionary.add_variable_from_spec( + name="Services", type="Table", object_type="Service" +) +root_dictionary.add_variable_from_spec( + name="Address", type="Entity", object_type="Address" +) + +# Create a DictionaryDomain (set of dictionaries) +dictionary_domain = kh.DictionaryDomain() +dictionary_domain.add_dictionary(root_dictionary) +dictionary_domain.add_dictionary(second_dictionary) +dictionary_domain.add_dictionary(third_dictionary) + +output_dir = os.path.join("kh_samples", "create_dictionary_domain") +dictionary_file_path = os.path.join(output_dir, "dict_from_scratch.kdic") + +# Create the output directory if needed +if not os.path.isdir(output_dir): + os.mkdir(output_dir) + +# Write the dictionary domain to a file +dictionary_domain.export_khiops_dictionary_file(dictionary_file_path) +``` +### `detect_data_table_format()` + +Detects the format of a data table with and without a dictionary file + +The user may provide a dictionary file or dictionary domain object specifying the +table schema. The detection heuristic is more accurate with this information. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +output_dir = os.path.join("kh_samples", "detect_data_table_format") +transformed_data_table_path = os.path.join(output_dir, "AdultWithAnotherFormat.txt") + +# Create the output directory +if not os.path.isdir(output_dir): + os.mkdir(output_dir) + +# Detect the format of the table +format_spec = kh.detect_data_table_format(data_table_path) +print("Format specification (header_line, field_separator)") +print("Format detected on original table:", format_spec) + +# Make a deployment to change the format of the data table +kh.deploy_model( + dictionary_file_path, + "Adult", + data_table_path, + transformed_data_table_path, + output_header_line=False, + output_field_separator=",", +) + +# Detect the new format of the table without a dictionary file +format_spec = kh.detect_data_table_format(transformed_data_table_path) +print("Format detected on reformatted table:", format_spec) + +# Detect the new format of the table with a dictionary file +format_spec = kh.detect_data_table_format( + transformed_data_table_path, + dictionary_file_path_or_domain=dictionary_file_path, + dictionary_name="Adult", +) +print("Format detected (with dictionary file) on reformatted table:", format_spec) +``` +### `check_database()` + +Runs an integrity check of a database + +The results are stored in the specified log file with at most 50 error messages. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +log_file = os.path.join("kh_samples", "check_database", "check_database.log") + +# Check the database +kh.check_database( + dictionary_file_path, + "Adult", + data_table_path, + log_file_path=log_file, + max_messages=50, +) +``` +### `export_dictionary_files()` + +Exports a customized dictionary to ".kdic" and to ".kdicj" (JSON) + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +output_dir = os.path.join("kh_samples", "export_dictionary_files") +output_dictionary_file_path = os.path.join(output_dir, "ModifiedAdult.kdic") +output_dictionary_json_path = os.path.join(output_dir, "ModifiedAdult.kdicj") +alt_output_dictionary_json_path = os.path.join(output_dir, "AltModifiedAdult.kdicj") + +# Load the dictionary domain from initial dictionary file +# Then obtain the "Adult" dictionary within +domain = kh.read_dictionary_file(dictionary_file_path) +dictionary = domain.get_dictionary("Adult") + +# Set some of its variables to unused +fnlwgt_variable = dictionary.get_variable("fnlwgt") +fnlwgt_variable.used = False +label_variable = dictionary.get_variable("Label") +label_variable.used = False + +# Create output directory if necessary +if not os.path.exists("kh_samples"): + os.mkdir("kh_samples") + os.mkdir(output_dir) +else: + if not os.path.exists(output_dir): + os.mkdir(output_dir) + +# Export to kdic +domain.export_khiops_dictionary_file(output_dictionary_file_path) + +# Export to kdicj either from the domain or from a kdic file +# Requires a Khiops execution, that's why it is not a method of DictionaryDomain +kh.export_dictionary_as_json(domain, output_dictionary_json_path) +kh.export_dictionary_as_json( + output_dictionary_file_path, alt_output_dictionary_json_path +) +``` +### `train_predictor()` + +Trains a predictor with a minimal setup + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +analysis_report_file_path = os.path.join( + "kh_samples", "train_predictor", "AnalysisReport.khj" +) + +# Train the predictor +kh.train_predictor( + dictionary_file_path, + "Adult", + data_table_path, + "class", + analysis_report_file_path, + max_trees=0, +) +``` +### `train_predictor_file_paths()` + +Trains a predictor and stores the return value of the function + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +report_file_path = os.path.join( + "kh_samples", "train_predictor_file_paths", "AnalysisResults.khj" +) + +# Train the predictor +_, modeling_dictionary_file_path = kh.train_predictor( + dictionary_file_path, + "Adult", + data_table_path, + "class", + report_file_path, + max_trees=0, +) +print("Reports file available at " + report_file_path) +print("Modeling dictionary file available at " + modeling_dictionary_file_path) + +# If you have Khiops Visualization installed you may open the report as follows +# kh.visualize_report(report_file_path) +``` +### `train_predictor_text()` + +Trains a predictor with just text-specific parameters + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join( + kh.get_samples_dir(), "NegativeAirlineTweets", "NegativeAirlineTweets.kdic" +) +data_table_path = os.path.join( + kh.get_samples_dir(), "NegativeAirlineTweets", "NegativeAirlineTweets.txt" +) +report_file_path = os.path.join( + "kh_samples", "train_predictor_text", "AnalysisResults.khj" +) + +# Train the predictor +kh.train_predictor( + dictionary_file_path, + "NegativeAirlineTweets", + data_table_path, + "negativereason", + report_file_path, + max_trees=5, + max_text_features=1000, + text_features="words", +) +``` +### `train_predictor_error_handling()` + +Shows how to handle errors when training a predictor + +Trains the predictor and handles the errors by printing a custom message. When the +Khiops application fails the Khiops Python library will raise a +KhiopsRuntimeError reporting the errors encountered by Khiops. + +If the latter information is not enough to diagnose the problem, it is +possible to save the temporary log file by activating the "trace" flag in +the call to [train_predictor][khiops.core.api.train_predictor]. +The path of the log file will be printed to the standard output, as well as +that of the dictionary and scenario files (note that the "trace" keyword +argument is available in all functions of the [khiops.core.api][] +submodule). + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths with a nonexistent dictionary file +dictionary_file_path = "NONEXISTENT_DICTIONARY_FILE.kdic" +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +output_dir = os.path.join("kh_samples", "train_predictor_error_handling") +report_file_path = os.path.join(output_dir, "AnalysisResults.khj") +log_file_path = os.path.join(output_dir, "khiops.log") +scenario_path = os.path.join(output_dir, "scenario._kh") + +# Train the predictor and handle the error +try: + kh.train_predictor( + dictionary_file_path, + "Adult", + data_table_path, + "class", + report_file_path, + trace=True, + log_file_path=log_file_path, + output_scenario_path=scenario_path, + ) +except kh.KhiopsRuntimeError as error: + print("Khiops training failed! Below the KhiopsRuntimeError message:") + print(error) + +print("\nFull log contents:") +print("------------------") +with open(log_file_path) as log_file: + for line in log_file: + print(line, end="") + +print("\nExecuted scenario") +print("-----------------") +with open(scenario_path) as scenario_file: + for line in scenario_file: + print(line, end="") +``` +### `train_predictor_mt()` + +Trains a multi-table predictor in the simplest way possible + +It is a call to [train_predictor][khiops.core.api.train_predictor] with +additional parameters to handle multi-table learning. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +accidents_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") +dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") +accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") +vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") +report_file_path = os.path.join( + "kh_samples", "train_predictor_mt", "AnalysisResults.khj" +) + +# Train the predictor. Besides the mandatory parameters, we specify: +# - A python dictionary linking data paths to file paths for non-root tables +# - To not construct any decision tree +# The default number of automatic features is 100 +kh.train_predictor( + dictionary_file_path, + "Accident", + accidents_table_path, + "Gravity", + report_file_path, + additional_data_tables={"Vehicles": vehicles_table_path}, + max_trees=0, +) +``` +### `train_predictor_mt_with_specific_rules()` + +Trains a multi-table predictor with specific construction rules + +It is the same as [train_predictor_mt][] but with the specification of the allowed +variable construction rules. The list of available rules is found in the field +`kh.ALL_CONSTRUCTION_RULES`. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +accidents_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") +dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") +accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") +vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") +report_file_path = os.path.join( + "kh_samples", + "train_predictor_mt_with_specific_rules", + "AnalysisResults.khj", +) + +# Train the predictor. Besides the mandatory parameters, it is specified: +# - A python dictionary linking data paths to file paths for non-root tables +# - The maximum number of aggregate variables to construct (1000) +# - The construction rules allowed to automatically create aggregates +# - To not construct any decision tree +kh.train_predictor( + dictionary_file_path, + "Accident", + accidents_table_path, + "Gravity", + report_file_path, + additional_data_tables={"Vehicles": vehicles_table_path}, + max_constructed_variables=1000, + construction_rules=["TableMode", "TableSelection"], + max_trees=0, +) +``` +### `train_predictor_mt_snowflake()` + +Trains a multi-table predictor for a dataset with a snowflake schema + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +accidents_dir = os.path.join(kh.get_samples_dir(), "Accidents") +dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") +accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") +vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") +users_table_path = os.path.join(accidents_dir, "Users.txt") +places_table_path = os.path.join(accidents_dir, "Places.txt") +report_file_path = os.path.join( + "kh_samples", "train_predictor_mt_snowflake", "AnalysisResults.khj" +) + +# Train the predictor. Besides the mandatory parameters, we specify: +# - A python dictionary linking data paths to file paths for non-root tables +# - To not construct any decision tree +# The default number of automatic features is 100 +kh.train_predictor( + dictionary_file_path, + "Accident", + accidents_table_path, + "Gravity", + report_file_path, + additional_data_tables={ + "Vehicles": vehicles_table_path, + "Vehicles/Users": users_table_path, + "Place": places_table_path, + }, + max_trees=0, +) +``` +### `train_predictor_with_train_percentage()` + +Trains a predictor with a 90%-10% train-test split + +Note: The default is a 70%-30% split + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +report_file_path = os.path.join( + "kh_samples", + "train_predictor_with_train_percentage", + "P90_AnalysisResults.khj", +) + +# Train the predictor. Besides the mandatory parameters, it is specified: +# - A 90% sampling rate for the training dataset +# - Set the test dataset as the complement of the training dataset (10%) +# - No trees +kh.train_predictor( + dictionary_file_path, + "Adult", + data_table_path, + "class", + report_file_path, + sample_percentage=90, + use_complement_as_test=True, + max_trees=0, +) +``` +### `train_predictor_with_trees()` + +Trains a predictor based on 15 trees with a 80%-20% train-test split + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Letter", "Letter.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Letter", "Letter.txt") +report_file_path = os.path.join( + "kh_samples", "train_predictor_with_trees", "P80_AnalysisResults.khj" +) + +# Train the predictor with at most 15 trees (default 10) +kh.train_predictor( + dictionary_file_path, + "Letter", + data_table_path, + "lettr", + report_file_path, + sample_percentage=80, + use_complement_as_test=True, + max_trees=15, +) +``` +### `train_predictor_with_pairs()` + +Trains a predictor with user specified pairs of variables + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +report_file_path = os.path.join( + "kh_samples", "train_predictor_with_pairs", "AnalysisResults.khj" +) + +# Train the predictor with at most 10 pairs as follows: +# - Include pairs age-race and capital_gain-capital_loss +# - Include all possible pairs having relationship as component +kh.train_predictor( + dictionary_file_path, + "Adult", + data_table_path, + "class", + report_file_path, + use_complement_as_test=True, + max_trees=0, + max_pairs=10, + specific_pairs=[ + ("age", "race"), + ("capital_gain", "capital_loss"), + ("relationship", ""), + ], +) +``` +### `train_predictor_with_multiple_parameters()` + +Trains a predictor with various additional parameters + +Some of these parameters are specific to [khiops.core.api.train_predictor][] +and others generic to any Khiops execution. + +In this example, we specify the following parameters in the call: + - A main target value + - The path where to store the "Khiops scenario" script + - The path where to store the log of the process + - The flag to show the execution trace (generic to any [khiops.core.api][] + function) + +Additionally the Khiops runner is set such that the learning is executed with only +1000 MB of memory. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +output_dir = os.path.join("kh_samples", "train_predictor_with_multiple_parameters") +report_file_path = os.path.join(output_dir, "AnalysisResults.khj") +output_script_path = os.path.join(output_dir, "output_scenario._kh") +log_path = os.path.join(output_dir, "log.txt") + +# Train the predictor. Besides the mandatory parameters, we specify: +# - The value "more" as main target value +# - The output Khiops script file location (generic) +# - The log file location (generic) +# - The maximum memory used, set to 1000 MB +# - To show the debug trace (generic) +kh.train_predictor( + dictionary_file_path, + "Adult", + data_table_path, + "class", + report_file_path, + main_target_value="more", + output_scenario_path=output_script_path, + log_file_path=log_path, + memory_limit_mb=1000, + trace=True, +) +``` +### `train_predictor_detect_format()` + +Trains a predictor without specifying the table format + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Iris", "Iris.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Iris", "Iris.txt") +output_dir = os.path.join("kh_samples", "train_predictor_detect_format") +transformed_data_table_path = os.path.join(output_dir, "TransformedIris.txt") +report_file_path = os.path.join(output_dir, "AnalysisResults.khj") + +# Transform the database format from header_line=True and field_separator=TAB +# to header_line=False and field_separator="," +# See the deploy_model examples below for more details +kh.deploy_model( + dictionary_file_path, + "Iris", + data_table_path, + transformed_data_table_path, + output_header_line=False, + output_field_separator=",", +) + +# Try to learn with the old format +try: + kh.train_predictor( + dictionary_file_path, + "Iris", + transformed_data_table_path, + "Class", + report_file_path, + header_line=True, + field_separator="", + ) +except kh.KhiopsRuntimeError as error: + print( + "This failed because of a bad data table format spec. " + + "Below the KhiopsRuntimeError message" + ) + print(error) + +# Train without specifyng the format (detect_format is True by default) +kh.train_predictor( + dictionary_file_path, + "Iris", + transformed_data_table_path, + "Class", + report_file_path, +) +``` +### `train_predictor_with_cross_validation()` + +Trains a predictor with a 5-fold cross-validation + +```python +# Imports +import math +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +output_dir = os.path.join("kh_samples", "train_predictor_with_cross_validation") +fold_dictionary_file_path = os.path.join(output_dir, "AdultWithFolding.kdic") + +# Create the output directory +if not os.path.isdir(output_dir): + os.mkdir(output_dir) + +# Load the learning dictionary object +domain = kh.read_dictionary_file(dictionary_file_path) +dictionary = domain.get_dictionary("Adult") + +# Add a random fold index variable to the learning dictionary +fold_number = 5 +dictionary.add_variable_from_spec(name="FoldIndex", type="Numerical", used=False) + +# Create fold indexing rule and set it on `fold_index_variable` +fold_index_variable = dictionary.get_variable("FoldIndex") +fold_index_variable.rule = str( + kh.Rule("Ceil", kh.Rule("Product", fold_number, kh.Rule("Random"))), +) + +# Add variables that indicate if the instance is in the train dataset: +for fold_index in range(1, fold_number + 1): + name = "IsInTrainDataset" + str(fold_index) + dictionary.add_variable_from_spec(name=name, type="Numerical", used=False) + dictionary.get_variable(name).rule = str( + kh.Rule("NEQ", fold_index_variable, fold_index), + ) + +# Print dictionary with fold variables +print("Dictionary file with fold variables") +domain.export_khiops_dictionary_file(fold_dictionary_file_path) +with open(fold_dictionary_file_path) as fold_dictionary_file: + for line in fold_dictionary_file: + print(line, end="") + +# For each fold k: +print("Training Adult with " + str(fold_number) + " folds") +print("\tfold\ttrain auc\ttest auc") +train_aucs = [] +test_aucs = [] +for fold_index in range(1, fold_number + 1): + analysis_report_file_path = os.path.join( + output_dir, "Fold" + str(fold_index) + "AnalysisResults.khj" + ) + # Train a model from the sub-dataset where IsInTrainDataset is 1 + _, modeling_dictionary_file_path = kh.train_predictor( + domain, + "Adult", + data_table_path, + "class", + analysis_report_file_path, + sample_percentage=100, + selection_variable="IsInTrainDataset" + str(fold_index), + selection_value=1, + max_trees=0, + ) + + evaluation_report_file_path = os.path.join( + output_dir, "Fold" + str(fold_index) + "AdultEvaluationResults.khj" + ) + # Evaluate the resulting model in the subsets where IsInTrainDataset is 0 + test_evaluation_report_path = kh.evaluate_predictor( + modeling_dictionary_file_path, + "SNB_Adult", + data_table_path, + evaluation_report_file_path, + sample_percentage=100, + selection_variable="IsInTrainDataset" + str(fold_index), + selection_value=0, + ) + + # Obtain the train AUC from the train report and the test AUC from the + # evaluation report and print them + train_results = kh.read_analysis_results_file(analysis_report_file_path) + test_evaluation_results = kh.read_analysis_results_file(test_evaluation_report_path) + train_auc = train_results.train_evaluation_report.get_snb_performance().auc + test_auc = test_evaluation_results.evaluation_report.get_snb_performance().auc + print("\t" + str(fold_index) + "\t" + str(train_auc) + "\t" + str(test_auc)) + + # Store the train and test AUCs in arrays + train_aucs.append(train_auc) + test_aucs.append(test_auc) + +# Print the mean +- error aucs for both train and test +mean_train_auc = sum(train_aucs) / fold_number +squared_error_train_aucs = [(auc - mean_train_auc) ** 2 for auc in train_aucs] +sd_train_auc = math.sqrt(sum(squared_error_train_aucs) / (fold_number - 1)) + +mean_test_auc = sum(test_aucs) / fold_number +squared_error_test_aucs = [(auc - mean_test_auc) ** 2 for auc in test_aucs] +sd_test_auc = math.sqrt(sum(squared_error_test_aucs) / (fold_number - 1)) + +print("final auc") +print("train auc: " + str(mean_train_auc) + " +- " + str(sd_train_auc)) +print("test auc: " + str(mean_test_auc) + " +- " + str(sd_test_auc)) +``` +### `interpret_predictor()` + +Builds interpretation model for existing predictor + +It calls [khiops.core.api.train_predictor][] and +[khiops.core.api.interpret_predictor][] only with their mandatory parameters. + +```python +# Imports +import os +from khiops import core as kh + +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +output_dir = os.path.join("kh_samples", "interpret_predictor") +analysis_report_file_path = os.path.join(output_dir, "AnalysisResults.khj") +interpretor_file_path = os.path.join(output_dir, "InterpretationModel.kdic") + +# Build prediction model +_, predictor_file_path = kh.train_predictor( + dictionary_file_path, + "Adult", + data_table_path, + "class", + analysis_report_file_path, +) + +# Build interpretation model +kh.interpret_predictor(predictor_file_path, "SNB_Adult", interpretor_file_path) + +print(f"The interpretation model is '{interpretor_file_path}'") +``` +### `reinforce_predictor()` + +Builds reinforced predictor for existing predictor + +The reinforced predictor produces the following reinforcement variables for the +specified target value to reinforce (i.e. whose probability of occurrence is +tentatively increased): + +- initial score, containing the conditional probability of the target value before + reinforcement +- four variables are output in decreasing reinforcement value: name of the lever + variable, reinforcement part, final score after reinforcement, and class change + tag. + +It calls [khiops.core.api.train_predictor][] and +[khiops.core.api.reinforce_predictor][] only with their mandatory parameters. + +```python +# Imports +import os +from khiops import core as kh + +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +output_dir = os.path.join("kh_samples", "reinforce_predictor") +analysis_report_file_path = os.path.join(output_dir, "AnalysisResults.khj") +reinforced_predictor_file_path = os.path.join(output_dir, "ReinforcedAdultModel.kdic") + +# Build prediction model +_, predictor_file_path = kh.train_predictor( + dictionary_file_path, + "Adult", + data_table_path, + "class", + analysis_report_file_path, +) + +# Build reinforced predictor +kh.reinforce_predictor( + predictor_file_path, + "SNB_Adult", + reinforced_predictor_file_path, + reinforcement_lever_variables=["occupation"], +) + +print(f"The reinforced predictor is '{reinforced_predictor_file_path}'") +``` +### `multiple_train_predictor()` + +Trains a sequence of models with a decreasing number of variables + +This example illustrates the use of the khiops classes +[DictionaryDomain][khiops.core.dictionary.DictionaryDomain] (for reading +dictionary files) and +[AnalysisResults][khiops.core.analysis_results.AnalysisResults] (for reading +training/evaluation results from JSON). + +```python +# Imports +import os +from khiops import core as kh + + +def display_test_results(json_result_file_path): + """Display some of the training results""" + results = kh.read_analysis_results_file(json_result_file_path) + train_performance = results.train_evaluation_report.get_snb_performance() + test_performance = results.test_evaluation_report.get_snb_performance() + print( + "\t" + + str(len(results.preparation_report.variables_statistics)) + + "\t" + + str(train_performance.auc) + + "\t" + + str(test_performance.auc) + ) + + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +output_dir = os.path.join("kh_samples", "multiple_train_predictor") +report_file_path = os.path.join(output_dir, "AnalysisResults.khj") + +# Read the dictionary file to obtain an instance of class Dictionary +dictionary_domain = kh.read_dictionary_file(dictionary_file_path) +dictionary = dictionary_domain.get_dictionary("Adult") + +# Train a SNB model using all the variables +print("\t#vars\ttrain auc\ttest auc") +kh.train_predictor( + dictionary_file_path, + "Adult", + data_table_path, + "class", + report_file_path, + sample_percentage=70, + use_complement_as_test=True, + max_trees=0, +) +display_test_results(report_file_path) + +# Read results to obtain the variables sorted by decreasing Level +analysis_results = kh.read_analysis_results_file(report_file_path) +preparation_results = analysis_results.preparation_report + +# Train a sequence of models with a decreasing number of variables +# We disable variables one-by-one in increasing level (predictive power) order +variable_number = len(preparation_results.variables_statistics) +for i in reversed(range(variable_number)): + # Search the next variable + variable = preparation_results.variables_statistics[i] + + # Disable this variable and save the dictionary with the Khiops format + dictionary.get_variable(variable.name).used = False + + # Train the model with this dictionary domain object + report_file_path = os.path.join( + output_dir, f"V{variable_number - 1 - i}_AnalysisResults.khj" + ) + kh.train_predictor( + dictionary_domain, + "Adult", + data_table_path, + "class", + report_file_path, + sample_percentage=70, + use_complement_as_test=True, + max_trees=0, + ) + + # Show a preview of the results + display_test_results(report_file_path) +``` +### `evaluate_predictor()` + +Evaluates a predictor in the simplest way possible + +It calls [khiops.core.api.evaluate_predictor][] with only its mandatory parameters. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +output_dir = os.path.join("kh_samples", "evaluate_predictor") +analysis_report_file_path = os.path.join(output_dir, "AnalysisResults.khj") + +# Train the predictor +_, model_dictionary_file_path = kh.train_predictor( + dictionary_file_path, + "Adult", + data_table_path, + "class", + analysis_report_file_path, + max_trees=0, +) + +evaluation_report_file_path = os.path.join(output_dir, "AdultEvaluationResults.khj") + +# Evaluate the predictor +kh.evaluate_predictor( + model_dictionary_file_path, + "SNB_Adult", + data_table_path, + evaluation_report_file_path, +) +print("Evaluation report available at " + evaluation_report_file_path) +``` +### `access_predictor_evaluation_report()` + +Shows the performance metrics of a predictor + +See [evaluate_predictor][] or [train_predictor_with_train_percentage][] to see +examples +on how to evaluate a model. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +report_file_path = os.path.join( + "kh_samples", "access_predictor_evaluation_report", "AdultAnalysisReport.khj" +) + +# Train the SNB predictor and some univariate predictors +# Note: Evaluation in test is 30% by default +kh.train_predictor( + dictionary_file_path, + "Adult", + data_table_path, + "class", + report_file_path, + max_trees=0, +) + +# Obtain the evaluation results +results = kh.read_analysis_results_file(report_file_path) +evaluation_report = results.test_evaluation_report +snb_performance = evaluation_report.get_snb_performance() + +# Print univariate metrics for the SNB +print("\nperformance metrics for " + snb_performance.name) +for metric_name in snb_performance.get_metric_names(): + print(metric_name + ": " + str(snb_performance.get_metric(metric_name))) + +# Print the confusion matrix +print("\nconfusion matrix:") +confusion_matrix = snb_performance.confusion_matrix + +for target_value in confusion_matrix.values: + print("\t" + target_value, end="") +print("") + +for i, target_value in enumerate(confusion_matrix.values): + observed_frequencies = confusion_matrix.matrix[i] + print(target_value, end="") + for frequency in observed_frequencies: + print("\t" + str(frequency), end="") + print("") + +# Print the head of the lift curves for the 'more' modality +print("\nfirst five values of the lift curves for 'more'") + +snb_lift_curve = evaluation_report.get_snb_lift_curve("more") +optimal_lift_curve = evaluation_report.get_classifier_lift_curve("Optimal", "more") +random_lift_curve = evaluation_report.get_classifier_lift_curve("Random", "more") + +for i in range(5): + print( + str(snb_lift_curve.values[i]) + + "\t" + + str(optimal_lift_curve.values[i]) + + "\t" + + str(random_lift_curve.values[i]) + ) + +# Print metrics for an SNB predictor +predictor_performance = evaluation_report.get_predictor_performance( + "Selective Naive Bayes" +) +print("\n\nperformance metrics for " + predictor_performance.name) +for metric_name in predictor_performance.get_metric_names(): + print(metric_name + ": " + str(predictor_performance.get_metric(metric_name))) +``` +### `train_recoder()` + +Train a database recoder in the simplest way possible + +It is a call to [khiops.core.api.train_recoder][] with only its mandatory +parameters. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +report_file_path = os.path.join("kh_samples", "train_recoder", "AnalysisResults.khj") + +# Train the recoder model +kh.train_recoder( + dictionary_file_path, "Adult", data_table_path, "class", report_file_path +) +``` +### `train_recoder_with_multiple_parameters()` + +Trains a recoder that transforms variable values to their respective part labels + +It also creates 10 pair features. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +report_file_path = os.path.join( + "kh_samples", + "train_recoder_with_multiple_parameters", + "AnalysisResults.khj", +) + +# Train the recoder model +kh.train_recoder( + dictionary_file_path, + "Adult", + data_table_path, + "class", + report_file_path, + max_pairs=10, + categorical_recoding_method="part label", + numerical_recoding_method="part label", +) +``` +### `train_recoder_mt_flatten()` + +Trains a recoder that flattens a multi-table database into a single table + +The constructed variables are all kept and no recoding is performed on their values + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +accidents_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") +dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") +accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") +vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") +report_file_path = os.path.join( + "kh_samples", "train_recoder_mt_flatten", "AnalysisResults.khj" +) + +# Train the recoder. Besides the mandatory parameters, it is specified: +# - A python dictionary linking data paths to file paths for non-root tables +# - The maximum number of aggregate variables to construct (1000) +# - To keep all the created variables independently of their informativeness (level) +# - To not recode the variables values +kh.train_recoder( + dictionary_file_path, + "Accident", + accidents_table_path, + "Gravity", + report_file_path, + additional_data_tables={"Vehicles": vehicles_table_path}, + max_constructed_variables=1000, + informative_variables_only=False, + categorical_recoding_method="none", + numerical_recoding_method="none", + keep_initial_categorical_variables=True, + keep_initial_numerical_variables=True, +) +``` +### `deploy_model()` + +Deploys a model in the simplest way possible + +It is a call to [khiops.core.api.deploy_model][] with its mandatory parameters. + +In this example, a Selective Naive Bayes (SNB) model is deployed by applying its +associated dictionary to the input database. The model predictions are written to +the output database. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +output_dir = os.path.join("kh_samples", "deploy_model") +report_file_path = os.path.join(output_dir, "AnalysisResults.khj") +output_data_table_path = os.path.join(output_dir, "ScoresAdult.txt") + +# Train the predictor +_, model_dictionary_file_path = kh.train_predictor( + dictionary_file_path, + "Adult", + data_table_path, + "class", + report_file_path, + max_trees=0, +) + +# Deploy the model on the database +# It will score it according to the trained predictor +kh.deploy_model( + model_dictionary_file_path, "SNB_Adult", data_table_path, output_data_table_path +) +``` +### `deploy_model_text()` + +Deploys a model learned on textual data +It is a call to [khiops.core.api.deploy_model][] with its mandatory parameters, plus +text-specific parameters. + +In this example, a Selective Naive Bayes (SNB) model is deployed by applying its +associated dictionary to the input database. The model predictions are written to +the output database. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join( + kh.get_samples_dir(), "NegativeAirlineTweets", "NegativeAirlineTweets.kdic" +) +data_table_path = os.path.join( + kh.get_samples_dir(), "NegativeAirlineTweets", "NegativeAirlineTweets.txt" +) +output_dir = os.path.join("kh_samples", "deploy_model_text") +report_file_path = os.path.join(output_dir, "AnalysisResults.khj") +output_data_table_path = os.path.join(output_dir, "ScoresNegativeAirlineTweets.txt") + +# Train the predictor +_, model_dictionary_file_path = kh.train_predictor( + dictionary_file_path, + "NegativeAirlineTweets", + data_table_path, + "negativereason", + report_file_path, + max_trees=5, + max_text_features=1000, + text_features="words", +) + +# Deploy the model on the database +# It will score it according to the trained predictor +kh.deploy_model( + model_dictionary_file_path, + "SNB_NegativeAirlineTweets", + data_table_path, + output_data_table_path, +) +``` +### `deploy_model_mt()` + +Deploys a multi-table classifier in the simplest way possible + +It is a call to [khiops.core.api.deploy_model][] with additional parameters +to handle multi-table deployment. + +In this example, a Selective Naive Bayes (SNB) model is deployed by applying its +associated dictionary to the input database. The model predictions are written to +the output database. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +accidents_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") +dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") +accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") +vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") +output_dir = os.path.join("kh_samples", "deploy_model_mt") +report_file_path = os.path.join(output_dir, "AnalysisResults.khj") +output_data_table_path = os.path.join(output_dir, "TransferredAccidents.txt") + +# Train the predictor (see train_predictor_mt for details) +_, model_dictionary_file_path = kh.train_predictor( + dictionary_file_path, + "Accident", + accidents_table_path, + "Gravity", + report_file_path, + additional_data_tables={"Vehicles": vehicles_table_path}, + max_trees=0, +) + +# Deploy the model on the database +# Besides the mandatory parameters, it is specified: +# - A python dictionary linking data paths to file paths for non-root tables +kh.deploy_model( + model_dictionary_file_path, + "SNB_Accident", + accidents_table_path, + output_data_table_path, + additional_data_tables={"Vehicles": vehicles_table_path}, +) +``` +### `deploy_model_mt_with_interpretation()` + +Deploys a multi-table interpretor in the simplest way possible + +It is a call to [khiops.core.api.deploy_model][] with additional parameters +related to the variable importances. + +In this example, a Selective Naive Bayes (SNB) interpretation model is +deployed by applying its associated dictionary to the input database. +The model variable importances are written to the output data table. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +accidents_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") +dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") +accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") +vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") +output_dir = os.path.join("kh_samples", "deploy_model_mt_with_interpretation") +report_file_path = os.path.join(output_dir, "AnalysisResults.khj") +interpretor_file_path = os.path.join(output_dir, "InterpretationModel.kdic") +output_data_table_path = os.path.join(output_dir, "InterpretedAccidents.txt") + +# Train the predictor (see train_predictor_mt for details) +# Add max_evaluated_variables so that an interpretation model can be built +# (see https://github.com/KhiopsML/khiops/issues/577) +_, model_dictionary_file_path = kh.train_predictor( + dictionary_file_path, + "Accident", + accidents_table_path, + "Gravity", + report_file_path, + additional_data_tables={"Vehicles": vehicles_table_path}, + max_trees=0, + max_evaluated_variables=10, +) + +# Interpret the predictor +kh.interpret_predictor( + model_dictionary_file_path, + "SNB_Accident", + interpretor_file_path, + max_variable_importances=3, + importance_ranking="Individual", +) + +# Deploy the interpretation model on the database +# Besides the mandatory parameters, it is specified: +# - A python dictionary linking data paths to file paths for non-root tables +kh.deploy_model( + interpretor_file_path, + "Interpretation_SNB_Accident", + accidents_table_path, + output_data_table_path, + additional_data_tables={"Vehicles": vehicles_table_path}, +) +``` +### `deploy_reinforced_model_mt()` + +Deploys a multi-table reinforced model in the simplest way possible + +It is a call to [khiops.core.api.deploy_model][] with additional parameters +related to the lever variables. + +In this example, a reinforced Selective Naive Bayes (SNB) model is +deployed by applying its associated dictionary to the input database. +The reinforced model predictions are written to the output data table. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +accidents_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") +dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") +accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") +vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") +output_dir = os.path.join("kh_samples", "deploy_reinforced_model_mt") +report_file_path = os.path.join(output_dir, "AnalysisResults.khj") +reinforced_predictor_file_path = os.path.join(output_dir, "ReinforcedModel.kdic") +output_data_table_path = os.path.join(output_dir, "ReinforcedAccidents.txt") + +# Train the predictor (see train_predictor_mt for details) +_, model_dictionary_file_path = kh.train_predictor( + dictionary_file_path, + "Accident", + accidents_table_path, + "Gravity", + report_file_path, + additional_data_tables={"Vehicles": vehicles_table_path}, + max_trees=0, +) + +# Reinforce the predictor +kh.reinforce_predictor( + model_dictionary_file_path, + "SNB_Accident", + reinforced_predictor_file_path, + reinforcement_target_value="NonLethal", + reinforcement_lever_variables=["InAgglomeration", "CollisionType"], +) + +# Deploy the reinforced model on the database +# Besides the mandatory parameters, it is specified: +# - A python dictionary linking data paths to file paths for non-root tables +kh.deploy_model( + reinforced_predictor_file_path, + "Reinforcement_SNB_Accident", + accidents_table_path, + output_data_table_path, + additional_data_tables={"Vehicles": vehicles_table_path}, +) +``` +### `deploy_model_mt_snowflake()` + +Deploys a classifier model on a dataset with a snowflake schema + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +accidents_dir = os.path.join(kh.get_samples_dir(), "Accidents") +dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") +accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") +vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") +users_table_path = os.path.join(accidents_dir, "Users.txt") +places_table_path = os.path.join(accidents_dir, "Places.txt") +output_dir = os.path.join("kh_samples", "deploy_model_mt_snowflake") +report_file_path = os.path.join(output_dir, "AnalysisResults.khj") +output_data_table_path = os.path.join(output_dir, "TransferredAccidents.txt") + +# Train the predictor. Besides the mandatory parameters, we specify: +# - A python dictionary linking data paths to file paths for non-root tables +# - To not construct any decision tree +# The default number of automatic features is 100 +_, model_dictionary_file_path = kh.train_predictor( + dictionary_file_path, + "Accident", + accidents_table_path, + "Gravity", + report_file_path, + additional_data_tables={ + "Vehicles": vehicles_table_path, + "Vehicles/Users": users_table_path, + "Place": places_table_path, + }, + max_trees=0, +) + +# Deploy the model on the database +# Besides the mandatory parameters, it is specified: +# - A python dictionary linking data paths to file paths for non-root tables +kh.deploy_model( + model_dictionary_file_path, + "SNB_Accident", + accidents_table_path, + output_data_table_path, + additional_data_tables={ + "Vehicles": vehicles_table_path, + "Vehicles/Users": users_table_path, + "Place": places_table_path, + }, +) +``` +### `deploy_model_expert()` + +Deploys a model with a specification of additional variables to be included + +In this example, a Selective Naive Bayes (SNB) model is deployed by applying its +associated dictionary to the input database. Specifically, the output file contains: + +- The model predictions +- The probabilities of all modalities of the target variable. + +The "expert" part of this example is the use of the khiops dictionary interface +and the [DictionaryDomain][khiops.core.dictionary.DictionaryDomain] class + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +output_dir = os.path.join("kh_samples", "deploy_model_expert") +report_file_path = os.path.join(output_dir, "AnalysisResults.khj") +output_data_table_path = os.path.join(output_dir, "ScoresAdult.txt") + +# Train the predictor +_, model_dictionary_file_path = kh.train_predictor( + dictionary_file_path, + "Adult", + data_table_path, + "class", + report_file_path, + max_trees=0, +) + +# Read the dictionary file to obtain an instance of class Dictionary +model_domain = kh.read_dictionary_file(model_dictionary_file_path) +snb_dictionary = model_domain.get_dictionary("SNB_Adult") + +# Select Label (identifier) +snb_dictionary.get_variable("Label").used = True + +# Select the variables containing the probabilities for each class +for variable in snb_dictionary.variables: + # The variable must have a meta data with key that start with "target_prob" + for key in variable.meta_data.keys: + if key.startswith("TargetProb"): + variable.used = True + +# Deploy the model. Besides the mandatory parameters, it is specified: +# - A DictionaryDomain object to use instead of the mandatory dictionary file +kh.deploy_model(model_domain, "SNB_Adult", data_table_path, output_data_table_path) +``` +### `deploy_classifier_for_metrics()` + +Constructs a small precision-recall curve for a classifier + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +output_dir = os.path.join("kh_samples", "deploy_classifier_for_metrics") +report_file_path = os.path.join(output_dir, "AnalysisResults.khj") +output_data_table_path = os.path.join(output_dir, "ScoresAdult.txt") + +# Train the classifier for the target "class" +_, modeling_dictionary_file_path = kh.train_predictor( + dictionary_file_path, + "Adult", + data_table_path, + "class", + report_file_path, + max_trees=0, +) +# Obtain the scores of the SNB on the test dataset to calculate the PR curve +kh.deploy_predictor_for_metrics( + modeling_dictionary_file_path, + "SNB_Adult", + data_table_path, + output_data_table_path, + sampling_mode="Exclude sample", + output_header_line=False, +) + +# We estimate the precision/recall for the class "more" and increasing thresholds +# Note: Normally one would do this with a package (eg. sklearn.metrics) +thresholds = [0.1, 0.3, 0.5, 0.7, 0.9] +true_positives = {thres: 0 for thres in thresholds} +false_positives = {thres: 0 for thres in thresholds} +false_negatives = {thres: 0 for thres in thresholds} +with open(output_data_table_path) as output_data_table: + for line in output_data_table: + fields = line.split("\t") + true_target = fields[0] + proba_more = float(fields[3]) + for thres in thresholds: + if true_target == "more" and proba_more >= thres: + true_positives[thres] += 1 + elif true_target == "more" and proba_more < thres: + false_negatives[thres] += 1 + elif true_target == "less" and proba_more >= thres: + false_positives[thres] += 1 + +precision = { + thres: true_positives[thres] / (true_positives[thres] + false_positives[thres]) + for thres in thresholds +} +recall = { + thres: true_positives[thres] / (true_positives[thres] + false_negatives[thres]) + for thres in thresholds +} + +# Print the curve at the selected points +print("Precision and Recall for class 'more'") +print("threshold\trecall\tprecision") +thresholds.reverse() +for thres in thresholds: + print(str(thres) + "\t" + str(recall[thres]) + "\t" + str(precision[thres])) +``` +### `deploy_regressor_for_metrics()` + +Estimates the R2 coefficient of a regressor + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +output_dir = os.path.join("kh_samples", "deploy_regressor_for_metrics") +report_file_path = os.path.join(output_dir, "AnalysisResults.khj") +output_data_table_path = os.path.join(output_dir, "TrueAndPredictedAges.txt") + +# Train the regressor for the target "age" (with 20% train to be quick) +_, modeling_dictionary_file_path = kh.train_predictor( + dictionary_file_path, + "Adult", + data_table_path, + "age", + report_file_path, + sample_percentage=20, + max_trees=0, +) + +# Obtain the predicted regression values of the SNB on the test dataset estimate R2 +kh.deploy_predictor_for_metrics( + modeling_dictionary_file_path, + "SNB_Adult", + data_table_path, + output_data_table_path, + sample_percentage=20, + sampling_mode="Exclude sample", + output_header_line=False, +) +# Estimate R2 +# Note: Normally one would do this with a package (eg. sklearn.metrics) +# First pass to estimate sums of residuals and the mean +ss_res = 0 +mean = 0 +n_instances = 0 +with open(output_data_table_path) as output_data_table: + for line in output_data_table: + fields = line.split("\t") + true_target = float(fields[0]) + predicted_target = float(fields[1]) + ss_res += (true_target - predicted_target) ** 2 + mean += true_target + n_instances += 1 + mean /= n_instances + +# Second pass to estimate the total sums of squares and finish the R2 estimation +ss_tot = 0 +with open(output_data_table_path) as output_data_table: + for line in output_data_table: + fields = line.split("\t") + true_target = float(fields[0]) + ss_tot += (true_target - mean) ** 2 +r2_score = 1 - ss_res / ss_tot + +# Print results +print("Adult 'age' regression (30% train)") +print(f"R2 (explained variance) = {r2_score}") +``` +### `sort_data_table()` + +Sorts a database in the simplest way possible + +It is a call to [khiops.core.api.sort_data_table][] with only its mandatory +parameters. This sorts a data table by its default key variable (specified +in the table's dictionary). + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +accidents_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") +dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") +accidents_table_path = os.path.join(accidents_dir, "Accidents.txt") +output_data_table_path = os.path.join( + "kh_samples", + "sort_data_table", + "SortedAccidents.txt", +) + +# Sort table +kh.sort_data_table( + dictionary_file_path, "Accident", accidents_table_path, output_data_table_path +) +``` +### `sort_data_table_expert()` + +Sorts a database by a field other than the default table key + +It is a call to [khiops.core.api.sort_data_table][] with additional parameters +to specify the sorting fields. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +accidents_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") +dictionary_file_path = os.path.join(accidents_dir, "Accidents.kdic") +vehicles_table_path = os.path.join(accidents_dir, "Vehicles.txt") +output_data_table_path = os.path.join( + "kh_samples", "sort_data_table_expert", "SortedVehicles.txt" +) + +# Sort table. Besides the mandatory parameters, it is specified: +# - A list containing the sorting fields +kh.sort_data_table( + dictionary_file_path, + "Vehicle", + vehicles_table_path, + output_data_table_path, + sort_variables=["AccidentId", "VehicleId"], +) +``` +### `extract_keys_from_data_table()` + +Extracts the keys from a database + +It is a call to [khiops.core.api.extract_keys_from_data_table][] with only +its mandatory parameters. + +Pre-requisite: the database must be sorted by its key. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +splice_dir = os.path.join(kh.get_samples_dir(), "SpliceJunction") +dictionary_file_path = os.path.join(splice_dir, "SpliceJunction.kdic") +data_table_path = os.path.join(splice_dir, "SpliceJunctionDNA.txt") +output_data_table_path = os.path.join( + "kh_samples", + "extract_keys_from_data_table", + "KeysSpliceJunction.txt", +) + +# Extract keys from table "SpliceJunctionDNA" to the output table +kh.extract_keys_from_data_table( + dictionary_file_path, + "SpliceJunctionDNA", + data_table_path, + output_data_table_path, +) +``` +### `train_coclustering()` + +Trains a coclustering model in the simplest way possible + +It is a call to [khiops.core.api.train_coclustering][] with only its mandatory +parameters. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +splice_dir = os.path.join(kh.get_samples_dir(), "SpliceJunction") +dictionary_file_path = os.path.join(splice_dir, "SpliceJunction.kdic") +data_table_path = os.path.join(splice_dir, "SpliceJunctionDNA.txt") +coclustering_report_path = os.path.join( + "kh_samples", "train_coclustering", "CoclusteringResults.khcj" +) + +# Train a coclustering model for variables "SampleId" and "Char" +kh.train_coclustering( + dictionary_file_path, + "SpliceJunctionDNA", + data_table_path, + ["SampleId", "Char"], + coclustering_report_path, +) +print(f"Coclustering report file available at {coclustering_report_path}") + +# If you have Khiops Co-Visualization installed you may open the report as follows +# kh.visualize_report(coclustering_report_path) +``` +### `train_instance_variable_coclustering()` + +Trains an instance-variable coclustering model in the simplest way possible + +It is a call to [khiops.core.api.train_instance_variable_coclustering][] with +only its mandatory parameters. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +iris_dir = os.path.join(kh.get_samples_dir(), "Iris") +dictionary_file_path = os.path.join(iris_dir, "Iris.kdic") +data_table_path = os.path.join(iris_dir, "Iris.txt") +coclustering_report_path = os.path.join( + "kh_samples", + "train_instance_variable_coclustering", + "CoclusteringResults.khcj", +) + +# Train a coclustering model for variables "SampleId" and "Char" +kh.train_instance_variable_coclustering( + dictionary_file_path, + "Iris", + data_table_path, + coclustering_report_path, +) +print( + "Instance-variable coclustering report file available " + f"at {coclustering_report_path}" +) + +# If you have Khiops Co-Visualization installed you may open the report as follows +# kh.visualize_report(coclustering_report_path) +``` +### `simplify_coclustering()` + +Simplifies a coclustering model while preserving 80% of its information + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +splice_dir = os.path.join(kh.get_samples_dir(), "SpliceJunction") +dictionary_file_path = os.path.join(splice_dir, "SpliceJunction.kdic") +data_table_path = os.path.join(splice_dir, "SpliceJunctionDNA.txt") +output_dir = os.path.join("kh_samples", "simplify_coclustering") +coclustering_file_path = os.path.join(output_dir, "Coclustering.khcj") +simplified_coclustering_file_path = os.path.join( + output_dir, "simplified_coclustering.khcj" +) + +# Train coclustering model for variables "SampleId" and "Char" +kh.train_coclustering( + dictionary_file_path, + "SpliceJunctionDNA", + data_table_path, + ["SampleId", "Char"], + coclustering_file_path, +) + +# Simplify the trained coclustering with the constraints +# - maximum information preserved: 80% +# - maximum total parts number: 4 +kh.simplify_coclustering( + coclustering_file_path, + simplified_coclustering_file_path, + max_preserved_information=80, + max_total_parts=4, +) +``` +### `extract_clusters()` + +Extract the clusters' id, members, frequencies and typicalities into a file + +```python +# Set the file paths +import os +from khiops import core as kh + +splice_dir = os.path.join(kh.get_samples_dir(), "SpliceJunction") +dictionary_file_path = os.path.join(splice_dir, "SpliceJunction.kdic") +data_table_path = os.path.join(splice_dir, "SpliceJunctionDNA.txt") +output_dir = os.path.join("kh_samples", "extract_clusters") +coclustering_file_path = os.path.join(output_dir, "Coclustering.khcj") +clusters_file_path = os.path.join(output_dir, "extracted_clusters.txt") + +# Train a coclustering model for variables "SampleId" and "Char" +kh.train_coclustering( + dictionary_file_path, + "SpliceJunctionDNA", + data_table_path, + ["SampleId", "Char"], + coclustering_file_path, +) + +# Extract clusters +kh.extract_clusters(coclustering_file_path, "Char", clusters_file_path) +``` +### `deploy_coclustering()` + +Deploys a coclustering + +```python +# Imports +import os +from khiops import core as kh + +# Set the initial file paths +splice_dir = os.path.join(kh.get_samples_dir(), "SpliceJunction") +data_table_path = os.path.join(splice_dir, "SpliceJunctionDNA.txt") +dictionary_file_path = os.path.join(splice_dir, "SpliceJunction.kdic") +output_dir = os.path.join("kh_samples", "deploy_coclustering") +coclustering_file_path = os.path.join(output_dir, "Coclustering.khcj") +coclustering_dictionary_file_path = os.path.join(output_dir, "Coclustering.kdic") +output_data_table_path = os.path.join(output_dir, "DeployedSpliceJunctionDNA.txt") + +# Train a coclustering model for variables "SampleId" and "Char" +kh.train_coclustering( + dictionary_file_path, + "SpliceJunctionDNA", + data_table_path, + ["SampleId", "Char"], + coclustering_file_path, +) + +# Deploy "Char" clusters in the training database +kh.deploy_coclustering( + dictionary_file_path, + "SpliceJunctionDNA", + data_table_path, + coclustering_file_path, + ["SampleId"], + "Char", + coclustering_dictionary_file_path, + output_data_table_path, + header_line=True, +) +``` +### `deploy_coclustering_expert()` + +Deploys a coclustering step-by-step + +The [khiops.core.api.prepare_coclustering_deployment][] method is called +twice to prepare the deployment at two granularity levels. Then, the model +is deployed and the respective deployment dictionary is built. + +This is one of the most complex workflows of the Khiops suite. + +```python +# Imports +import os +from khiops import core as kh + +# Set the initial file paths +splice_dir = os.path.join(kh.get_samples_dir(), "SpliceJunction") +dictionary_file_path = os.path.join(splice_dir, "SpliceJunction.kdic") +data_table_path = os.path.join(splice_dir, "SpliceJunction.txt") +secondary_data_table_path = os.path.join(splice_dir, "SpliceJunctionDNA.txt") +output_dir = os.path.join("kh_samples", "deploy_coclustering_expert") +coclustering_file_path = os.path.join(output_dir, "Coclustering.khcj") + +# Train a coclustering model for variables "SampleId" and "Char" +print("train coclustering on SpliceJunctionDNA") +kh.train_coclustering( + dictionary_file_path, + "SpliceJunctionDNA", + secondary_data_table_path, + ["SampleId", "Char"], + coclustering_file_path, +) + +print("prepare_coclustering_deployment") +# The input dictionary is extended with new coclustering based variables +augmented_dictionary_file_path = os.path.join(output_dir, "Coclustering.kdic") +kh.prepare_coclustering_deployment( + dictionary_file_path, + "SpliceJunction", + coclustering_file_path, + "DNA", + "SampleId", + augmented_dictionary_file_path, +) + +print("prepare_coclustering_deployment with at most two clusters") +# Extend the already extended dictionary with the new variables from a simplified CC +reaugmented_dictionary_file_path = os.path.join( + output_dir, "ReaugmentedCoclustering.kdic" +) +kh.prepare_coclustering_deployment( + augmented_dictionary_file_path, + "SpliceJunction", + coclustering_file_path, + "DNA", + "SampleId", + reaugmented_dictionary_file_path, + variables_prefix="C2_", + max_part_numbers={"SampleId": 2}, +) + +output_data_table_path = os.path.join(output_dir, "TransferredSpliceJunction.txt") + +# Deploy the coclustering with the extended dictionary +print("deploy_model with the new coclustering based variables") +kh.deploy_model( + reaugmented_dictionary_file_path, + "SpliceJunction", + data_table_path, + output_data_table_path, + additional_data_tables={"DNA": secondary_data_table_path}, +) + +deployed_dictionary_file_path = os.path.join( + output_dir, "Transferred_Coclustering.kdic" +) +print("build_deployed_dictionary to get the new dictionary") +kh.build_deployed_dictionary( + reaugmented_dictionary_file_path, + "SpliceJunction", + deployed_dictionary_file_path, +) +``` +### `scenario_prologue()` + +Trains a simple model with a prologue written in the Khiops scenario language + +!!! note + + This is an **advanced** feature. + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +report_file_path = os.path.join( + "kh_samples", "scenario_prologue", "AnalysisResults.khj" +) + +# Set the maximum memory "by hand" with an scenario prologue +scenario_prologue = """ + // Max memory 2000 mb + AnalysisSpec.SystemParameters.MemoryLimit 2000 + """ + +# Train the predictor +kh.train_predictor( + dictionary_file_path, + "Adult", + data_table_path, + "class", + report_file_path, + max_trees=0, + scenario_prologue=scenario_prologue, +) +``` +### `build_deployed_dictionary()` + +Builds a dictionary file to read the output table of a deployed model + +```python +# Imports +import os +from khiops import core as kh + +# Set the file paths +dictionary_file_path = os.path.join(kh.get_samples_dir(), "Iris", "Iris.kdic") +data_table_path = os.path.join(kh.get_samples_dir(), "Iris", "Iris.txt") +output_dir = os.path.join("kh_samples", "build_deployed_dictionary") +deployed_dictionary_file_path = os.path.join(output_dir, "SNB_Iris_deployed.kdic") +report_file_path = os.path.join(output_dir, "AnalysisResults.khj") + +# Train the predictor +_, modeling_dictionary_file_path = kh.train_predictor( + dictionary_file_path, + "Iris", + data_table_path, + "Class", + report_file_path, + max_trees=0, +) + +# Build the dictionary to read the output of the predictor dictionary file +# It will contain the columns of the table generated by deploying the model +kh.build_deployed_dictionary( + modeling_dictionary_file_path, + "SNB_Iris", + deployed_dictionary_file_path, +) + +# Print the deployed dictionary +with open(deployed_dictionary_file_path) as deployed_dictionary_file: + for line in deployed_dictionary_file: + print(line, end="") +``` diff --git a/doc/site/samples/samples_sklearn.md b/doc/site/samples/samples_sklearn.md new file mode 100644 index 00000000..c92c5aec --- /dev/null +++ b/doc/site/samples/samples_sklearn.md @@ -0,0 +1,882 @@ +# Samples sklearn + +The code snippets on this page demonstrate the basic use of the [khiops.sklearn][] module. + +## Script and Jupyter notebook + +The samples in this page are also available as: + +- [Python script](samples_sklearn.py) +- [Jupyter notebook](samples_sklearn.ipynb) + +## Setup + +First make sure you have installed the sample datasets. In a configured +conda shell (ex. *Anaconda Prompt* in Windows) execute: + +```shell +kh-download-datasets +``` + +If that doesn't work open a python console and execute: + +```python +from khiops.tools import download_datasets +download_datasets() +``` + +## Samples + +### `khiops_classifier()` + +Trains a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] +on a monotable dataframe + +```python +# Imports +import os +import pandas as pd +from khiops import core as kh +from khiops.sklearn import KhiopsClassifier +from sklearn import metrics +from sklearn.model_selection import train_test_split + +# Load the dataset into a pandas dataframe +adult_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +adult_df = pd.read_csv(adult_path, sep="\t") + +# Split the whole dataframe into train and test (70%-30%) +adult_train_df, adult_test_df = train_test_split( + adult_df, test_size=0.3, random_state=1 +) + +# Split the dataset into: +# - the X feature table +# - the y target vector ("class" column) +X_train = adult_train_df.drop("class", axis=1) +X_test = adult_test_df.drop("class", axis=1) +y_train = adult_train_df["class"] +y_test = adult_test_df["class"] + +# Create the classifier object +khc = KhiopsClassifier() + +# Train the classifier +khc.fit(X_train, y_train) + +# Predict the classes on the test dataset +y_test_pred = khc.predict(X_test) +print("Predicted classes (first 10):") +print(y_test_pred[0:10]) +print("---") + +# Predict the class probabilities on the test dataset +y_test_probas = khc.predict_proba(X_test) +print(f"Class order: {khc.classes_}") +print("Predicted class probabilities (first 10):") +print(y_test_probas[0:10]) +print("---") + +# Evaluate accuracy and auc metrics on the test dataset +test_accuracy = metrics.accuracy_score(y_test, y_test_pred) +test_auc = metrics.roc_auc_score(y_test, y_test_probas[:, 1]) +print(f"Test accuracy = {test_accuracy}") +print(f"Test auc = {test_auc}") + +# If you have Khiops Visualization installed you may open the report as follows +# khc.export_report_file("report.khj") +# kh.visualize_report("report.khj") +``` +### `khiops_classifier_multiclass()` + +Trains a multiclass +[KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] on a monotable +dataframe + +```python +# Imports +import os +import pandas as pd +from khiops import core as kh +from khiops.sklearn import KhiopsClassifier +from sklearn import metrics +from sklearn.model_selection import train_test_split + +# Load the dataset into a pandas dataframe +iris_path = os.path.join(kh.get_samples_dir(), "Iris", "Iris.txt") +iris_df = pd.read_csv(iris_path, sep="\t") + +# Split the whole dataframe into train and test (70%-30%) +iris_train_df, iris_test_df = train_test_split(iris_df, test_size=0.3, random_state=1) + +# Split the dataset into: +# - the X feature table +# - the y target vector ("Class" column) +X_train = iris_train_df.drop("Class", axis=1) +X_test = iris_test_df.drop("Class", axis=1) +y_train = iris_train_df["Class"] +y_test = iris_test_df["Class"] + +# Create the classifier object +khc = KhiopsClassifier() + +# Train the classifier +khc.fit(X_train, y_train) + +# Predict the classes on the test dataset +y_test_pred = khc.predict(X_test) +print("Predicted classes (first 10):") +print(y_test_pred[:10]) +print("---") + +# Predict the class probabilities on the test datasets +y_test_probas = khc.predict_proba(X_test) +print(f"Class order: {khc.classes_}") +print("Predicted class probabilities (first 10):") +print(y_test_probas[:10]) +print("---") + +# Evaluate accuracy and auc metrics on the test dataset +test_accuracy = metrics.accuracy_score(y_test, y_test_pred) +test_auc = metrics.roc_auc_score(y_test, y_test_probas, multi_class="ovr") +print(f"Test accuracy = {test_accuracy}") +print(f"Test auc = {test_auc}") +``` +### `khiops_classifier_text()` + +Train a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] on +a monotable dataframe with textual data + +```python +# Imports +import os +import pandas as pd +from khiops import core as kh +from khiops.sklearn import KhiopsClassifier +from sklearn import metrics +from sklearn.model_selection import train_test_split + +# Load the dataset into a pandas dataframe +data_table_path = os.path.join( + kh.get_samples_dir(), "NegativeAirlineTweets", "NegativeAirlineTweets.txt" +) +data_df = pd.read_csv(data_table_path, sep="\t") + +# Split the whole dataframe into train and test (70%-30%) +data_train_df, data_test_df = train_test_split(data_df, test_size=0.3, random_state=1) + +# Split the dataset into: +# - the X feature table +# - the y target vector ("negativereason" column) +X_train = data_train_df.drop("negativereason", axis=1) +X_test = data_test_df.drop("negativereason", axis=1) +y_train = data_train_df["negativereason"] +y_test = data_test_df["negativereason"] + +# Set Pandas StringDType on the "text" column +X_train["text"] = X_train["text"].astype("string") +X_test["text"] = X_test["text"].astype("string") + +# Create the classifier object +khc = KhiopsClassifier() + +# Train the classifier +khc.fit(X_train, y_train) + +# Predict the classes on the test dataset +y_test_pred = khc.predict(X_test) +print("Predicted classes (first 10):") +print(y_test_pred[0:10]) +print("---") + +# Predict the class probabilities on the test dataset +y_test_probas = khc.predict_proba(X_test) +print(f"Class order: {khc.classes_}") +print("Predicted class probabilities (first 10):") +print(y_test_probas[0:10]) +print("---") + +# Evaluate the accuracy metric on the test dataset +test_accuracy = metrics.accuracy_score(y_test, y_test_pred) +print(f"Test accuracy = {test_accuracy}") + +# If you have Khiops Visualization installed you may open the report as follows +# khc.export_report_file("report.khj") +# kh.visualize_report("report.khj") +``` +### `khiops_classifier_multitable_star()` + +Trains a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] +on a star multi-table dataset + +```python +# Imports +import os +import pandas as pd +from khiops import core as kh +from khiops.sklearn import KhiopsClassifier, train_test_split_dataset +from sklearn import metrics + +# Load the dataset into pandas dataframes +accidents_data_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") +accidents_df = pd.read_csv( + os.path.join(accidents_data_dir, "Accidents.txt"), + sep="\t", +) +vehicles_df = pd.read_csv(os.path.join(accidents_data_dir, "Vehicles.txt"), sep="\t") + +# Create the dataset spec and the target +X = { + "main_table": (accidents_df.drop("Gravity", axis=1), ["AccidentId"]), + "additional_data_tables": { + "Vehicles": (vehicles_df, ["AccidentId", "VehicleId"]), + }, +} +y = accidents_df["Gravity"] + +# Split the dataset into train and test +X_train, X_test, y_train, y_test = train_test_split_dataset( + X, y, test_size=0.3, random_state=1 +) + +# Train the classifier (by default it analyzes 100 multi-table features) +khc = KhiopsClassifier() +khc.fit(X_train, y_train) + +# Predict the class on the test dataset +y_test_pred = khc.predict(X_test) +print("Predicted classes (first 10):") +print(y_test_pred[:10]) +print("---") + +# Predict the class probability on the test dataset +y_test_probas = khc.predict_proba(X_test) +print(f"Class order: {khc.classes_}") +print("Predicted class probabilities (first 10):") +print(y_test_probas[:10]) +print("---") + +# Evaluate accuracy and auc metrics on the test dataset +test_accuracy = metrics.accuracy_score(y_test, y_test_pred) +test_auc = metrics.roc_auc_score(y_test, y_test_probas[:, 1]) +print(f"Test accuracy = {test_accuracy}") +print(f"Test auc = {test_auc}") +``` +### `khiops_classifier_multitable_snowflake()` + +Trains a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] +on a snowflake multi-table dataset + +```python +# Imports +import os +import pandas as pd +from khiops import core as kh +from khiops.sklearn import KhiopsClassifier, train_test_split_dataset +from sklearn import metrics + +# Load the dataset tables into dataframes +accidents_data_dir = os.path.join(kh.get_samples_dir(), "Accidents") +accidents_df = pd.read_csv(os.path.join(accidents_data_dir, "Accidents.txt"), sep="\t") +users_df = pd.read_csv(os.path.join(accidents_data_dir, "Users.txt"), sep="\t") +vehicles_df = pd.read_csv(os.path.join(accidents_data_dir, "Vehicles.txt"), sep="\t") +places_df = pd.read_csv( + os.path.join(accidents_data_dir, "Places.txt"), sep="\t", low_memory=False +) + +# Build the multi-table dataset spec (drop the target column "Gravity") +X = { + "main_table": (accidents_df.drop("Gravity", axis=1), ["AccidentId"]), + "additional_data_tables": { + "Vehicles": (vehicles_df, ["AccidentId", "VehicleId"]), + "Vehicles/Users": (users_df, ["AccidentId", "VehicleId"]), + "Places": (places_df, ["AccidentId"], True), + }, +} + +# Load the target variable "Gravity" +y = accidents_df["Gravity"] + +# Split into train and test datasets +X_train, X_test, y_train, y_test = train_test_split_dataset(X, y) + +# Train the classifier (by default it creates 1000 multi-table features) +khc = KhiopsClassifier(n_trees=0) +khc.fit(X_train, y_train) + +# Predict the class on the test dataset +y_test_pred = khc.predict(X_test) +print("Predicted classes (first 10):") +print(y_test_pred[:10]) +print("---") + +# Predict the class probability on the test dataset +y_test_probas = khc.predict_proba(X_test) +print(f"Class order: {khc.classes_}") +print("Predicted class probabilities (first 10):") +print(y_test_probas[:10]) +print("---") + +# Evaluate accuracy and auc metrics on the test dataset +test_accuracy = metrics.accuracy_score(y_test_pred, y_test) +test_auc = metrics.roc_auc_score(y_test, y_test_probas[:, 1]) +print(f"Test accuracy = {test_accuracy}") +print(f"Test auc = {test_auc}") +``` +### `khiops_classifier_sparse()` + +Trains a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] +on a monotable sparse matrix + +```python +# Imports +from khiops.sklearn import KhiopsClassifier +from sklearn import metrics +from sklearn.datasets import fetch_20newsgroups +from sklearn.feature_extraction.text import HashingVectorizer + +# Load 3 classes of the 20newsgroups dataset +categories = ["comp.graphics", "sci.space", "misc.forsale"] +data_train, y_train = fetch_20newsgroups( + subset="train", + categories=categories, + return_X_y=True, +) +data_test, y_test = fetch_20newsgroups( + subset="test", + categories=categories, + return_X_y=True, +) + +# Extract features from the training data using a sparse vectorizer +vectorizer = HashingVectorizer(n_features=2**10, stop_words="english") +X_train = vectorizer.fit_transform(data_train) + +# Extract features from the test data using the same vectorizer +X_test = vectorizer.transform(data_test) + +# Create the classifier object +khc = KhiopsClassifier() + +# Train the classifier +khc.fit(X_train, y_train) + +# Predict the classes on the test dataset +y_test_pred = khc.predict(X_test) +print("Predicted classes (first 10):") +print(y_test_pred[0:10]) +print("---") + +# Predict the class probabilities on the test dataset +y_test_probas = khc.predict_proba(X_test) +print(f"Class order: {khc.classes_}") +print("Predicted class probabilities (first 10):") +print(y_test_probas[0:10]) +print("---") + +# Evaluate accuracy and auc metrics on the test dataset +test_accuracy = metrics.accuracy_score(y_test, y_test_pred) +test_auc = metrics.roc_auc_score(y_test, y_test_probas, multi_class="ovr") +print(f"Test accuracy = {test_accuracy}") +print(f"Test auc = {test_auc}") +``` +### `khiops_classifier_pickle()` + +Shows the serialization and deserialization of a +[KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] + +```python +# Imports +import os +import pandas as pd +import pickle +from khiops import core as kh +from khiops.sklearn import KhiopsClassifier + +# Create/clean the output directory +results_dir = os.path.join("kh_samples", "khiops_classifier_pickle") +khc_pickle_path = os.path.join(results_dir, "khiops_classifier.pkl") +if os.path.exists(khc_pickle_path): + os.remove(khc_pickle_path) +else: + os.makedirs(results_dir, exist_ok=True) + +# Load the "Iris" dataset +iris_path = os.path.join(kh.get_samples_dir(), "Iris", "Iris.txt") +iris_df = pd.read_csv(iris_path, sep="\t") +X = iris_df.drop("Class", axis=1) +y = iris_df["Class"] + +# Train the model with the Iris dataset +khc = KhiopsClassifier() +khc.fit(X, y) + +# Pickle its content to a file +with open(khc_pickle_path, "wb") as khc_pickle_output_file: + pickle.dump(khc, khc_pickle_output_file) + +# Unpickle it +with open(khc_pickle_path, "rb") as khc_pickle_file: + new_khc = pickle.load(khc_pickle_file) + +# Make some predictions on the training dataset with the unpickled classifier +new_khc.predict(X) +y_predicted = new_khc.predict(X) +print("Predicted classes (first 10):") +print(y_predicted[:10]) +print("---") +``` +### `khiops_classifier_with_hyperparameters()` + +Trains a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] +on a star multi-table dataset (advanced version with more hyperparameters) + +```python +# Imports +import os +import pandas as pd +from khiops import core as kh +from khiops.sklearn import KhiopsClassifier +from sklearn import metrics +from sklearn.model_selection import train_test_split + +# Load the root table of the dataset into a pandas dataframe +accidents_dataset_path = os.path.join(kh.get_samples_dir(), "AccidentsSummary") +accidents_df = pd.read_csv( + os.path.join(accidents_dataset_path, "Accidents.txt"), + sep="\t", +) + +# Split the root dataframe into train and test +accidents_train_df, accidents_test_df = train_test_split( + accidents_df, test_size=0.3, random_state=1 +) + +# Obtain the main X feature table and the y target vector ("Class" column) +y_train = accidents_train_df["Gravity"] +y_test = accidents_test_df["Gravity"] +X_train_main = accidents_train_df.drop("Gravity", axis=1) +X_test_main = accidents_test_df.drop("Gravity", axis=1) + +# Load the secondary table of the dataset into a pandas dataframe +vehicles_df = pd.read_csv( + os.path.join(accidents_dataset_path, "Vehicles.txt"), sep="\t" +) + +# Split the secondary dataframe with the keys of the split root dataframe +X_train_ids = X_train_main["AccidentId"].to_frame() +X_test_ids = X_test_main["AccidentId"].to_frame() +X_train_secondary = X_train_ids.merge(vehicles_df, on="AccidentId") +X_test_secondary = X_test_ids.merge(vehicles_df, on="AccidentId") + +# Create the dataset multitable specification for the train/test split +# We specify each table with a name and a tuple (dataframe, key_columns) +X_train = { + "main_table": (X_train_main, ["AccidentId"]), + "additional_data_tables": { + "Vehicles": (X_train_secondary, ["AccidentId", "VehicleId"]), + }, +} +X_test = { + "main_table": (X_test_main, ["AccidentId"]), + "additional_data_tables": { + "Vehicles": (X_test_secondary, ["AccidentId", "VehicleId"]), + }, +} +# Train the classifier (by default it analyzes 100 multi-table features) +khc = KhiopsClassifier( + n_features=20, + n_pairs=5, + n_trees=5, + n_selected_features=10, + n_evaluated_features=15, + specific_pairs=[("Light", "Weather"), ("Light", "IntersectionType")], + all_possible_pairs=True, + construction_rules=["TableMode", "TableSelection"], + group_target_value=False, +) +khc.fit(X_train, y_train) + +# Predict the class on the test dataset +y_test_pred = khc.predict(X_test) +print("Predicted classes (first 10):") +print(y_test_pred[:10]) +print("---") + +# Predict the class probability on the test dataset +y_test_probas = khc.predict_proba(X_test) +print(f"Class order: {khc.classes_}") +print("Predicted class probabilities (first 10):") +print(y_test_probas[:10]) +print("---") + +# Evaluate accuracy and auc metrics on the test dataset +test_accuracy = metrics.accuracy_score(y_test, y_test_pred) +test_auc = metrics.roc_auc_score(y_test, y_test_probas[:, 1]) +print(f"Test accuracy = {test_accuracy}") +print(f"Test auc = {test_auc}") +``` +### `khiops_regressor()` + +Trains a [KhiopsRegressor][khiops.sklearn.estimators.KhiopsRegressor] on +a monotable dataframe + +```python +# Imports +import os +import pandas as pd +from khiops import core as kh +from khiops.sklearn import KhiopsRegressor +from sklearn import metrics +from sklearn.model_selection import train_test_split + +# Load the "Adult" dataset and set the target to the "age" column +adult_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +adult_df = pd.read_csv(adult_path, sep="\t") +X = adult_df.drop("age", axis=1) +y = adult_df["age"] + +# Split the whole dataframe into train and test (40%-60% for speed) +X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=1) + +# Create the regressor object +khr = KhiopsRegressor() + +# Train the regressor +khr.fit(X_train, y_train) + +# Predict the values on the test dataset +y_test_pred = khr.predict(X_test) +print("Predicted values for 'age' (first 10):") +print(y_test_pred[:10]) +print("---") + +# Evaluate R2 and MAE metrics on the test dataset +test_r2 = metrics.r2_score(y_test, y_test_pred) +test_mae = metrics.mean_absolute_error(y_test, y_test_pred) +print(f"Test R2 = {test_r2}") +print(f"Test MAE = {test_mae}") + +# If you have Khiops Visualization installed you may open the report as follows +# khr.export_report_file("report.khj") +# kh.visualize_report("report.khj") +``` +### `khiops_encoder()` + +Trains a [KhiopsEncoder][khiops.sklearn.estimators.KhiopsEncoder] on a +monotable dataframe + +The Khiops encoder is a supervised feature encoder. It discretizes numerical +features and groups categorical features in a way that the resulting interval/groups +have the highest class-purity. + +!!! note + + For simplicity we train from the whole dataset. To assess the performance one + usually splits the dataset into train and test subsets. + +```python +# Imports +import os +import pandas as pd +from khiops import core as kh +from khiops.sklearn import KhiopsEncoder + +# Load the dataset +iris_path = os.path.join(kh.get_samples_dir(), "Iris", "Iris.txt") +iris_df = pd.read_csv(iris_path, sep="\t") +X = iris_df.drop("Class", axis=1) +y = iris_df["Class"] + +# Create the encoder object +khe = KhiopsEncoder(transform_type_numerical="part_label") +khe.fit(X, y) + +# Transform the training dataset +X_transformed = khe.transform(X) + +# Print both the original and transformed features +print("Original:") +print(X[:10]) +print("---") +print("Encoded feature names:") +print(khe.feature_names_out_) +print("Encoded data:") +print(X_transformed[:10]) +print("---") + +# If you have Khiops Visualization installed you may open the report as follows +# khe.export_report_file("report.khj") +# kh.visualize_report("report.khj") +``` +### `khiops_encoder_multitable_star()` + +Trains a [KhiopsEncoder][khiops.sklearn.estimators.KhiopsEncoder] on a +star multi-table dataset + +```python +# Imports +import os +import pandas as pd +from khiops import core as kh +from khiops.sklearn import KhiopsEncoder + +# Load the dataset tables into dataframe +accidents_data_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") +accidents_df = pd.read_csv( + os.path.join(accidents_data_dir, "Accidents.txt"), + sep="\t", +) +vehicles_df = pd.read_csv(os.path.join(accidents_data_dir, "Vehicles.txt"), sep="\t") + +# Build the multi-table dataset spec (drop the target column "Gravity") +X = { + "main_table": (accidents_df.drop("Gravity", axis=1), ["AccidentId"]), + "additional_data_tables": { + "Vehicles": (vehicles_df, ["AccidentId", "VehicleId"]), + }, +} + +# Load the target variable "Gravity" +y = accidents_df["Gravity"] + +# Create the KhiopsEncoder with 5 multitable features and fit it +khe = KhiopsEncoder(n_features=10) +khe.fit(X, y) + +# Transform the train dataset +print("Encoded feature names:") +print(khe.feature_names_out_) +print("Encoded data:") +print(khe.transform(X)[:10]) +``` +### `khiops_encoder_multitable_snowflake()` + +Trains a [KhiopsEncoder][khiops.sklearn.estimators.KhiopsEncoder] on a +snowflake multi-table dataset + +```python +# Imports +import os +import pandas as pd +from khiops import core as kh +from khiops.sklearn import KhiopsEncoder + +# Load the tables into dataframes +accidents_data_dir = os.path.join(kh.get_samples_dir(), "Accidents") +accidents_df = pd.read_csv(os.path.join(accidents_data_dir, "Accidents.txt"), sep="\t") +users_df = pd.read_csv(os.path.join(accidents_data_dir, "Users.txt"), sep="\t") +vehicles_df = pd.read_csv(os.path.join(accidents_data_dir, "Vehicles.txt"), sep="\t") +places_df = pd.read_csv( + os.path.join(accidents_data_dir, "Places.txt"), sep="\t", low_memory=False +) + +# Build the multi-table dataset spec (drop the target column "Gravity") +X = { + "main_table": (accidents_df.drop("Gravity", axis=1), ["AccidentId"]), + "additional_data_tables": { + "Vehicles": (vehicles_df, ["AccidentId", "VehicleId"]), + "Vehicles/Users": (users_df, ["AccidentId", "VehicleId"]), + "Places": (places_df, ["AccidentId"], True), + }, +} + +# Load the target variable "Gravity" +y = accidents_df["Gravity"] + +# Create the KhiopsEncoder with 10 additional multitable features and fit it +khe = KhiopsEncoder(n_features=10) +khe.fit(X, y) + +# Transform the train dataset +print("Encoded feature names:") +print(khe.feature_names_out_) +print("Encoded data:") +print(khe.transform(X)[:10]) +``` +### `khiops_encoder_pipeline_with_hgbc()` + +Uses a [KhiopsEncoder][khiops.sklearn.estimators.KhiopsEncoder] with a +[HistGradientBoostingClassifier][sklearn.ensemble.HistGradientBoostingClassifier] + +```python +# Imports +import os +import pandas as pd +from khiops import core as kh +from khiops.sklearn import KhiopsEncoder +from sklearn import metrics +from sklearn.compose import ColumnTransformer +from sklearn.ensemble import HistGradientBoostingClassifier +from sklearn.model_selection import train_test_split +from sklearn.pipeline import Pipeline +from sklearn.preprocessing import OneHotEncoder + +# Load the dataset into dataframes +adult_path = os.path.join(kh.get_samples_dir(), "Adult", "Adult.txt") +adult_df = pd.read_csv(adult_path, sep="\t") +X = adult_df.drop("class", axis=1) +y = adult_df["class"] + +# Split the dataset into train and test (70%-30%) +X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1) + +# Create the pipeline and fit it. Steps: +# - The khiops supervised column encoder, generates a full-categorical table +# - One hot encoder in all columns +# - Train the HGB classifier +pipe_steps = [ + ("khiops_enc", KhiopsEncoder()), + ( + "onehot_enc", + ColumnTransformer([], remainder=OneHotEncoder(sparse_output=False)), + ), + ("hgb_clf", HistGradientBoostingClassifier()), +] +pipe = Pipeline(pipe_steps) +pipe.fit(X_train, y_train) + +# Predict the classes on the test dataset +y_test_pred = pipe.predict(X_test) +print("Predicted classes (first 10):") +print(y_test_pred[:10]) +print("---") + +# Predict the class probabilities on the test dataset +y_test_probas = pipe.predict_proba(X_test) +print("Predicted class probabilities (first 10):") +print(y_test_probas[:10]) +print("---") + +# Evaluate accuracy and auc metrics on the test dataset +test_accuracy = metrics.accuracy_score(y_test, y_test_pred) +test_auc = metrics.roc_auc_score(y_test, y_test_probas[:, 1]) +print(f"Test accuracy = {test_accuracy}") +print(f"Test auc = {test_auc}") +``` +### `khiops_encoder_with_hyperparameters()` + +Trains a [KhiopsEncoder][khiops.sklearn.estimators.KhiopsEncoder] on a +star multi-table dataset (advanced version with more hyperparameters) + +```python +# Imports +import os +import pandas as pd +from khiops import core as kh +from khiops.sklearn import KhiopsEncoder + +# Load the tables into dataframes +accidents_data_dir = os.path.join(kh.get_samples_dir(), "AccidentsSummary") +accidents_df = pd.read_csv(os.path.join(accidents_data_dir, "Accidents.txt"), sep="\t") +vehicles_df = pd.read_csv(os.path.join(accidents_data_dir, "Vehicles.txt"), sep="\t") + +# Build the multi-table dataset spec (drop the target column "Gravity") +X = { + "main_table": (accidents_df.drop("Gravity", axis=1), ["AccidentId"]), + "additional_data_tables": { + "Vehicles": (vehicles_df, ["AccidentId", "VehicleId"]), + }, +} + +# Load the target variable "Gravity" +y = accidents_df["Gravity"] + +# Create the KhiopsEncoder with 10 additional multitable features and fit it +khe = KhiopsEncoder( + n_features=20, + n_pairs=5, + n_trees=5, + specific_pairs=[("Light", "Weather"), ("Light", "IntersectionType")], + all_possible_pairs=True, + construction_rules=["TableMode", "TableSelection"], + group_target_value=False, + informative_features_only=True, + keep_initial_variables=True, + transform_type_categorical="part_id", + transform_type_numerical="part_id", + transform_type_pairs="part_id", +) +khe.fit(X, y) + +# Transform the train dataset +print("Encoded feature names:") +print(khe.feature_names_out_) +print("Encoded data:") +print(khe.transform(X)[:10]) +``` +### `khiops_coclustering()` + +Trains a [KhiopsCoclustering][khiops.sklearn.estimators.KhiopsCoclustering] +on a dataframe + +```python +# Imports +import os +import pandas as pd +from khiops import core as kh +from khiops.sklearn import KhiopsCoclustering +from sklearn.model_selection import train_test_split + +# Load the secondary table of the dataset into a pandas dataframe +splice_data_dir = os.path.join(kh.get_samples_dir(), "SpliceJunction") +splice_dna_df = pd.read_csv( + os.path.join(splice_data_dir, "SpliceJunctionDNA.txt"), sep="\t" +) + +# Train with only 70% of data (for speed in this example) +X, _ = train_test_split(splice_dna_df, test_size=0.3, random_state=1) + +# Create the KhiopsCoclustering instance +khcc = KhiopsCoclustering() + +# Train the model with the whole dataset +khcc.fit(X, id_column="SampleId") + +# Predict the clusters in some instances +X_clusters = khcc.predict(X) +print("Predicted clusters (first 10)") +print(X_clusters[:10]) +print("---") + +# If you have Khiops Co-Visualization installed you may open the report as follows +# khcc.export_report_file("report.khcj") +# kh.visualize_report("report.khcj") +``` +### `khiops_coclustering_simplify()` + +Simplifies a [KhiopsCoclustering][khiops.sklearn.estimators.KhiopsCoclustering] +already trained on a dataframe + +```python +# Imports +import os +import pandas as pd +from khiops import core as kh +from khiops.sklearn import KhiopsCoclustering +from sklearn.model_selection import train_test_split + +# Load the secondary table of the dataset into a pandas dataframe +splice_data_dir = os.path.join(kh.get_samples_dir(), "SpliceJunction") +splice_dna_X = pd.read_csv( + os.path.join(splice_data_dir, "SpliceJunctionDNA.txt"), sep="\t" +) + +# Train with only 70% of data (for speed in this example) +X, _ = train_test_split(splice_dna_X, test_size=0.3, random_state=1) + +# Create the KhiopsCoclustering instance +khcc = KhiopsCoclustering() + +# Train the model with the whole dataset +khcc.fit(X, id_column="SampleId") + +# Simplify coclustering along the individual ID dimension +simplified_khcc = khcc.simplify(max_part_numbers={"SampleId": 3}) + +# Predict the clusters using the simplified model +X_clusters = simplified_khcc.predict(X) +print("Predicted clusters (only three at most)") +print(X_clusters) +print("---") +``` diff --git a/doc/site/sklearn/estimators.md b/doc/site/sklearn/estimators.md new file mode 100644 index 00000000..d30d0a92 --- /dev/null +++ b/doc/site/sklearn/estimators.md @@ -0,0 +1,8 @@ +# khiops.sklearn.estimators + +*Submodule of khiops.sklearn* + +::: khiops.sklearn.estimators + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/site/sklearn/helpers.md b/doc/site/sklearn/helpers.md new file mode 100644 index 00000000..dc45dca9 --- /dev/null +++ b/doc/site/sklearn/helpers.md @@ -0,0 +1,8 @@ +# khiops.sklearn.helpers + +*Submodule of khiops.sklearn* + +::: khiops.sklearn.helpers + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/site/sklearn/index.md b/doc/site/sklearn/index.md new file mode 100644 index 00000000..89855d29 --- /dev/null +++ b/doc/site/sklearn/index.md @@ -0,0 +1,27 @@ +# khiops.sklearn + +::: khiops.sklearn + options: + members: false + show_root_heading: false + +!!! note + + For convenience, the public members of the above modules are imported to the + `sklearn` namespace. For example the estimator class + [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] can be imported as follows: + + ```python + from khiops.sklearn import KhiopsClassifier + clf = KhiopsClassifier() + ``` + +## Modules + +- [estimators](estimators.md) - Scikit-Learn compatible estimators +- [helpers](helpers.md) - Helper functions + +## Related Docs + +- [Samples sklearn](../samples/samples_sklearn.md) +- [Notes](../notes.md) diff --git a/doc/site/tools/index.md b/doc/site/tools/index.md new file mode 100644 index 00000000..0d469c8d --- /dev/null +++ b/doc/site/tools/index.md @@ -0,0 +1,8 @@ +# khiops.tools + +*Submodule of khiops* + +::: khiops.tools + options: + heading_level: 2 + show_root_heading: false diff --git a/doc/sklearn/index.rst b/doc/sklearn/index.rst deleted file mode 100644 index 238b4412..00000000 --- a/doc/sklearn/index.rst +++ /dev/null @@ -1,23 +0,0 @@ -khiops.sklearn -============== -.. note:: - For convenience, the public members of the above modules are imported to the - ``sklearn`` namespace. For example the estimator class - `~.sklearn.estimators.KhiopsClassifier` can be imported as follows:: - - from khiops.sklearn import KhiopsClassifier - clf = KhiopsClassifier() - -.. currentmodule:: khiops.sklearn -.. autosummary:: - :toctree: generated - :recursive: - :nosignatures: - - estimators - helpers - -Related Docs ------------- -- :doc:`../samples/samples_sklearn` -- :doc:`../notes` diff --git a/doc/tools/index.rst b/doc/tools/index.rst deleted file mode 100644 index f6c1a7cf..00000000 --- a/doc/tools/index.rst +++ /dev/null @@ -1,10 +0,0 @@ -Tools -===== -These are auxiliary tools for the Khiops Python library. - -.. currentmodule:: khiops -.. autosummary:: - :toctree: generated - :nosignatures: - - tools diff --git a/doc/tutorials/index.rst b/doc/tutorials/index.rst deleted file mode 100644 index eed6a2b9..00000000 --- a/doc/tutorials/index.rst +++ /dev/null @@ -1,20 +0,0 @@ -Tutorials -========= - -Sklearn -------- -These :download:`Jupyter notebook tutorials ` cover the basic usage of the ``sklearn`` Khiops sub-module. The solution notebooks are :download:`available here ` or you can browse them in this page: - -- :doc:`Sklearn Basics 1 - Train, Evaluate and Deploy a Classifier` -- :doc:`Sklearn Basics 2 - Train a Classifier on a Star Multi-Table Dataset` -- :doc:`Sklearn Basics 3 - Train a Classifier on a Snowflake Multi-Table Dataset` -- :doc:`Sklearn Basics 4 - Train a Coclustering` - -Core ----- -These :download:`Jupyter notebook tutorials ` cover the basic usage of the ``core`` Khiops sub-module. The solution notebooks are :download:`available here ` or you can browse them in this page: - -- :doc:`Core Basics 1 - Train, Evaluate and Deploy a Classifier` -- :doc:`Core Basics 2 - Train a Classifier on a Star Multi-Table Dataset` -- :doc:`Core Basics 3 - Train a Classifier on a Snowflake Multi-Table Dataset` -- :doc:`Core Basics 4 - Train a Coclustering` diff --git a/doc/clean-doc b/doc/util/clean-doc similarity index 86% rename from doc/clean-doc rename to doc/util/clean-doc index bac977a8..011e164e 100755 --- a/doc/clean-doc +++ b/doc/util/clean-doc @@ -9,6 +9,7 @@ # Argbash is FREE SOFTWARE, see https://argbash.io for more info # Generated online by https://argbash.io/generate +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) die() { @@ -72,16 +73,13 @@ parse_commandline "$@" ### END OF CODE GENERATED BY Argbash (sortof) ### ]) # [ <-- needed because of Argbash -rm -v core/generated/*.rst -rm -v sklearn/generated/*.rst -rm -v internal/generated/*.rst -rm -v tools/generated/*.rst -sphinx-build -M clean . _build +rm -rf "${SCRIPT_DIR}/../build" +git checkout -- "${SCRIPT_DIR}/../../zensical.toml" 2>/dev/null || true if [[ $_arg_clean_tutorial == "on" ]] then - rm -rfv khiops-python-tutorial - rm -rfv ./tutorials + rm -rfv "${SCRIPT_DIR}/khiops-python-tutorial" + rm -rfv "${SCRIPT_DIR}/../site/tutorials" fi # ] <-- needed because of Argbash diff --git a/doc/util/convert-samples-hook b/doc/util/convert-samples-hook new file mode 100755 index 00000000..65f4e83f --- /dev/null +++ b/doc/util/convert-samples-hook @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Set the documentation and samples directories +DOC_DIR=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) +SAMPLES_DIR="$(dirname "$(dirname "$DOC_DIR")")/khiops/samples" +SITE_DIR="$(dirname "$DOC_DIR")/site" + +# Generate the Markdown pages +python "$DOC_DIR/convert_samples.py" -f md "$SAMPLES_DIR" "$SITE_DIR/samples/samples.md" +python "$DOC_DIR/convert_samples.py" --sklearn -f md "$SAMPLES_DIR" "$SITE_DIR/samples/samples_sklearn.md" + +# Generate the notebooks +python "$DOC_DIR/convert_samples.py" "$SAMPLES_DIR" "$SAMPLES_DIR/samples.ipynb" +python "$DOC_DIR/convert_samples.py" --sklearn "$SAMPLES_DIR" "$SAMPLES_DIR/samples_sklearn.ipynb" + +# Copy source scripts and notebooks to doc/samples/ for download links +cp "$SAMPLES_DIR/samples.py" "$SITE_DIR/samples/" +cp "$SAMPLES_DIR/samples.ipynb" "$SITE_DIR/samples/" +cp "$SAMPLES_DIR/samples_sklearn.py" "$SITE_DIR/samples/" +cp "$SAMPLES_DIR/samples_sklearn.ipynb" "$SITE_DIR/samples/" diff --git a/doc/convert_samples.py b/doc/util/convert_samples.py similarity index 77% rename from doc/convert_samples.py rename to doc/util/convert_samples.py index b36b65e1..57d96e03 100644 --- a/doc/convert_samples.py +++ b/doc/util/convert_samples.py @@ -5,7 +5,7 @@ # which is available at https://spdx.org/licenses/BSD-3-Clause-Clear.html or # # see the "LICENSE.md" file for more details. # ###################################################################################### -"""Transforms the samples.py script to a notebook or reST page""" +"""Transforms the samples.py script to a notebook or Markdown page""" import argparse import inspect @@ -66,57 +66,51 @@ def create_sample_cells(sample_method): return sample_execution_cells -def create_rest_page_header(script_name): +def create_markdown_page_header(script_name): + """Creates the header for a Markdown samples page""" subtitle = "The code snippets on this page demonstrate the basic use of the " if script_name == "samples": title = "Samples core" - subtitle += ":py:mod:`khiops.core` module." + subtitle += "[khiops.core][] module." else: title = "Samples sklearn" - subtitle += ":py:mod:`khiops.sklearn ` module." + subtitle += "[khiops.sklearn][] module." return ( - ":orphan:\n" - "\n" - f".. currentmodule:: {script_name}\n" - "\n" - f"{title}\n" - f"{'=' * len(title)}\n" + f"# {title}\n" "\n" f"{subtitle}\n" "\n" - "Script and Jupyter notebook\n" - "---------------------------\n" + "## Script and Jupyter notebook\n" + "\n" "The samples in this page are also available as:\n" "\n" - f"- :download:`Python script <../../khiops/samples/{script_name}.py>`\n" - f"- :download:`Jupyter notebook <../../khiops/samples/{script_name}.ipynb>`\n" + f"- [Python script]({script_name}.py)\n" + f"- [Jupyter notebook]({script_name}.ipynb)\n" + "\n" + "## Setup\n" "\n" - "Setup\n" - "-----\n" "First make sure you have installed the sample datasets. In a configured\n" "conda shell (ex. *Anaconda Prompt* in Windows) execute:\n" "\n" - ".. code-block:: shell\n" - "\n" - " kh-download-datasets\n" + "```shell\n" + "kh-download-datasets\n" + "```\n" "\n" "If that doesn't work open a python console and execute:\n" "\n" - ".. code-block:: python\n" - "\n" - " from khiops.tools import download_datasets\n" - " download_datasets()\n" + "```python\n" + "from khiops.tools import download_datasets\n" + "download_datasets()\n" + "```\n" "\n" - "\n" - "Samples\n" - "-------\n" + "## Samples\n" ) def split_docstring(source): docstring_open_quote = source.find('"""') if docstring_open_quote == -1: - source_without_docstring = sample_source + source_without_docstring = source docstring = "" else: docstring_close_quote = ( @@ -124,20 +118,23 @@ def split_docstring(source): ) source_without_docstring = source[docstring_close_quote + 4 :] docstring = source[docstring_open_quote + 3 : docstring_close_quote] - return source_without_docstring, docstring + return source_without_docstring, inspect.cleandoc(docstring) -def create_rest_page_section(sample_function): - code, _ = split_docstring(inspect.getsource(sample_function)) +def create_markdown_page_section(sample_function): + """Creates a Markdown section for a sample function""" + code, docstring = split_docstring(inspect.getsource(sample_function)) code = textwrap.dedent(code) code = black.format_str(code, mode=black.Mode()) - code = textwrap.indent(code, " ") code = code.rstrip() return ( - f".. autofunction:: {sample_function.__name__}\n" - ".. code-block:: python\n" + f"### `{sample_function.__name__}()`\n" + "\n" + f"{inspect.cleandoc(docstring)}\n" "\n" - f"{code}" + "```python\n" + f"{code}\n" + "```" ) @@ -185,24 +182,24 @@ def main(args): with open(args.output_path, "w") as notebook: json.dump(notebook_objects, notebook, indent=1) - # Case of a reST page: Print the header and sections to the file + # Case of a Markdown page: Print the header and sections to the file else: - with open(args.output_path, "w") as rest_page: - print(create_rest_page_header(script_name), file=rest_page) + with open(args.output_path, "w") as md_page: + print(create_markdown_page_header(script_name), file=md_page) for sample_method in samples.exported_samples: - print(create_rest_page_section(sample_method), file=rest_page) + print(create_markdown_page_section(sample_method), file=md_page) if __name__ == "__main__": parser = argparse.ArgumentParser( prog="python convert_samples.py", formatter_class=argparse.RawTextHelpFormatter, - description="Transforms the samples.py script to a notebook or reST page", + description="Transforms the samples.py script to a notebook or Markdown page", ) parser.add_argument("samples_dir", metavar="PYFILE", help="samples scripts dir") parser.add_argument("output_path", metavar="OUTFILE", help="output file") parser.add_argument("--sklearn", action="store_true", default=False) parser.add_argument( - "-f", "--format", type=str, choices=["ipynb", "rst"], default="ipynb" + "-f", "--format", type=str, choices=["ipynb", "md"], default="ipynb" ) main(parser.parse_args()) diff --git a/doc/convert_tutorials.py b/doc/util/convert_tutorials.py similarity index 55% rename from doc/convert_tutorials.py rename to doc/util/convert_tutorials.py index 2f9d6e86..4d07952d 100644 --- a/doc/convert_tutorials.py +++ b/doc/util/convert_tutorials.py @@ -4,17 +4,19 @@ # which is available at https://spdx.org/licenses/BSD-3-Clause-Clear.html or # # see the "LICENSE.md" file for more details. # ###################################################################################### -"""Converts the Jupyter notebooks of the Khiops Python tutorial to reST""" +"""Converts the Jupyter notebooks of the Khiops Python tutorial to Markdown""" import argparse import glob import os +import subprocess import sys +from pathlib import Path import nbformat from jupyter_client import KernelManager -from nbconvert import NotebookExporter, RSTExporter -from nbconvert.preprocessors import ExecutePreprocessor +from nbconvert import MarkdownExporter, NotebookExporter +from nbconvert.preprocessors import CellExecutionError, ExecutePreprocessor from nbformat import notebooknode as nbnode @@ -28,6 +30,10 @@ def main(args): os.makedirs(args.output_dir, exist_ok=True) abs_output_dir = os.path.abspath(args.output_dir) + # Store the repository root for the kernel's sys.path (must be computed + # before the chdir) + repository_root = str(Path(__file__).resolve().parents[2]) + # Save and change the current directory to that of the notebooks initial_working_dir = os.getcwd() os.chdir(args.tutorial_dir) @@ -38,58 +44,61 @@ def main(args): os.path.splitext(os.path.basename(path))[0] for path in notebook_paths ] - # Execute each notebook and convert it to reST if specified + # Execute each notebook and convert it to Markdown if specified if args.execute_notebooks: # Set up one kernel for all executions kernel_manager = KernelManager(kernel_name="python3") - kernel_manager.start_kernel() + + # Do not print errors + kernel_manager.start_kernel(stderr=subprocess.DEVNULL) preprocessor = ExecutePreprocessor(km=kernel_manager) for notebook_path, notebook_name in zip(notebook_paths, notebook_names): print(f"Processing {notebook_path}") with open(notebook_path, encoding="utf8") as notebook_file: notebook = nbformat.read(notebook_file, 4) - notebook_exporter = NotebookExporter() - rst_exporter = RSTExporter() - export_setups = [(rst_exporter, "rst"), (notebook_exporter, "ipynb")] - - for exporter, file_ext in export_setups: - # Add a setup cell for rest - # - Disables the html output when displaying dataframes - # - Adds "../.." to the sys path - setup_source = "import sys\n" 'sys.path.append("../..")\n' - if file_ext == "rst": - setup_source += ( - "import pandas as pd\n" - 'pd.set_option("display.notebook_repr_html", False)\n' - ) - - setup_cell_dict = { + + # Add setup cell (sys.path + disable HTML dataframes) + setup_source = ( + "import sys\n" + f'sys.path.append("{repository_root}")\n' + "import pandas as pd\n" + 'pd.set_option("display.notebook_repr_html", False)\n' + ) + setup_cell = nbnode.from_dict( + { "cell_type": "code", "execution_count": None, "metadata": {}, "outputs": [], "source": setup_source, } - setup_cell = nbnode.from_dict(setup_cell_dict) - notebook.cells.insert(0, setup_cell) - - # Execute the notebook to obtain the output cells - preprocessor.preprocess(notebook, {}) - - # Eliminate the setup cell - notebook.cells.pop(0) - - # Execute the notebook and write output - output_file_path = os.path.join( - abs_output_dir, f"{notebook_name}.{file_ext}" - ) - print(f"Writing file {output_file_path}") - with open(output_file_path, "w", encoding="utf8") as output_file: - body, _ = exporter.from_notebook_node(notebook) - if file_ext == "rst": - output_file.write(":orphan:\n\n") - output_file.write(body) + ) + notebook.cells.insert(0, setup_cell) + + # Execute the notebook once + try: + preprocessor.preprocess(notebook, {}, km=kernel_manager) + except CellExecutionError: + print(f"WARNING: '{notebook_path}' had execution" f" errors") + + # Remove the setup cell from the executed notebook + notebook.cells.pop(0) + + # Export as Markdown (with text-only dataframe outputs) + md_path = os.path.join(abs_output_dir, f"{notebook_name}.md") + print(f"Writing file {md_path}") + with open(md_path, "w", encoding="utf8") as output_file: + body, _ = MarkdownExporter().from_notebook_node(notebook) + output_file.write(body) + + # Export as .ipynb (same executed outputs) + nb_path = os.path.join(abs_output_dir, f"{notebook_name}.ipynb") + print(f"Writing file {nb_path}") + with open(nb_path, "w", encoding="utf8") as output_file: + body, _ = NotebookExporter().from_notebook_node(notebook) + output_file.write(body) + kernel_manager.shutdown_kernel(now=True) # Restore the initial current directory @@ -98,40 +107,38 @@ def main(args): # Define the message creator local function def _tutorials_message(module_name): return ( - "These " - f":download:`Jupyter notebook tutorials <{module_name}_tutorials.zip>` " - f"cover the basic usage of the ``{module_name}`` Khiops sub-module. The " + f"These [Jupyter notebook tutorials]({module_name}_tutorials.zip) " + f"cover the basic usage of the `{module_name}` Khiops sub-module. The " "solution notebooks are " - f":download:`available here <{module_name}_tutorials_solutions.zip>` " + f"[available here]({module_name}_tutorials_solutions.zip) " "or you can browse them in this page:\n\n" ) # Write the tutorial page sklearn_tutorials = [name for name in notebook_names if name.startswith("Sklearn")] core_tutorials = [name for name in notebook_names if name.startswith("Core")] - tutorials_file_path = os.path.join(abs_output_dir, "index.rst") + tutorials_file_path = os.path.join(abs_output_dir, "index.md") with open(tutorials_file_path, "w", encoding="utf8") as tutorials_file: - tutorials_file.write("Tutorials\n") - tutorials_file.write("=========\n") + tutorials_file.write("# Tutorials\n") + tutorials_file.write("\n") + tutorials_file.write("## Sklearn\n") tutorials_file.write("\n") - tutorials_file.write("Sklearn\n") - tutorials_file.write("-------\n") tutorials_file.write(_tutorials_message("sklearn")) for name in sklearn_tutorials: - tutorials_file.write(f"- :doc:`{name}`\n") + tutorials_file.write(f"- [{name}]({name}.md)\n") + tutorials_file.write("\n") + tutorials_file.write("## Core\n") tutorials_file.write("\n") - tutorials_file.write("Core\n") - tutorials_file.write("----\n") tutorials_file.write(_tutorials_message("core")) for name in core_tutorials: - tutorials_file.write(f"- :doc:`{name}`\n") + tutorials_file.write(f"- [{name}]({name}.md)\n") if __name__ == "__main__": parser = argparse.ArgumentParser( prog="python convert_tutorial.py", formatter_class=argparse.RawTextHelpFormatter, - description="Converts the tutorial notebooks to a reST page", + description="Converts the tutorial notebooks to a Markdown page", ) parser.add_argument( "tutorial_dir", diff --git a/doc/create-doc b/doc/util/create-doc similarity index 65% rename from doc/create-doc rename to doc/util/create-doc index 0578e8d7..dc5da0d0 100755 --- a/doc/create-doc +++ b/doc/util/create-doc @@ -3,6 +3,7 @@ set -euo pipefail # Obtain this script's dir SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +SITE_DIR="${SCRIPT_DIR}/../site" # Default parameter values TRANSFORM_NOTEBOOKS="" @@ -10,19 +11,21 @@ DOWNLOAD_REPO="" DEFAULT_KHIOPS_TUTORIAL_REPO_URL="https://github.com/KhiopsML/khiops-python-tutorial.git" DEFAULT_KHIOPS_TUTORIAL_REPO_REF="main" DEFAULT_KHIOPS_TUTORIAL_DIR="${SCRIPT_DIR}/khiops-python-tutorial" +PREPARE_ONLY="" # Function to display the usage help usage() { echo "Usage: create-doc [-r REPO_URL] [-d] [-t] [-g] [-l]" echo "Options:" echo " -d: Downloads the Khiops tutorial repository. Implies -t. See also -r." - echo " -t: Transform the Khiops Jupyter notebooks tutorials into reST." + echo " -t: Transform the Khiops Jupyter notebooks tutorials into Markdown." echo " -r: Set the Khiops tutorial repository URL. The default is" echo " '$DEFAULT_KHIOPS_TUTORIAL_REPO_URL'." echo " -g: Set the Khiops tutorial repository Git reference. The default is" echo " '$DEFAULT_KHIOPS_TUTORIAL_REPO_REF'." echo " -l: Directory of the local copy of the khiops tutorial repository. The default is" echo " '$DEFAULT_KHIOPS_TUTORIAL_DIR'." + echo " -p: Prepare only - skip the final Zensical build." echo "" } @@ -32,7 +35,7 @@ exit_bad() { } # Read command line arguments -while getopts "dtrg:l:" opt +while getopts "dtr:g:l:p" opt do case "$opt" in d ) DOWNLOAD_REPO=true && TRANSFORM_NOTEBOOKS="true" ;; @@ -40,6 +43,7 @@ do r ) KHIOPS_TUTORIAL_REPO_URL="$OPTARG" ;; g ) KHIOPS_TUTORIAL_REPO_REF="$OPTARG" ;; l ) KHIOPS_TUTORIAL_REPO_DIR="$OPTARG" ;; + p ) PREPARE_ONLY="true" ;; * ) exit_bad ;; esac done @@ -47,17 +51,8 @@ KHIOPS_TUTORIAL_REPO_URL="${KHIOPS_TUTORIAL_REPO_URL:-$DEFAULT_KHIOPS_TUTORIAL_R KHIOPS_TUTORIAL_REPO_REF="${KHIOPS_TUTORIAL_REPO_REF:-$DEFAULT_KHIOPS_TUTORIAL_REPO_REF}" KHIOPS_TUTORIAL_REPO_DIR="${KHIOPS_TUTORIAL_REPO_DIR:-$DEFAULT_KHIOPS_TUTORIAL_DIR}" - -# Add the khiops directory to the Python path -if [[ -z "${PYTHONPATH+x}" ]] -then - export PYTHONPATH=".." -else - export PYTHONPATH="$PYTHONPATH:.." -fi - # Check command existence -command_requirements="tar python make zip" +command_requirements="zip" if [[ $DOWNLOAD_REPO ]] then command_requirements="$command_requirements git" @@ -81,13 +76,13 @@ then && rm -rf "$KHIOPS_TUTORIAL_REPO_DIR/.git" fi -# Convert tutorials to reST -tutorials_dir="$(realpath ./tutorials)" -mkdir -p "$tutorials_dir" +# Convert tutorials to Markdown +mkdir -p "${SITE_DIR}/tutorials" +tutorials_dir="$(realpath "${SITE_DIR}/tutorials")" if [[ $TRANSFORM_NOTEBOOKS ]] then - echo "Creating reST tutorial pages" - python convert_tutorials.py \ + echo "Creating Markdown tutorial pages" + uv run --active --frozen --no-sync "${SCRIPT_DIR}/convert_tutorials.py" \ --execute-notebooks "$KHIOPS_TUTORIAL_REPO_DIR" "$tutorials_dir" fi @@ -100,7 +95,7 @@ touch exercises/.dummy # Create a dummy so the "exercises" directory is created zip "core_tutorials_solutions.zip" Core*.ipynb data/*/* exercises/.dummy zip "sklearn_tutorials_solutions.zip" Sklearn*.ipynb data/*/* exercises/.dummy cd "$KHIOPS_TUTORIAL_REPO_DIR" -python create-coursework.py +uv run --active --frozen --no-sync create-coursework.py cd coursework mkdir -p exercises touch exercises/.dummy # Create a dummy so the "exercises" directory is created on unzip @@ -108,6 +103,28 @@ zip "$tutorials_dir/core_tutorials.zip" Core*.ipynb data/*/* exercises/.dummy zip "$tutorials_dir/sklearn_tutorials.zip" Sklearn*.ipynb data/*/* exercises/.dummy cd "../.." -# Create the documentation with Sphinx -echo "Executing Sphinx" -sphinx-build -M html . _build/ +# Copy source scripts and notebooks to doc/samples/ for download links +echo "Copying sample scripts and notebooks" +SAMPLES_DIR="$SCRIPT_DIR/../../khiops/samples" +cp -f "$SAMPLES_DIR/samples.py" "${SITE_DIR}/samples/" +cp -f "$SAMPLES_DIR/samples.ipynb" "${SITE_DIR}/samples/" +cp -f "$SAMPLES_DIR/samples_sklearn.py" "${SITE_DIR}/samples/" +cp -f "$SAMPLES_DIR/samples_sklearn.ipynb" "${SITE_DIR}/samples/" + +# Build the documentation with Zensical (unless prepare-only) +if [[ ! $PREPARE_ONLY ]]; then + echo "Executing Zensical" + cd "$SCRIPT_DIR/../.." + + # Inject the khiops version into zensical.toml (replaces the old + # mkdocs hooks-based mechanism, which Zensical does not support) + VERSION=$(uv run --active --frozen --no-sync python -c "import khiops; print(khiops.__version__)") + awk -v v="$VERSION" ' + /^site_name = / { print "site_name = \"Khiops Python " v "\""; next } + { print } + ' zensical.toml > zensical.toml.tmp && mv zensical.toml.tmp zensical.toml + uv run --active --frozen --no-sync zensical build --clean --strict + + # Restore zensical.toml to its previous state + git checkout -- zensical.toml +fi diff --git a/doc/util/requirements.txt b/doc/util/requirements.txt new file mode 100644 index 00000000..8401caea --- /dev/null +++ b/doc/util/requirements.txt @@ -0,0 +1,7 @@ +zensical>=0.0.51,<=0.0.55 +mkdocstrings[python]>=0.25.0 +ipykernel>=6.9.1 +nbconvert>=7.0.0 +nbformat>=5.3.0 +pandas>=2.3.3,<4.0.0 +scikit-learn>=1.7.2,<1.9.0 diff --git a/khiops/__init__.py b/khiops/__init__.py index 2b1584ca..99ab9e06 100644 --- a/khiops/__init__.py +++ b/khiops/__init__.py @@ -18,7 +18,7 @@ (extension ".kdicj") - core/analysis_results: Classes to inspect Khiops JSON report files (extension ".khj") -- core/coclustering_results: Classes to instpect Khiops Coclustering report files +- core/coclustering_results: Classes to inspect Khiops Coclustering report files (extension ".khcj") - sklearn: Scikit-Learn estimator classes to learn and use Khiops models """ diff --git a/khiops/core/analysis_results.py b/khiops/core/analysis_results.py index ec3f1b78..5a5f8098 100644 --- a/khiops/core/analysis_results.py +++ b/khiops/core/analysis_results.py @@ -12,75 +12,83 @@ are mostly compositions (has-a relations) and we omit native attributes (str, int, float, etc). -The main class of this module is `AnalysisResults` and it is largely a -composition of sub-reports objects given by the following structure:: - - AnalysisResults - |- preparation_report | - |- text_preparation_report |-> PreparationReport - |- tree_preparation_report | - |- bivariate_preparation_report -> BivariatePreparationReport - |- modeling_report -> ModelingReport - |- train_evaluation_report | - |- test_evaluation_report |-> EvaluationReport - |- evaluation_report | +The main class of this module is [AnalysisResults][] and it is largely a +composition of sub-reports objects given by the following structure: + +```text +AnalysisResults +|- preparation_report | +|- text_preparation_report |-> PreparationReport +|- tree_preparation_report | +|- bivariate_preparation_report -> BivariatePreparationReport +|- modeling_report -> ModelingReport +|- train_evaluation_report | +|- test_evaluation_report |-> EvaluationReport +|- evaluation_report | +``` These sub-classes in turn use other tertiary classes to represent specific information -pieces of each report. The dependencies for the classes `PreparationReport` and -`BivariatePreparationReport` are:: +pieces of each report. The dependencies for the classes [PreparationReport][] and +[BivariatePreparationReport][] are: - PreparationReport - |- variables_statistics -> list of VariableStatistics - |- trees -> list of Tree (only for tree_preparation_report) +```text +PreparationReport +|- variables_statistics -> list of VariableStatistics +|- trees -> list of Tree (only for tree_preparation_report) - BivariatePreparationReport - |- variable_pair_statistics -> list of VariablePairStatistics +BivariatePreparationReport +|- variable_pair_statistics -> list of VariablePairStatistics - VariableStatistics - |- data_grid -> DataGrid - |- modl_histograms -> ModlHistograms +VariableStatistics +|- data_grid -> DataGrid +|- modl_histograms -> ModlHistograms - VariablePairStatistics - |- data_grid -> DataGrid +VariablePairStatistics +|- data_grid -> DataGrid - Tree - |- target_partition -> TargetPartition - |- nodes -> list of TreeNode +Tree +|- target_partition -> TargetPartition +|- nodes -> list of TreeNode - TargetPartition - |- partition -> list of PartInterval +TargetPartition +|- partition -> list of PartInterval - DataGrid - |- dimensions -> list of DataGridDimension +DataGrid +|- dimensions -> list of DataGridDimension - ModlHistograms - |- histograms -> list of Histogram +ModlHistograms +|- histograms -> list of Histogram - DataGridDimension - |- partition -> list of PartInterval OR - | list of PartValue OR - | list of PartValueGroup +DataGridDimension +|- partition -> list of PartInterval OR +| list of PartValue OR +| list of PartValueGroup +``` -for class `ModelingReport`:: +for class [ModelingReport][]: - ModelingReport - |- trained_predictors -> list of TrainedPredictors +```text +ModelingReport +|- trained_predictors -> list of TrainedPredictors - TrainedPredictor - |- selected_variables -> list of SelectedVariable +TrainedPredictor +|- selected_variables -> list of SelectedVariable +``` -and for class `EvaluationReport`:: +and for class [EvaluationReport][]: - EvaluationReport - |- predictors_performance -> list of PredictorPerformance - |- classification_lift_curves -> list of PredictorCurve (classification only) - |- regression_rec_curves -> list of PredictorCurve (regression only) +```text +EvaluationReport +|- predictors_performance -> list of PredictorPerformance +|- classification_lift_curves -> list of PredictorCurve (classification only) +|- regression_rec_curves -> list of PredictorCurve (regression only) - PredictorPerformance - |- confusion_matrix -> ConfusionMatrix (classification only) +PredictorPerformance +|- confusion_matrix -> ConfusionMatrix (classification only) +``` To have a complete illustration of the access to the information of all classes in this -module look at their ``to_dict`` methods which write Python dictionaries in the +module look at their `to_dict` methods which write Python dictionaries in the same format as the Khiops JSON reports. """ import io @@ -98,7 +106,7 @@ class AnalysisResults(KhiopsJSONObject): """Main class containing the information of a Khiops JSON file - Sub-reports not available in the JSON data are optional (set to ``None``). + Sub-reports not available in the JSON data are optional (set to `None`). Parameters ---------- @@ -106,8 +114,9 @@ class AnalysisResults(KhiopsJSONObject): A dictionary representing the data of a Khiops JSON report file. If not specified it returns an empty instance. - .. note:: - See also the `.read_analysis_results_file` function to obtain an instance + !!! note + + See also the [read_analysis_results_file][] function to obtain an instance of this class from a Khiops JSON file. Attributes @@ -124,22 +133,23 @@ class AnalysisResults(KhiopsJSONObject): 2-tuples linking each sub-task name to a list containing the warnings and errors found during the execution of that sub-task. Available only if there were errors or warnings. - preparation_report : `PreparationReport` + preparation_report : PreparationReport A report about the variables' discretizations and groupings. - bivariate_preparation_report : `BivariatePreparationReport`, optional + bivariate_preparation_report : BivariatePreparationReport, optional A report of the grid models created from pairs of variables. Available only when pair of variables were created in the analysis. - modeling_report : `ModelingReport` + modeling_report : ModelingReport A report describing the predictor models. Available only in supervised analysis. - train_evaluation_report : `EvaluationReport` + train_evaluation_report : EvaluationReport An evaluation report of the trained models on the *train* dataset split. Available only in supervised analysis. - test_evaluation_report : `EvaluationReport` + test_evaluation_report : EvaluationReport An evaluation report of the trained models on the *test* dataset split. Available only in supervised analysis and when the *test* split was not empty. - evaluation_report : `EvaluationReport` - An `EvaluationReport` instance for evaluations created with an explicit - evaluation (either with the `~.api.evaluate_predictor` core API function or the + evaluation_report : EvaluationReport + An [EvaluationReport][] instance for evaluations created with an explicit + evaluation (either with the [api.evaluate_predictor][] core API function or + the *Evaluate Predictor* feature of the Khiops desktop app). Available only when the report was generated with the aforementioned features. """ @@ -231,9 +241,10 @@ def get_reports(self): def write_report_file(self, report_file_path): # pragma: no cover """Writes a TSV report file with the object's information - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- @@ -280,13 +291,14 @@ def to_dict(self): def write_report(self, stream_or_writer): # pragma: no cover """Writes the instance's TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - stream_or_writer : `io.IOBase` or `.KhiopsOutputWriter` + stream_or_writer : io.IOBase or KhiopsOutputWriter Output stream or writer. """ # Warn the user that this method is deprecated and will be removed @@ -335,12 +347,12 @@ def read_analysis_results_file(json_file_path): Returns ------- - `.AnalysisResults` + AnalysisResults An instance of AnalysisResults containing the report's information. Examples -------- - See the following functions of the ``samples.py`` documentation script: + See the following functions of the `samples.py` documentation script: - `samples.access_predictor_evaluation_report()` - `samples.train_predictor_with_cross_validation()` - `samples.multiple_train_predictor()` @@ -357,7 +369,7 @@ class PreparationReport: Parameters ---------- json_data : dict, optional - JSON data of the ``preparationReport`` field of a + JSON data of the `preparationReport` field of a Khiops JSON report file. If not specified it returns an empty instance. Attributes @@ -369,7 +381,7 @@ class PreparationReport: variable_types : list of str The different types of variables. variable_numbers : list of int - Number of variables for each type. Synchronized with ``variable_types``. + Number of variables for each type. Synchronized with `variable_types`. database : str Path of the main training data table file. sample_percentage : int @@ -379,7 +391,7 @@ class PreparationReport: selection_variable : str Name of the variable used to select training instances. selection_value : str - Value of ``selection_variable`` to select training instance. + Value of `selection_variable` to select training instance. constructed_variable_number : int Number of constructed variables. instance_number : int @@ -413,7 +425,7 @@ class PreparationReport: target_values : list of str Values of a categorical target variable. target_value_frequencies : list of int - Frequencies for each target value. Synchronized with ``target_values``. + Frequencies for each target value. Synchronized with `target_values`. evaluated_variable_number : int Number of variables analyzed. informative_variable_number : int @@ -440,9 +452,9 @@ class PreparationReport: Coding length of the null preparation model. null_model_data_cost : float Coding length of the data given the null model. - variables_statistics : list of `VariableStatistics` + variables_statistics : list of VariableStatistics Variable statistics for each variable analyzed. - trees : list of `Tree` + trees : list of Tree Tree details for each tree built. """ @@ -574,12 +586,12 @@ def get_variable_statistics(self, variable_name): Returns ------- - `VariableStatistics` + VariableStatistics The statistics of the specified variable. Raises ------ - `KeyError` + KeyError If no variable with the specified names exist. """ return self._variables_statistics_by_name[variable_name] @@ -594,12 +606,12 @@ def get_tree(self, tree_name): Returns ------- - `Tree` + Tree The tree which has the specified name. Raises ------ - `KeyError` + KeyError If no tree with the specified name exists. """ return self._trees_by_name[tree_name] @@ -756,14 +768,15 @@ def to_dict(self): def write_report(self, writer): # pragma: no cover """Writes the instance's TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -878,7 +891,7 @@ class BivariatePreparationReport: Parameters ---------- json_data : dict, optional - JSON data of the ``bivariatePreparationReport`` field of a Khiops JSON report + JSON data of the `bivariatePreparationReport` field of a Khiops JSON report file. If not specified it returns an empty instance. Attributes @@ -890,7 +903,7 @@ class BivariatePreparationReport: variable_types : list of str The different types of variables. variable_numbers : list of int - The number of variables for each type in ``variables_types`` (synchronized + The number of variables for each type in `variables_types` (synchronized lists). database : str Path of the main training data table file. @@ -920,7 +933,7 @@ class BivariatePreparationReport: target_values : list of str Values of a categorical target variable. target_value_frequencies : list of int - Frequencies for each value in ``target_values`` (synchronized lists). + Frequencies for each value in `target_values` (synchronized lists). evaluated_pair_number : int Number of variable pairs evaluated. selected_pair_number : int @@ -928,7 +941,7 @@ class BivariatePreparationReport: informative_pair_number : int Number of informative variable pairs. A pair is considered informative if its level is greater than the sum of its components' levels. - variable_pair_statistics : list of `VariablePairStatistics` + variable_pair_statistics : list of VariablePairStatistics Statistics for each analyzed pair of variables. """ @@ -1031,7 +1044,8 @@ def get_variable_pair_names(self): def get_variable_pair_statistics(self, variable_name_1, variable_name_2): """Returns the statistics of the specified pair of variables - .. note:: + !!! note + The variable names can be given in any order. Parameters @@ -1043,12 +1057,12 @@ def get_variable_pair_statistics(self, variable_name_1, variable_name_2): Returns ------- - `VariablePairStatistics` + VariablePairStatistics The statistics of the specified pair of variables. Raises ------ - `KeyError` + KeyError If no pair with the specified names exist. """ return self._variables_pairs_statistics_by_name[ @@ -1143,14 +1157,15 @@ def to_dict(self): def write_report(self, writer): # pragma: no cover """Writes the instance's TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -1216,7 +1231,7 @@ class ModelingReport: Parameters ---------- json_data : dict, optional - JSON data of the ``modelingReport`` field of Khiops JSON report file. If not + JSON data of the `modelingReport` field of Khiops JSON report file. If not specified it returns an empty instance. Attributes @@ -1234,14 +1249,14 @@ class ModelingReport: selection_variable : str Variable used to select instances for training. selection_value : str - Value of ``selection_variable`` to select instances for training. + Value of `selection_variable` to select instances for training. learning_task : "Classification analysis" or "Regression analysis" Name of the associated learning task. target_variable : str Name of the target variable. main_target_value : str Main value of the target variable. - trained_predictors : list of `TrainedPredictor` + trained_predictors : list of TrainedPredictor The predictors trained in the task. """ @@ -1305,12 +1320,12 @@ def get_predictor(self, predictor_name): Returns ------- - `TrainedPredictor` + TrainedPredictor The predictor object for the specified name. Raises ------ - `KeyError` + KeyError If there is no predictor with the specified name. """ return self._trained_predictors_by_name[predictor_name] @@ -1320,12 +1335,12 @@ def get_snb_predictor(self): Returns ------- - `TrainedPredictor` + TrainedPredictor The predictor object for "Selective Naive Bayes". Raises ------ - `KeyError` + KeyError If there is no predictor named "Selective Naive Bayes". """ return self.get_predictor("Selective Naive Bayes") @@ -1380,14 +1395,15 @@ def to_dict(self): def write_report(self, writer): # pragma: no cover """Writes the instance's TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -1431,15 +1447,16 @@ class EvaluationReport: ---------- json_data : dict, optional JSON data of the fields: - - ``trainEvaluationReport``: predictor training - - ``testEvaluationReport``: predictor training & non-empty test split - - ``evaluationReport``: explicit evaluation + - `trainEvaluationReport`: predictor training + - `testEvaluationReport`: predictor training & non-empty test split + - `evaluationReport`: explicit evaluation The first two fields are set when doing a supervised analysis: either with the - "Train Model" feature of the Khiops app or the `~.api.train_predictor` function + "Train Model" feature of the Khiops app or the [api.train_predictor][] + function of the Khiops Python core API. The third field is set when doing an explicit evaluation: either with the *Evaluate Predictor* feature of the Khiops app or - the `~.api.evaluate_predictor` function of the Khiops Python core API. + the [api.evaluate_predictor][] function of the Khiops Python core API. If not specified it returns an empty instance. @@ -1469,14 +1486,14 @@ class EvaluationReport: Name of the target variable. main_target_value : str Main value of the target variable. - predictors_performance : list of `PredictorPerformance` + predictors_performance : list of PredictorPerformance Performance metrics for each predictor. - regression_rec_curves : list of `PredictorCurve` + regression_rec_curves : list of PredictorCurve REC curves for each regressor. classification_target_values : list of str Target variable values for which a classifier lift curve was evaluated. - classification_lift_curves : list of `PredictorCurve` - Lift curves for each target value in ``classification_target_values``. The lift + classification_lift_curves : list of PredictorCurve + Lift curves for each target value in `classification_target_values`. The lift curve for the optimal predictor is prepended to those of the target values. """ @@ -1587,12 +1604,12 @@ def get_predictor_performance(self, predictor_name): Returns ------- - `PredictorPerformance` + PredictorPerformance The performance metrics for the specified predictor. Raises ------ - `KeyError` + KeyError If no predictor with the specified name exists. """ return self._predictors_performance_by_name[predictor_name] @@ -1602,12 +1619,12 @@ def get_snb_performance(self): Returns ------- - `PredictorPerformance` + PredictorPerformance The performance metrics for the Selective Naive Bayes predictor. Raises ------ - `ValueError` + ValueError If the Selective Naive Bayes information is not available in the report. """ if "Selective Naive Bayes" not in self._predictors_performance_by_name: @@ -1624,14 +1641,14 @@ def get_regressor_rec_curve(self, regressor_name): Returns ------- - `PredictorCurve` + PredictorCurve The REC curve for the specified regressor. Raises ------ - `ValueError` + ValueError If no regressor curves available. ( - `KeyError` + KeyError If no regressor with the specified name exists. """ if self.learning_task != "Regression analysis": @@ -1646,12 +1663,12 @@ def get_snb_rec_curve(self): Returns ------- - `PredictorCurve` + PredictorCurve The REC curve for the Selective Naive Bayes regressor. Raises ------ - `ValueError` + ValueError If the Selective Naive Bayes information is not available in the report. """ if self.learning_task != "Regression analysis": @@ -1673,12 +1690,12 @@ def get_classifier_lift_curve(self, classifier_name, target_value): Returns ------- - `PredictorCurve` + PredictorCurve The lift curve for the specified classifier and target value. Raises ------ - `KeyError` + KeyError If no classifier with the specified exists or no target value with the specified name exists. """ @@ -1714,15 +1731,15 @@ def get_snb_lift_curve(self, target_value): Returns ------- - `PredictorCurve` + PredictorCurve The lift curve of the Selective Naive Bayes classifier for the specified target value. Raises ------ - `ValueError` + ValueError If the Selective Naive Bayes classifier information is not available. - `KeyError` + KeyError If no target value with the specified name exists. """ if self.learning_task != "Classification analysis": @@ -1813,14 +1830,15 @@ def to_dict(self): def write_report(self, writer): # pragma: no cover """Writes the instance's TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer object. """ # Warn the user that this method is deprecated and will be removed @@ -1917,7 +1935,8 @@ def write_report(self, writer): # pragma: no cover class VariableStatistics: """Variable information and statistics - .. note:: + !!! note + The statistics in this class are for both numerical and categorical data. @@ -1925,14 +1944,15 @@ class VariableStatistics: ---------- json_data : dict, optional JSON data of an element of the list found at the - ``variablesStatistics`` field within the ``preparationReport`` field of a Khiops + `variablesStatistics` field within the `preparationReport` field of a Khiops JSON report file. If not specified it returns an empty instance. - .. note:: - The ``data_grid`` field is considered a "detail" and is not initialized in + !!! note + + The `data_grid` field is considered a "detail" and is not initialized in the constructor. Instead, it is initialized explicitly via the - ``init_details`` method. This allows to make partial initializations for + [init_details][] method. This allows to make partial initializations for large reports. If not specified it returns an empty instance. @@ -1975,7 +1995,7 @@ class VariableStatistics: Different values taken by the variable. If there are too many values only the more frequent will be available. input_value_frequencies : list of int - The frequencies for each input value. Synchronized with ``input_values``. + The frequencies for each input value. Synchronized with `input_values`. construction_cost : float Construction cost of the variable. More complex variables cost more. preparation_cost : float @@ -1985,10 +2005,10 @@ class VariableStatistics: construction model. derivation_rule : str If the variable is not native it is Khiops dictionary function to derive it. - Otherwise is set to ``None``. - data_grid : `DataGrid` + Otherwise is set to `None`. + data_grid : DataGrid A density estimation of the partitioned variable with respect to the target. - modl_histograms : `ModlHistograms` + modl_histograms : ModlHistograms MODL optimal histograms for for numerical variables. Only for unsupervised analysis. """ @@ -2054,7 +2074,7 @@ def init_details(self, json_data=None): ---------- json_data : dict, optional JSON data of an element of the list found at the - ``variablesDetailedStatistics`` field within the ``preparationReport`` field + `variablesDetailedStatistics` field within the `preparationReport` field of a Khiops JSON report file. If not specified it leaves the object as-is. """ @@ -2174,14 +2194,15 @@ def write_report_header_line(self, writer): # pragma: no cover The header is the same for all variable types. - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -2213,14 +2234,15 @@ def write_report_header_line(self, writer): # pragma: no cover def write_report_line(self, writer): # pragma: no cover """Writes a line of the TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -2287,14 +2309,15 @@ def write_report_line(self, writer): # pragma: no cover def write_report_details(self, writer): # pragma: no cover """Writes the details' attributes into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -2329,14 +2352,16 @@ class VariablePairStatistics: Parameters ---------- json_data : dict, optional - JSON data of an element of the list found at the ``variablesPairStatistics`` - field within the ``bivariatePreparationReport`` field of a Khiops JSON report + JSON data of an element of the list found at the `variablesPairStatistics` + field within the `bivariatePreparationReport` field of a Khiops JSON report file. If not specified it returns an empty instance. - .. note:: - The ``data_grid`` field is considered as "detail" and is not initialized in + !!! note + + The `data_grid` field is considered as "detail" and is not initialized in the constructor. Instead, it is initialized explicitly via the - `init_details` method. This allows to make partial initializations for large + [init_details][] method. This allows to make partial initializations for + large reports. If not specified it returns an empty instance. @@ -2356,7 +2381,7 @@ class VariablePairStatistics: Predictive importance of the second variable. delta_level : float Difference between the pair's level and the sum of those of its components - (``delta_level = level - level1 - level2``). + (`delta_level = level - level1 - level2`). variable_number : int Number of active variables in the pair: - 0 means that there is no information in any of the variables @@ -2376,7 +2401,7 @@ class VariablePairStatistics: data_cost : float *Advanced:* Negative log-likelihood of the variable given a preparation model and a construction model. - data_grid : `DataGrid` + data_grid : DataGrid A density estimation of the partitioned pair of variable with respect to the target. """ @@ -2419,8 +2444,8 @@ def init_details(self, json_data=None): ---------- json_data : dict, optional JSON data of an element of the list found at - the ``variablesPairsDetailedStatistics`` field within the - ``bivariatePreparationReport`` field of a Khiops JSON report file. If not + the `variablesPairsDetailedStatistics` field within the + `bivariatePreparationReport` field of a Khiops JSON report file. If not specified it leaves the object as-is. """ # Check the type of json_data @@ -2483,14 +2508,15 @@ def write_report_header_line(self, writer): # pragma: no cover The header is the same for all variable types. - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -2527,14 +2553,15 @@ def write_report_header_line(self, writer): # pragma: no cover def write_report_line(self, writer): # pragma: no cover """Writes a line of the TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -2569,14 +2596,15 @@ def write_report_line(self, writer): # pragma: no cover def write_report_details(self, writer): # pragma: no cover """Writes the details' attributes into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -2597,7 +2625,7 @@ class Tree: ---------- json_data : dict, optional JSON data of a value associated to the rank key in the object found at the - ``treeDetails`` field within the ``treePreparationReport`` field of a Khiops + `treeDetails` field within the `treePreparationReport` field of a Khiops JSON report file. If not specified, it returns an empty instance. Attributes @@ -2608,9 +2636,9 @@ class Tree: Number of variables in the tree. depth : int Depth of the tree. - target_partition : `TargetPartition` + target_partition : TargetPartition Summary of the target partition. For regression only. - nodes: list of `TreeNode` + nodes: list of TreeNode Nodes of the tree. """ @@ -2673,8 +2701,8 @@ class TargetPartition: Parameters ---------- json_data : dict, optional - JSON data of the ``targetPartition`` field of the ``treeDetails`` field of the - ``treePreparationReport`` field of a Khiops JSON report file. If not specified + JSON data of the `targetPartition` field of the `treeDetails` field of the + `treePreparationReport` field of a Khiops JSON report file. If not specified it returns an empty instance. Attributes @@ -2686,8 +2714,8 @@ class TargetPartition: partition_type : "Intervals" (only possible value) Partition type. partition : list - The dimension parts. The list objects are of type `PartInterval`, as - ``partition_type`` is "Intervals" + The dimension parts. The list objects are of type [PartInterval][], as + `partition_type` is "Intervals" frequencies : list of int Frequencies of the intervals in the target partition. """ @@ -2762,22 +2790,22 @@ class TreeNode: json_data : dict, optional JSON data of either: - - the ``treeNodes`` field of the ``treeDetails`` field of the - ``treePreparationReport`` field of a Khiops JSON report file, or - - an element of the ``childNodes`` field of the ``treeNodes`` field of the - ``treeDetails`` field of the ``treePreparationReport`` field of a Khiops + - the `treeNodes` field of the `treeDetails` field of the + `treePreparationReport` field of a Khiops JSON report file, or + - an element of the `childNodes` field of the `treeNodes` field of the + `treeDetails` field of the `treePreparationReport` field of a Khiops JSON report file. If not specified it returns an empty instance parent_id : str, optional - Identifier of the parent ``TreeNode`` instance. Not set for "root" nodes. + Identifier of the parent `TreeNode` instance. Not set for "root" nodes. Attributes ---------- id : str - Identifier of the ``TreeNode`` instance. + Identifier of the `TreeNode` instance. parent_id : str, optional - Value of the ``id`` field of another ``TreeNode`` instance. Not set for "root" + Value of the `id` field of another `TreeNode` instance. Not set for "root" nodes. variable : str Name of the tree variable. @@ -2790,7 +2818,7 @@ class TreeNode: target_values : list of str Values of a categorical tree target variable. target_value_frequencies : list of int - Frequencies of each tree target value. Synchronized with ``target_values``. + Frequencies of each tree target value. Synchronized with `target_values`. """ def __init__(self, json_data=None, parent_id=None): @@ -2852,8 +2880,8 @@ class ModlHistograms: Parameters ---------- json_data : dict, optional - JSON data at a ``modlHistograms`` field of an element of the list found at the - ``variablesDetailedStatistics`` field within the ``preparationReport`` field + JSON data at a `modlHistograms` field of an element of the list found at the + `variablesDetailedStatistics` field within the `preparationReport` field of a Khiops JSON report file. If not specified, it returns an empty instance. Attributes @@ -2862,7 +2890,7 @@ class ModlHistograms: Number of available histograms. interpretable_histogram_number : int Number of interpretable histograms. Can be equal to either - ``histogram_number`` or ``histogram_number - 1``. + `histogram_number` or `histogram_number - 1`. truncation_epsilon : float Truncation epsilon used by the truncation heuristic implemented in Khiops. Equals 0 if no truncation is detected in the input data. @@ -2871,27 +2899,27 @@ class ModlHistograms: obtain the first interpretable histogram. granularities : list of int Histogram granularities, sorted in increasing order. - Synchronized with ``histograms``. + Synchronized with `histograms`. interval_numbers : list of int Histogram interval numbers, sorted in increasing order. - Synchronized with ``histograms``. + Synchronized with `histograms`. peak_interval_numbers : list of int Histogram peak interval numbers, sorted in increasing order. - Synchronized with ``histograms``. + Synchronized with `histograms`. spike_interval_numbers : list of int Histogram spike interval numbers, sorted in increasing order. - Synchronized with ``histograms``. + Synchronized with `histograms`. empty_interval_numbers : list of int Histogram empty interval numbers, sorted in increasing order. - Synchronized with ``histograms``. + Synchronized with `histograms`. levels : list of float List of histogram levels, sorted in increasing order. - Synchronized with ``histograms``. + Synchronized with `histograms`. information_rates : list of float Histogram information rates, sorted in increasing order. Between 0 and 100 for interpretable histograms. - Synchronized with ``histograms``. - histograms : list of `Histogram` + Synchronized with `histograms`. + histograms : list of Histogram The MODL histograms. """ @@ -2970,14 +2998,14 @@ def to_dict(self): class Histogram: """A histogram - Represents one of the refinement levels of a `ModlHistograms` object. + Represents one of the refinement levels of a [ModlHistograms][] object. Parameters ---------- json_data : dict, optional - JSON data of an element at the ``histograms`` field of a ``modlHistograms`` - field of an element of the list found at the ``variablesDetailedStatistics`` - field within the ``preparationReport`` field of a Khiops JSON report file. + JSON data of an element at the `histograms` field of a `modlHistograms` + field of an element of the list found at the `variablesDetailedStatistics` + field within the `preparationReport` field of a Khiops JSON report file. If not specified it returns an empty instance. Attributes @@ -3035,35 +3063,35 @@ class DataGrid: Parameters ---------- json_data : dict, optional - JSON data at a ``dataGrid`` field of an element of the list found at the - ``variablesDetailedStatistics`` field within the ``preparationReport`` field of + JSON data at a `dataGrid` field of an element of the list found at the + `variablesDetailedStatistics` field within the `preparationReport` field of a Khiops JSON report file. If not specified it returns an empty instance. Attributes ---------- is_supervised : bool - ``True`` if the data grid is supervised (there is a target). - dimensions : list of `DataGridDimension` + `True` if the data grid is supervised (there is a target). + dimensions : list of DataGridDimension The dimensions of the data grid. frequencies : list of int *Unsupervised only:* Frequencies for each part. part_interests : list of float *Supervised univariate only:* Prediction interests for each part of the input - dimension. Synchronized with ``dimensions[0].partition``. + dimension. Synchronized with `dimensions[0].partition`. part_target_frequencies : list *Supervised univariate only:* List of frequencies per target value for each part - of the input dimension. Synchronized with ``dimensions[0].partition``. + of the input dimension. Synchronized with `dimensions[0].partition`. cell_ids : list of str *Multivariate only:* Unique identifiers of the grid's cells. cell_part_indexes : list *Multivariate only:* List of dimension indexes defining each cell. Synchronized - with ``cell_ids``. + with `cell_ids`. cell_frequencies : list of int *Unsupervised multivariate only:* Frequencies for each cell. Synchronized with - ``cell_ids``. + `cell_ids`. cell_target_frequencies : list *Supervised multivariate only:* List of frequencies per target value for each - cell. Synchronized with ``cell_ids``. + cell. Synchronized with `cell_ids`. """ def __init__(self, json_data=None): @@ -3149,14 +3177,15 @@ def to_dict(self): def write_report(self, writer): # pragma: no cover """Writes the instance's TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -3287,9 +3316,9 @@ class DataGridDimension: Parameters ---------- json_data : dict, optional - JSON data of an element at the ``dimensions`` field of a ``dataGrid`` field of - an element of the list found at the ``variablesDetailedStatistics`` field within - the ``preparationReport`` field of a Khiops JSON report file. If not specified + JSON data of an element at the `dimensions` field of a `dataGrid` field of + an element of the list found at the `variablesDetailedStatistics` field within + the `preparationReport` field of a Khiops JSON report file. If not specified it returns an empty instance. Attributes @@ -3302,9 +3331,9 @@ class DataGridDimension: Partition type. partition : list The dimension parts. The list objects are of type: - - `PartInterval`: If ``partition type`` is "Intervals" - - `PartValue`: If ``partition_type`` is "Values" - - `PartValueGroup`: If ``partition_type`` is "Value groups" + - [PartInterval][]: If `partition type` is "Intervals" + - [PartValue][]: If `partition_type` is "Values" + - [PartValueGroup][]: If `partition_type` is "Value groups" """ def __init__(self, json_data=None): @@ -3387,14 +3416,15 @@ def to_dict(self): def write_report(self, writer): # pragma: no cover """Writes the instance's TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -3415,9 +3445,9 @@ class PartInterval: Parameters ---------- json_data : list, optional - JSON data of the ``partition`` field of a ``dataGrid`` field of an element of - the list found at the ``variablesDetailedStatistics`` field within the - ``preparationReport`` field of a Khiops JSON report file. If not specified it + JSON data of the `partition` field of a `dataGrid` field of an element of + the list found at the `variablesDetailedStatistics` field within the + `preparationReport` field of a Khiops JSON report file. If not specified it returns an empty instance. Attributes @@ -3427,12 +3457,12 @@ class PartInterval: upper_bound : float The upper bound of the interval. is_missing : bool - True if it is the missing values part (bounds are ``None``). + True if it is the missing values part (bounds are `None`). is_left_open : bool - True if the interval has no minimum. ``lower_bound`` still contains the minimum + True if the interval has no minimum. `lower_bound` still contains the minimum value seen on data. is_right_open : bool - True if the interval has no maximum. ``upper_bound`` still contains the minimum + True if the interval has no maximum. `upper_bound` still contains the minimum value seen on data. """ @@ -3499,14 +3529,15 @@ def to_dict(self): def write_report_line(self, writer): # pragma: no cover """Writes a line of the TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -3571,14 +3602,15 @@ def to_dict(self): def write_report_line(self, writer): # pragma: no cover """Writes a line of the TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -3647,14 +3679,15 @@ def to_dict(self): def write_report_line(self, writer): # pragma: no cover """Writes a line of the TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -3677,33 +3710,35 @@ class TrainedPredictor: Parameters ---------- json_data : dict, optional - JSON data of an element of the list found at the ``trainedPredictors`` field - within the ``modelingReport`` field of a Khiops JSON report file. If not + JSON data of an element of the list found at the `trainedPredictors` field + within the `modelingReport` field of a Khiops JSON report file. If not specified it returns an empty instance. - .. note:: - The ``selected_variables`` field is considered a "detail" and is not + !!! note + + The `selected_variables` field is considered a "detail" and is not initialized in the constructor. Instead, it is initialized explicitly via - the `init_details` method. This allows to make partial initializations for + the [init_details][] method. This allows to make partial initializations + for large reports. Attributes ---------- family : str - Predictor family name. Valid values are found in the ``predictor_families`` + Predictor family name. Valid values are found in the `predictor_families` class variable. They are: - "Baseline": for regression only, - "Selective Naive Bayes": in all other cases. type : "Classifier" or "Regressor" - Predictor type. Valid values are found in the ``predictor_types`` class + Predictor type. Valid values are found in the `predictor_types` class attribute. name : str Human readable predictor name. variable_number : int Number of variables used by the predictor. - selected_variables : list of `SelectedVariable` + selected_variables : list of SelectedVariable Variables used by the predictor. Only for type "Selective Naive Bayes". """ @@ -3736,8 +3771,8 @@ def init_details(self, json_data=None): Parameters ---------- json_data : dict, optional - JSON data of the dictionary found at the ``trainedPredictorsDetails`` field - within the ``modelingReport`` field of a Khiops JSON report file. If not + JSON data of the dictionary found at the `trainedPredictorsDetails` field + within the `modelingReport` field of a Khiops JSON report file. If not specified it leaves the object as-is. """ # Check the type of json_data @@ -3789,16 +3824,17 @@ def to_dict(self, details=False): def write_report_header_line(self, writer): # pragma: no cover """Writes the header line of a TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. The header is the same for all variable types. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -3816,14 +3852,15 @@ def write_report_header_line(self, writer): # pragma: no cover def write_report_line(self, writer): # pragma: no cover """Writes a line of the TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -3839,14 +3876,15 @@ def write_report_line(self, writer): # pragma: no cover def write_report_details(self, writer): # pragma: no cover """Writes the details of the TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -3874,8 +3912,8 @@ class SelectedVariable: Parameters ---------- json_data : dict, optional - JSON data representing an element of the ``selectedVariables`` list in the - ``trainedPredictorsDetails`` field within the ``modelingReport`` field of a + JSON data representing an element of the `selectedVariables` list in the + `trainedPredictorsDetails` field within the `modelingReport` field of a Khiops JSON report file. If not specified it returns an empty instance. Attributes @@ -3928,14 +3966,15 @@ def write_report_header_line(self, writer): # pragma: no cover The header is the same for all variable types. - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -3955,14 +3994,15 @@ def write_report_header_line(self, writer): # pragma: no cover def write_report_line(self, writer): # pragma: no cover """Writes a line of the TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -3987,14 +4027,16 @@ class PredictorPerformance: Parameters ---------- json_data : dict, optional - JSON data of an element of the dictionary found at the ``predictorPerformances`` + JSON data of an element of the dictionary found at the `predictorPerformances` field within the one of the evaluation report fields of a Khiops JSON report file. If not specified it returns an empty instance. - .. note:: - The ``confusion_matrix`` field is considered as "detail" and is not + !!! note + + The `confusion_matrix` field is considered as "detail" and is not initialized in the constructor. Instead, it is initialized explicitly via - the `init_details` method. This allows to make partial initializations for + the [init_details][] method. This allows to make partial initializations + for large reports. @@ -4006,7 +4048,7 @@ class PredictorPerformance: Type of the predictor. name : str Human readable name. - data_grid : `DataGrid` + data_grid : DataGrid Data grid representing the distribution of the target values per part of the descriptive variable in the evaluated dataset. accuracy : float @@ -4116,7 +4158,9 @@ def get_metric_names(self): def get_metric(self, metric_name): """Returns the value of the specified metric - .. note:: The available metrics is available via the method `get_metric_names`. + !!! note + + The available metrics is available via the method [get_metric_names][]. Parameters ---------- @@ -4182,14 +4226,15 @@ def write_report_header_line(self, writer): # pragma: no cover The header is the same for all variable types. - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -4217,14 +4262,15 @@ def write_report_header_line(self, writer): # pragma: no cover def write_report_line(self, writer): # pragma: no cover """Writes a line of the TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -4242,14 +4288,15 @@ def write_report_line(self, writer): # pragma: no cover def write_report_details(self, writer): # pragma: no cover """Writes the details of the TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -4278,8 +4325,8 @@ class ConfusionMatrix: Parameters ---------- json_data : dict, optional - JSON data of the ``confusionMatrix`` field of an element of the dictionary found - at the ``predictorsDetailedPerformances`` field within one of the evaluation + JSON data of the `confusionMatrix` field of an element of the dictionary found + at the `predictorsDetailedPerformances` field within one of the evaluation report fields of a Khiops JSON report file. If not specified it returns an empty object. @@ -4289,7 +4336,7 @@ class ConfusionMatrix: Values of the target variable. matrix : list Matrix of predicted frequencies vs target frequencies. This list is synchornized - with ``values``. Each list element represents a row of the confusion matrix, + with `values`. Each list element represents a row of the confusion matrix, that is, the target frequencies for a fixed predicted target value. """ @@ -4315,14 +4362,15 @@ def to_dict(self): def write_report(self, writer): # pragma: no cover """Writes the instance's TSV report into a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed @@ -4351,7 +4399,7 @@ class PredictorCurve: Parameters ---------- json_data : dict, optional - JSON data of an element of the ``liftCurves`` or ``recCurves`` field of one of + JSON data of an element of the `liftCurves` or `recCurves` field of one of the evaluation report fields of a Khiops JSON report file. If not specified it returns an empty instance. diff --git a/khiops/core/api.py b/khiops/core/api.py index efdc2fc5..ae143b6f 100644 --- a/khiops/core/api.py +++ b/khiops/core/api.py @@ -8,11 +8,12 @@ The methods in this module allow to execute all Khiops and Khiops Coclustering tasks. -See also: - - :ref:`core-api-common-params` - - :ref:`core-api-input-types` - - :ref:`core-api-sampling-mode` - - :ref:`core-api-env-samples-dir` +See Also +-------- +- [Common Parameters](../notes.md#core-api-common-params) +- [Input Types](../notes.md#core-api-input-types) +- [Database Sampling](../notes.md#core-api-sampling-mode) +- [Samples Directory Customization](../notes.md#core-api-env-samples-dir) """ import io import os @@ -48,7 +49,8 @@ ] """List of construction rules that Khiops uses by default -.. note:: +!!! note + These are all the multi-table rules. """ # pylint: disable=pointless-string-statement @@ -68,9 +70,10 @@ These rules include: date, time and timestamp rules. -.. note:: +!!! note + These rules are not enabled by default. The user needs to explicitly - select each of them via the ``construction_rules`` parameter of the + select each of them via the `construction_rules` parameter of the relevant Core API functions. """ # pylint: disable=pointless-string-statement @@ -168,12 +171,15 @@ def _preprocess_arguments(args): ------- tuple A 3-tuple containing: - - A `~.CommandLineOptions` instance - - A `~.SystemSettings` instance - - A `bool` that is ``True`` if the value of the `dictionary_file_or_domain` - `args` key is a `~.DictionaryDomain` instance. + - A [CommandLineOptions][] instance + - A [SystemSettings][] instance + - A [bool][] that is `True` if the value of the + [dictionary_file_or_domain][] + `args` key is a [DictionaryDomain][] instance. + + !!! note - .. note:: This function *mutates* the input `args` dictionary. + This function *mutates* the input `args` dictionary. """ # Execute the preprocess of common task arguments task_is_called_with_domain = _preprocess_task_arguments(args) @@ -221,7 +227,9 @@ def _preprocess_arguments(args): def _deprecate_legacy_data_path(data_path_task_arg_name, task_args): """Detect and replace legacy data path with the current syntax - .. note:: The function mutates task_args. + !!! note + + The function mutates task_args. """ if ( data_path_task_arg_name in task_args @@ -294,7 +302,7 @@ def _preprocess_task_arguments(task_args): Returns ------- bool - ``True`` if the task was called with an input `.DictionaryDomain`. + `True` if the task was called with an input [DictionaryDomain][]. """ # Process the output path # if path is dir, then generate full report path according to GUI defaults @@ -394,9 +402,9 @@ def _preprocess_format_spec(detect_format, header_line, field_separator): r"""Preprocess the user format spec to be used in a task More precisely: - - Sets ``detect_format`` to ``False`` if either ``header_line`` or - ``field_separator`` are set - - If either ``header_line`` or ``field_separator`` is ``None``, then they are + - Sets `detect_format` to `False` if either `header_line` or + `field_separator` are set + - If either `header_line` or `field_separator` is `None`, then they are set to their default values - It transforms the field separator "\\t" to the empty string "" """ @@ -474,7 +482,8 @@ def get_khiops_version(): Returns ------- str - The Khiops version of the current `.KhiopsRunner` backend. + The Khiops version of the current + [KhiopsRunner][khiops.core.internals.runner.KhiopsRunner] backend. """ return get_runner().khiops_version @@ -508,22 +517,24 @@ def export_dictionary_as_json( temp_dir="", scenario_prologue="", ): - """Exports a Khiops dictionary file to JSON format (``.kdicj``) + """Exports a Khiops dictionary file to JSON format (`.kdicj`) Parameters ---------- - dictionary_file_path_or_domain : str or `.DictionaryDomain` + dictionary_file_path_or_domain : str or DictionaryDomain Path of a Khiops dictionary file or a DictionaryDomain object. json_dictionary_file_path : str Path (absolute path recommended) to the output dictionary file, in the JSON format. Note that a relative path will produce a file in the current working directory. - ... : - See :ref:`core-api-common-params`. + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) Examples -------- - See the following function of the ``samples.py`` documentation script: + See the following function of the `samples.py` documentation script: - `samples.export_dictionary_files()` """ # Save the task arguments @@ -564,20 +575,22 @@ def build_dictionary_from_data_table( output_dictionary_file_path : str Path (absolute path recommended) of the output dictionary file. Note that a relative path will produce a file in the current working directory. - detect_format : bool, default ``True`` - If ``True`` detects automatically whether the data table file has a header and - its field separator. It is set to ``False`` if ``header_line`` or - ``field_separator`` are set. - header_line : bool, optional (default ``True``) - If ``True`` it uses the first line of the data as column names. Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. + detect_format : bool, default `True` + If `True` detects automatically whether the data table file has a header and + its field separator. It is set to `False` if `header_line` or + `field_separator` are set. + header_line : bool, optional (default `True`) + If `True` it uses the first line of the data as column names. Sets + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. field_separator : str, optional (default "\\t") A field separator character. "" has the same effect as "\\t". Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. - ... : - See :ref:`core-api-common-params`. + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) """ # Save the task arguments # WARNING: Do not move this line, see the top of the "tasks" section for details @@ -616,46 +629,50 @@ def check_database( Parameters ---------- - dictionary_file_path_or_domain : str or `.DictionaryDomain` + dictionary_file_path_or_domain : str or DictionaryDomain Path of a Khiops dictionary file or a DictionaryDomain object. dictionary_name : str Name of the dictionary of the table to be checked. data_table_path : str Path of the data table file. - detect_format : bool, default ``True`` - If ``True`` detects automatically whether the data table file has a header and - its field separator. It is set to ``False`` if ``header_line`` or - ``field_separator`` are set. - header_line : bool, optional (default ``True``) - If ``True`` it uses the first line of the data as column names. Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. + detect_format : bool, default `True` + If `True` detects automatically whether the data table file has a header and + its field separator. It is set to `False` if `header_line` or + `field_separator` are set. + header_line : bool, optional (default `True`) + If `True` it uses the first line of the data as column names. Sets + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. field_separator : str, optional (default "\\t") A field separator character. "" has the same effect as "\\t". Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. sample_percentage : float, default 100.0 - See the ``sampling_mode`` option below. + See the `sampling_mode` option below. sampling_mode : "Include sample" or "Exclude sample" - If equal to "Include sample" it checks ``sample_percentage`` percent of + If equal to "Include sample" it checks `sample_percentage` percent of the data; if equal to "Exclude sample" it checks the complement of the - data selected with "Include sample". See also :ref:`core-api-sampling-mode`. + data selected with "Include sample". See also + [Database Sampling](../notes.md#core-api-sampling-mode). selection_variable : str, default "" - It checks only the records such that the value of ``selection_variable`` is - equal to ``selection_value``. Ignored if equal to "". + It checks only the records such that the value of `selection_variable` is + equal to `selection_value`. Ignored if equal to "". selection_value: str or int or float, default "" - See ``selection_variable`` option above. Ignored if equal to "". + See `selection_variable` option above. Ignored if equal to "". additional_data_tables : dict, optional A dictionary containing the data paths and file paths for a multi-table - dictionary file. For more details see :doc:`/multi_table_primer`. + dictionary file. For more details see + [Multi-Table Learning Primer](../multi_table_primer.md). max_messages : int, default 20 Maximum number of error messages to write in the log file. - ... : - See :ref:`core-api-common-params`. + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) Examples -------- - See the following function of the ``samples.py`` documentation script: + See the following function of the `samples.py` documentation script: - `samples.check_database()` """ # Save the task arguments @@ -712,14 +729,15 @@ def train_predictor( ): r"""Trains a model from a data table - .. note:: - For all input dictionaries, this function creates model dictionaries whose - names are prefixed with ``SNB_``. For regression models, additional - dictionaries are created and their names are prefixed with ``Baseline_``. + !!! note + + For all input dictionaries, this function creates model dictionaries whose + names are prefixed with `SNB_`. For regression models, additional + dictionaries are created and their names are prefixed with `Baseline_`. Parameters ---------- - dictionary_file_path_or_domain : str or `.DictionaryDomain` + dictionary_file_path_or_domain : str or DictionaryDomain Path of a Khiops dictionary file or a DictionaryDomain object. dictionary_name : str Name of the dictionary to be analyzed. @@ -732,47 +750,48 @@ def train_predictor( analysis_report_file_path : str Path (absolute path recommended) to the analysis report file, in the JSON format. An additional dictionary file with the same name and - extension ``.model.kdic`` is built, which contains the trained models. + extension `.model.kdic` is built, which contains the trained models. Note that a relative path will produce a report file in the current working directory. - detect_format : bool, default ``True`` - If ``True`` detects automatically whether the data table file has a header and - its field separator. It is set to ``False`` if ``header_line`` or - ``field_separator`` are set. - header_line : bool, optional (default ``True``) - If ``True`` it uses the first line of the data as column names. Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. + detect_format : bool, default `True` + If `True` detects automatically whether the data table file has a header and + its field separator. It is set to `False` if `header_line` or + `field_separator` are set. + header_line : bool, optional (default `True`) + If `True` it uses the first line of the data as column names. Sets + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. field_separator : str, optional (default "\\t") A field separator character. "" has the same effect as "\\t". Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. sample_percentage : float, default 70.0 - See the ``sampling_mode`` option below. + See the `sampling_mode` option below. sampling_mode : "Include sample" or "Exclude sample" - If equal to "Include sample" it trains the predictor on ``sample_percentage`` + If equal to "Include sample" it trains the predictor on `sample_percentage` percent of the data and tests the model on the remainder of the data if - ``use_complement_as_test`` is set to ``True``. If equal to "Exclude sample" the + `use_complement_as_test` is set to `True`. If equal to "Exclude sample" the train and test datasets above are exchanged. See also - :ref:`core-api-sampling-mode`. - use_complement_as_test : bool, default ``True`` + [Database Sampling](../notes.md#core-api-sampling-mode). + use_complement_as_test : bool, default `True` Uses the complement of the sampled database as test database for computing the model's performance metrics. selection_variable : str, default "" - It trains with only the records such that the value of ``selection_variable`` is - equal to ``selection_value``. Ignored if equal to "". + It trains with only the records such that the value of `selection_variable` is + equal to `selection_value`. Ignored if equal to "". selection_value: str or int or float, default "" - See ``selection_variable`` option above. Ignored if equal to "". + See `selection_variable` option above. Ignored if equal to "". additional_data_tables : dict, optional A dictionary containing the data paths and file paths for a multi-table - dictionary file. For more details see :doc:`/multi_table_primer`. - do_data_preparation_only : bool, default ``False`` - If ``True`` it only does data preparation via MODL preprocessing without + dictionary file. For more details see + [Multi-Table Learning Primer](../multi_table_primer.md). + do_data_preparation_only : bool, default `False` + If `True` it only does data preparation via MODL preprocessing without training a Selective Naive Bayes Predictor. main_target_value : str, default "" If this target value is specified then it guarantees the calculation of lift curves for it. - keep_selected_variables_only : bool, default ``True`` + keep_selected_variables_only : bool, default `True` Keeps only predictor-selected variables in the supervised analysis report. max_evaluated_variables : int, default 0 Maximum number of variables to be evaluated in the SNB predictor training. If @@ -785,7 +804,7 @@ def train_predictor( construction_rules : list of str, optional Allowed rules for the automatic variable construction. If not set, Khiops uses the multi-table construction rules listed in - `DEFAULT_CONSTRUCTION_RULES`. + [DEFAULT_CONSTRUCTION_RULES][]. max_text_features : int, default 10000 Maximum number of text features to construct. text_features : str, default "words" @@ -802,13 +821,13 @@ def train_predictor( specific_pairs : list of tuple, optional User-specified pairs as a list of 2-tuples of feature names. If a given tuple contains only one non-empty feature name, then it generates all the pairs - containing it (within the maximum limit ``max_pairs``). These pairs have top + containing it (within the maximum limit `max_pairs`). These pairs have top priority: they are constructed first. - all_possible_pairs : bool, default ``True`` - If ``True`` tries to create all possible pairs within the limit ``max_pairs``. - Pairs specified with ``specific_pairs`` have top priority: they are constructed + all_possible_pairs : bool, default `True` + If `True` tries to create all possible pairs within the limit `max_pairs`. + Pairs specified with `specific_pairs` have top priority: they are constructed first. - group_target_value : bool, default ``False`` + group_target_value : bool, default `False` Allows grouping of the target variable values in classification. It can substantially increase the training time. discretization_method : str, default "MODL" @@ -824,11 +843,13 @@ def train_predictor( to 0 it is automatically calculated. Special default values for unsupervised analysis: - - If ``discretization_method`` is "EqualWidth" or "EqualFrequency": 10 - - If ``grouping_method`` is "BasicGrouping": 10 + - If `discretization_method` is "EqualWidth" or "EqualFrequency": 10 + - If `grouping_method` is "BasicGrouping": 10 - ... : - See :ref:`core-api-common-params`. + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) Returns ------- @@ -839,14 +860,14 @@ def train_predictor( Raises ------ - `ValueError` + ValueError Invalid values of an argument - `TypeError` + TypeError Invalid type of an argument Examples -------- - See the following functions of the ``samples.py`` documentation script: + See the following functions of the `samples.py` documentation script: - `samples.train_predictor()` - `samples.train_predictor_file_paths()` - `samples.train_predictor_error_handling()` @@ -904,7 +925,7 @@ def interpret_predictor( Parameters ---------- - dictionary_file_path_or_domain : str or `.DictionaryDomain` + dictionary_file_path_or_domain : str or DictionaryDomain Path of a Khiops dictionary file or a DictionaryDomain object. predictor_dictionary_name : str Name of the predictor dictionary used while building the interpretation model. @@ -919,19 +940,21 @@ def interpret_predictor( - "Global": predictor variables are ranked by decreasing global importance. - "Individual": predictor variables are ranked by decreasing individual Shapley value. - ... : - See :ref:`core-api-common-params`. + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) Raises ------ - `ValueError` + ValueError Invalid values of an argument - `TypeError` + TypeError Invalid type of an argument Examples -------- - See the following functions of the ``samples.py`` documentation script: + See the following functions of the `samples.py` documentation script: - `samples.interpret_predictor()` - `samples.deploy_model_mt_with_interpretation()` """ @@ -969,7 +992,7 @@ def reinforce_predictor( Parameters ---------- - dictionary_file_path_or_domain : str or `.DictionaryDomain` + dictionary_file_path_or_domain : str or DictionaryDomain Path of a Khiops dictionary file or a DictionaryDomain object. predictor_dictionary_name : str Name of the predictor dictionary used while building the reinforced predictor. @@ -982,19 +1005,21 @@ def reinforce_predictor( The names of variables to use as lever variables while building the reinforced predictor. Min length: 1. Max length: the total number of variables in the prediction model. - ... : - See :ref:`core-api-common-params`. + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) Raises ------ - `ValueError` + ValueError Invalid values of an argument - `TypeError` + TypeError Invalid type of an argument Examples -------- - See the following functions of the ``samples.py`` documentation script: + See the following functions of the `samples.py` documentation script: - `samples.reinforce_predictor()` - `samples.deploy_reinforced_model_mt()` """ @@ -1036,7 +1061,7 @@ def evaluate_predictor( Parameters ---------- - dictionary_file_path_or_domain : str or `.DictionaryDomain` + dictionary_file_path_or_domain : str or DictionaryDomain Path of a Khiops dictionary file or a DictionaryDomain object. train_dictionary_name : str Name of the main dictionary used while training the models. @@ -1046,57 +1071,61 @@ def evaluate_predictor( Path (absolute path recommended) to the evaluation report file, in the JSON format. Note that a relative path will produce a report file in the current working directory. - detect_format : bool, default ``True`` - If ``True`` detects automatically whether the data table file has a header and - its field separator. It is set to ``False`` if ``header_line`` or - ``field_separator`` are set. - header_line : bool, optional (default ``True``) - If ``True`` it uses the first line of the data as column names. Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. + detect_format : bool, default `True` + If `True` detects automatically whether the data table file has a header and + its field separator. It is set to `False` if `header_line` or + `field_separator` are set. + header_line : bool, optional (default `True`) + If `True` it uses the first line of the data as column names. Sets + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. field_separator : str, optional (default "\\t") A field separator character. "" has the same effect as "\\t". Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. sample_percentage : float, default 100.0 - See ``sampling_mode`` option below. + See `sampling_mode` option below. sampling_mode : "Include sample" or "Exclude sample" - If equal to "Include sample" it evaluates the predictor on ``sample_percentage`` + If equal to "Include sample" it evaluates the predictor on `sample_percentage` percent of the data. If equal to "Exclude sample" it evaluates the predictor on the complement of the data selected with "Include sample". See also - :ref:`core-api-sampling-mode`. + [Database Sampling](../notes.md#core-api-sampling-mode). selection_variable : str, default "" - It trains with only the records such that the value of ``selection_variable`` is - equal to ``selection_value``. Ignored if equal "". + It trains with only the records such that the value of `selection_variable` is + equal to `selection_value`. Ignored if equal "". selection_value: str or int or float, default "" - See ``selection_variable`` option above. Ignored if equal to "". + See `selection_variable` option above. Ignored if equal to "". additional_data_tables : dict, optional A dictionary containing the data paths and file paths for a multi-table - dictionary file. For more details see :doc:`/multi_table_primer`. + dictionary file. For more details see + [Multi-Table Learning Primer](../multi_table_primer.md). + + !!! note - .. note:: - For external tables, use the initial dictionary name in the data paths, - which is the same as the one used for training the predictor. + For external tables, use the initial dictionary name in the data paths, + which is the same as the one used for training the predictor. main_target_value : str, default "" If this target value is specified then it guarantees the calculation of lift curves for it. - ... : - See :ref:`core-api-common-params`. + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) Returns ------- str - The path of the JSON evaluation report (extension ``.khj``). + The path of the JSON evaluation report (extension `.khj`). Raises ------ - `TypeError` + TypeError Invalid type of an argument. Examples -------- - See the following functions of the ``samples.py`` documentation script: + See the following functions of the `samples.py` documentation script: - `samples.evaluate_predictor()` - `samples.access_predictor_evaluation_report()` - `samples.train_predictor_with_cross_validation()` @@ -1162,23 +1191,24 @@ def train_recoder( A recoding model consists in the discretization of numerical variables and the grouping of categorical variables. - If the ``target_variable`` is specified these partitions are constructed in + If the `target_variable` is specified these partitions are constructed in supervised mode, meaning that each resulting discretizations/groupings best separates the target variable while maintaining a simple interval/group model of the data. Different recoding methods can be specified via the - ``numerical_recoding_method``, ``categorical_recoding_method`` and - ``pairs_recoding_method`` options. + `numerical_recoding_method`, `categorical_recoding_method` and + `pairs_recoding_method` options. + + The output files of this process contain a dictionary file (`.kdic`) that can be + used to recode databases with the [deploy_model][] function. - The output files of this process contain a dictionary file (``.kdic``) that can be - used to recode databases with the `deploy_model` function. + !!! note - .. note:: - For all input dictionaries, this function creates model dictionaries whose names - are prefixed with ``R_``. + For all input dictionaries, this function creates model dictionaries whose names + are prefixed with `R_`. Parameters ---------- - dictionary_file_path_or_domain : str or `.DictionaryDomain` + dictionary_file_path_or_domain : str or DictionaryDomain Path of a Khiops dictionary file or a DictionaryDomain object. dictionary_name : str Name of the dictionary to be recoded. @@ -1189,42 +1219,43 @@ def train_recoder( analysis_report_file_path : str Path (absolute path recommended) to the analysis report file, in the JSON format. An additional dictionary file with the same name and - extension ``.model.kdic`` is built, which contains the trained recoding model. + extension `.model.kdic` is built, which contains the trained recoding model. Note that a relative path will produce a report file in the current working directory. - detect_format : bool, default ``True`` - If ``True`` detects automatically whether the data table file has a header and - its field separator. It is set to ``False`` if ``header_line`` or - ``field_separator`` are set. - header_line : bool, optional (default ``True``) - If ``True`` it uses the first line of the data as column names. Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. + detect_format : bool, default `True` + If `True` detects automatically whether the data table file has a header and + its field separator. It is set to `False` if `header_line` or + `field_separator` are set. + header_line : bool, optional (default `True`) + If `True` it uses the first line of the data as column names. Sets + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. field_separator : str, optional (default "\\t") A field separator character. "" has the same effect as "\\t". Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. sample_percentage : float, default 100.0 - See ``sampling_mode`` option below. + See `sampling_mode` option below. sampling_mode : "Include sample" or "Exclude sample" - If equal to "Include sample" it trains the recoder on ``sample_percentage`` + If equal to "Include sample" it trains the recoder on `sample_percentage` percent of the data. If equal to "Exclude sample" it trains the recoder on the complement of the data selected with "Include sample". See also - :ref:`core-api-sampling-mode`. + [Database Sampling](../notes.md#core-api-sampling-mode). selection_variable : str, default "" - It trains with only the records such that the value of ``selection_variable`` is - equal to ``selection_value``. Ignored if equal to "". + It trains with only the records such that the value of `selection_variable` is + equal to `selection_value`. Ignored if equal to "". selection_value: str or int or float, default "" - See ``selection_variable`` option above. Ignored if equal to "". + See `selection_variable` option above. Ignored if equal to "". additional_data_tables : dict, optional A dictionary containing the data paths and file paths for a multi-table - dictionary file. For more details see :doc:`/multi_table_primer`. + dictionary file. For more details see + [Multi-Table Learning Primer](../multi_table_primer.md). max_constructed_variables : int, default 100 Maximum number of variables to construct. construction_rules : list of str, optional Allowed rules for the automatic variable construction. If not set, Khiops uses the multi-table construction rules listed in - `DEFAULT_CONSTRUCTION_RULES`. + [DEFAULT_CONSTRUCTION_RULES][]. max_text_features : int, default 10000 Maximum number of text features to construct. text_features : str, default "words" @@ -1241,23 +1272,23 @@ def train_recoder( specific_pairs : list of tuple, optional User-specified pairs as a list of 2-tuples of feature names. If a given tuple contains only one non-empty feature name, then it generates all the pairs - containing it (within the maximum limit ``max_pairs``). These pairs have top + containing it (within the maximum limit `max_pairs`). These pairs have top priority: they are constructed first. - all_possible_pairs : bool, default ``True`` - If ``True`` tries to create all possible pairs within the limit ``max_pairs``. - Pairs specified with ``specific_pairs`` have top priority: they are constructed + all_possible_pairs : bool, default `True` + If `True` tries to create all possible pairs within the limit `max_pairs`. + Pairs specified with `specific_pairs` have top priority: they are constructed first. - group_target_value : bool, default ``False`` + group_target_value : bool, default `False` Allows grouping of the target variable values in classification. It can substantially increase the training time. - informative_variables_only : bool, default ``True`` - If ``True`` keeps only informative variables. + informative_variables_only : bool, default `True` + If `True` keeps only informative variables. max_variables : int, default 0 Maximum number of variables to keep. If equal to 0 keeps all variables. - keep_initial_categorical_variables : bool, default ``False`` - If ``True`` keeps the initial categorical variables. - keep_initial_numerical_variables : bool, default ``False`` - If ``True`` keeps initial numerical variables. + keep_initial_categorical_variables : bool, default `False` + If `True` keeps the initial categorical variables. + keep_initial_numerical_variables : bool, default `False` + If `True` keeps initial numerical variables. categorical_recoding_method : str Type of recoding for categorical variables. Types available: @@ -1302,11 +1333,13 @@ def train_recoder( to 0 it is automatically calculated. Special default values for unsupervised analysis: - - If ``discretization_method`` is "EqualWidth" or "EqualFrequency": 10 - - If ``grouping_method`` is "BasicGrouping": 10 + - If `discretization_method` is "EqualWidth" or "EqualFrequency": 10 + - If `grouping_method` is "BasicGrouping": 10 - ... : - See :ref:`core-api-common-params`. + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) Returns ------- @@ -1318,7 +1351,7 @@ def train_recoder( Examples -------- - See the following functions of the ``samples.py`` documentation script: + See the following functions of the `samples.py` documentation script: - `samples.train_recoder()` - `samples.train_recoder_with_multiple_parameters()` - `samples.train_recoder_mt_flatten()` @@ -1373,7 +1406,7 @@ def deploy_model( Parameters ---------- - dictionary_file_path_or_domain : str or `.DictionaryDomain` + dictionary_file_path_or_domain : str or DictionaryDomain Path of a Khiops dictionary file or a DictionaryDomain object. This file/object defines the model to be deployed. Note that this model is not necessarily a predictor, it can be a generic table transformation. @@ -1384,54 +1417,59 @@ def deploy_model( output_data_table_path : str Path (absolute path recommended) of the output data file. Note that a relative path will produce a file in the current working directory. - detect_format : bool, default ``True`` - If ``True`` detects automatically whether the data table file has a header and - its field separator. It is set to ``False`` if ``header_line`` or - ``field_separator`` are set. - header_line : bool, optional (default ``True``) - If ``True`` it uses the first line of the data as column names. Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. + detect_format : bool, default `True` + If `True` detects automatically whether the data table file has a header and + its field separator. It is set to `False` if `header_line` or + `field_separator` are set. + header_line : bool, optional (default `True`) + If `True` it uses the first line of the data as column names. Sets + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. field_separator : str, optional (default "\\t") A field separator character. "" has the same effect as "\\t". Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. sample_percentage : float, default 100.0 - See ``sampling_mode`` option below. + See `sampling_mode` option below. sampling_mode : "Include sample" or "Exclude sample" - If equal to "Include sample" it deploys the model on ``sample_percentage`` + If equal to "Include sample" it deploys the model on `sample_percentage` percent of the data. If equal to "Exclude sample" it deploys the model on the complement of the data selected with "Include sample". See also - :ref:`core-api-sampling-mode`. + [Database Sampling](../notes.md#core-api-sampling-mode). selection_variable : str, default "" - It deploys only the records such that the value of ``selection_variable`` is - equal to ``selection_value``. Ignored if equal to "". + It deploys only the records such that the value of `selection_variable` is + equal to `selection_value`. Ignored if equal to "". selection_value: str or int or float, default "" - See ``selection_variable`` option above. Ignored if equal to "". + See `selection_variable` option above. Ignored if equal to "". additional_data_tables : dict, optional A dictionary containing the data paths and file paths for a multi-table - dictionary file. For more details see :doc:`/multi_table_primer`. + dictionary file. For more details see + [Multi-Table Learning Primer](../multi_table_primer.md). - .. note:: Use model dictionary data paths for external tables. + !!! note - output_header_line : bool, default ``True`` - If ``True`` writes a header line with the column names in the output table. + Use model dictionary data paths for external tables. + output_header_line : bool, default `True` + If `True` writes a header line with the column names in the output table. output_field_separator : str, default "\\t" The field separator character for the output table ("" counts as "\\t"). output_additional_data_tables : dict, optional A dictionary containing the output data paths and file paths for a multi-table - dictionary file. For more details see :doc:`/multi_table_primer`. - ... : - See :ref:`core-api-common-params`. + dictionary file. For more details see + [Multi-Table Learning Primer](../multi_table_primer.md). + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) Raises ------ - `TypeError` + TypeError Invalid type of an argument. Examples -------- - See the following functions of the ``samples.py`` documentation script: + See the following functions of the `samples.py` documentation script: - `samples.deploy_model()` - `samples.deploy_model_mt()` - `samples.deploy_model_mt_with_interpretation()` @@ -1467,24 +1505,26 @@ def build_deployed_dictionary( Parameters ---------- - dictionary_file_path_or_domain : str or `.DictionaryDomain` + dictionary_file_path_or_domain : str or DictionaryDomain Path of a Khiops dictionary file or a DictionaryDomain object. dictionary_name : str Name of the dictionary to be analyzed. output_dictionary_file_path : str Path (absolute path recommended) of the output dictionary file. Note that a relative path will produce a file in the current working directory. - ... : - See :ref:`core-api-common-params`. + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) Raises ------ - `TypeError` + TypeError Invalid type of an argument Examples -------- - See the following functions of the ``samples.py`` documentation script: + See the following functions of the `samples.py` documentation script: - `samples.build_deployed_dictionary()` """ # Save the task arguments @@ -1522,7 +1562,7 @@ def sort_data_table( Parameters ---------- - dictionary_file_path_or_domain : str or `.DictionaryDomain` + dictionary_file_path_or_domain : str or DictionaryDomain Path of a Khiops dictionary file or a DictionaryDomain object. dictionary_name : str Name of the dictionary to be analyzed. @@ -1533,34 +1573,36 @@ def sort_data_table( relative path will produce a file in the current working directory. sort_variables : list of str, optional The names of the variables to sort. If not set sorts the table by its key. - detect_format : bool, default ``True`` - If ``True`` detects automatically whether the data table file has a header and - its field separator. It is set to ``False`` if ``header_line`` or - ``field_separator`` are set. - header_line : bool, optional (default ``True``) - If ``True`` it uses the first line of the data as column names. Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. + detect_format : bool, default `True` + If `True` detects automatically whether the data table file has a header and + its field separator. It is set to `False` if `header_line` or + `field_separator` are set. + header_line : bool, optional (default `True`) + If `True` it uses the first line of the data as column names. Sets + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. field_separator : str, optional (default "\\t") A field separator character. "" has the same effect as "\\t". Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. - output_header_line : bool, default ``True`` - If ``True`` writes a header line with the column names in the output table. + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. + output_header_line : bool, default `True` + If `True` writes a header line with the column names in the output table. output_field_separator : str, default "\\t" The field separator character for the output table ("" counts as "\\t"). - ... : - See :ref:`core-api-common-params`. + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) Raises ------ - `TypeError` + TypeError Invalid type of a argument. Examples -------- - See the following functions of the ``samples.py`` documentation script: + See the following functions of the `samples.py` documentation script: - `samples.sort_data_table()` - `samples.sort_data_table_expert()` """ @@ -1598,7 +1640,7 @@ def extract_keys_from_data_table( Parameters ---------- - dictionary_file_path_or_domain : str or `.DictionaryDomain` + dictionary_file_path_or_domain : str or DictionaryDomain Path of a Khiops dictionary file or a DictionaryDomain object. dictionary_name : str Name of the dictionary of the data table. @@ -1607,33 +1649,35 @@ def extract_keys_from_data_table( output_data_table_path : str Path (absolute path recommended) of the output data file. Note that a relative path will produce a file in the current working directory. - detect_format : bool, default ``True`` - If ``True`` detects automatically whether the data table file has a header and - its field separator. It is set to ``False`` if ``header_line`` or - ``field_separator`` are set. - header_line : bool, optional (default ``True``) - If ``True`` it uses the first line of the data as column names. Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. + detect_format : bool, default `True` + If `True` detects automatically whether the data table file has a header and + its field separator. It is set to `False` if `header_line` or + `field_separator` are set. + header_line : bool, optional (default `True`) + If `True` it uses the first line of the data as column names. Sets + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. field_separator : str, optional (default "\\t") A field separator character. "" has the same effect as "\\t". Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. - output_header_line : bool, default ``True`` - If ``True`` writes a header line with the column names in the output table. + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. + output_header_line : bool, default `True` + If `True` writes a header line with the column names in the output table. output_field_separator : str, default "\\t" The field separator character for the output table ("" counts as "\\t"). - ... : - See :ref:`core-api-common-params`. + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) Raises ------ - `TypeError` + TypeError Invalid type of an argument. Examples -------- - See the following function of the ``samples.py`` documentation script: + See the following function of the `samples.py` documentation script: - `samples.extract_keys_from_data_table()` """ # Save the task arguments @@ -1676,7 +1720,7 @@ def train_coclustering( Parameters ---------- - dictionary_file_path_or_domain : str or `.DictionaryDomain` + dictionary_file_path_or_domain : str or DictionaryDomain Path of a Khiops dictionary file or a DictionaryDomain object. dictionary_name : str Name of the dictionary to be analyzed. @@ -1688,39 +1732,43 @@ def train_coclustering( Path (absolute path recommended) to the coclustering report file, in the JSON format. Note that a relative path will produce a report file in the current working directory. - detect_format : bool, default ``True`` - If ``True`` detects automatically whether the data table file has a header and - its field separator. It is set to ``False`` if ``header_line`` or - ``field_separator`` are set. - header_line : bool, optional (default ``True``) - If ``True`` it uses the first line of the data as column names. Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. + detect_format : bool, default `True` + If `True` detects automatically whether the data table file has a header and + its field separator. It is set to `False` if `header_line` or + `field_separator` are set. + header_line : bool, optional (default `True`) + If `True` it uses the first line of the data as column names. Sets + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. field_separator : str, optional (default "\\t") A field separator character. "" has the same effect as "\\t". Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. sample_percentage : float, default 100.0 - See ``sampling_mode`` option below. + See `sampling_mode` option below. sampling_mode : "Include sample" or "Exclude sample" If equal to "Include sample" it trains the coclustering estimator on - ``sample_percentage`` percent of the data. If equal to "Exclude sample" it + `sample_percentage` percent of the data. If equal to "Exclude sample" it trains the coclustering estimator on the complement of the data selected with - "Include sample". See also :ref:`core-api-sampling-mode`. + "Include sample". See also + [Database Sampling](../notes.md#core-api-sampling-mode). selection_variable : str, default "" - It trains with only the records such that the value of ``selection_variable`` is - equal to ``selection_value``. Ignored if equal to "". + It trains with only the records such that the value of `selection_variable` is + equal to `selection_value`. Ignored if equal to "". selection_value: str or int or float, default "" - See ``selection_variable`` option above. Ignored if equal to "". + See `selection_variable` option above. Ignored if equal to "". additional_data_tables : dict, optional A dictionary containing the data paths and file paths for a multi-table - dictionary file. For more details see :doc:`/multi_table_primer`. + dictionary file. For more details see + [Multi-Table Learning Primer](../multi_table_primer.md). frequency_variable : str, default "" Name of frequency variable. min_optimization_time : int, default 0 Minimum optimization time in seconds. - ... : - See :ref:`core-api-common-params`. + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) Returns ------- @@ -1729,14 +1777,14 @@ def train_coclustering( Raises ------ - `ValueError` + ValueError Number of coclustering variables out of the range 2-10. - `TypeError` + TypeError Invalid type of an argument. Examples -------- - See the following function of the ``samples.py`` documentation script: + See the following function of the `samples.py` documentation script: - `samples.train_coclustering()` """ # Save the task arguments @@ -1783,7 +1831,7 @@ def train_instance_variable_coclustering( **kwargs, ): r"""Trains an instance-variable coclustering model from a data table - .. note:: + !!! note If keys are available in the input dictionary, they are used as instance identifiers. Otherwise, line numbers in the instance data table are used as @@ -1791,7 +1839,7 @@ def train_instance_variable_coclustering( Parameters ---------- - dictionary_file_path_or_domain : str or `.DictionaryDomain` + dictionary_file_path_or_domain : str or DictionaryDomain Path of a Khiops dictionary file or a DictionaryDomain object. dictionary_name : str Name of the dictionary to be analyzed. @@ -1801,37 +1849,41 @@ def train_instance_variable_coclustering( Path (absolute path recommended) to the coclustering report file, in the JSON format. Note that a relative path will produce a report file in the current working directory. - detect_format : bool, default ``True`` - If ``True`` detects automatically whether the data table file has a header and - its field separator. It is set to ``False`` if ``header_line`` or - ``field_separator`` are set. - header_line : bool, optional (default ``True``) - If ``True`` it uses the first line of the data as column names. Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. + detect_format : bool, default `True` + If `True` detects automatically whether the data table file has a header and + its field separator. It is set to `False` if `header_line` or + `field_separator` are set. + header_line : bool, optional (default `True`) + If `True` it uses the first line of the data as column names. Sets + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. field_separator : str, optional (default "\\t") A field separator character. "" has the same effect as "\\t". Sets - ``detect_format`` to ``False`` if set. Ignored if ``detect_format`` - is ``True``. + `detect_format` to `False` if set. Ignored if `detect_format` + is `True`. sample_percentage : float, default 100.0 - See ``sampling_mode`` option below. + See `sampling_mode` option below. sampling_mode : "Include sample" or "Exclude sample" If equal to "Include sample" it trains the coclustering estimator on - ``sample_percentage`` percent of the data. If equal to "Exclude sample" it + `sample_percentage` percent of the data. If equal to "Exclude sample" it trains the coclustering estimator on the complement of the data selected with - "Include sample". See also :ref:`core-api-sampling-mode`. + "Include sample". See also + [Database Sampling](../notes.md#core-api-sampling-mode). selection_variable : str, default "" - It trains with only the records such that the value of ``selection_variable`` is - equal to ``selection_value``. Ignored if equal to "". + It trains with only the records such that the value of `selection_variable` is + equal to `selection_value`. Ignored if equal to "". selection_value: str or int or float, default "" - See ``selection_variable`` option above. Ignored if equal to "". + See `selection_variable` option above. Ignored if equal to "". additional_data_tables : dict, optional A dictionary containing the data paths and file paths for a multi-table - dictionary file. For more details see :doc:`/multi_table_primer`. + dictionary file. For more details see + [Multi-Table Learning Primer](../multi_table_primer.md). min_optimization_time : int, default 0 Minimum optimization time in seconds. - ... : - See :ref:`core-api-common-params`. + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) Returns ------- @@ -1840,14 +1892,14 @@ def train_instance_variable_coclustering( Raises ------ - `ValueError` + ValueError Number of coclustering variables out of the range 2-10. - `TypeError` + TypeError Invalid type of an argument. Examples -------- - See the following function of the ``samples.py`` documentation script: + See the following function of the `samples.py` documentation script: - `samples.train_instance_variable_coclustering()` """ # Save the task arguments @@ -1886,7 +1938,7 @@ def simplify_coclustering( Parameters ---------- coclustering_file_path : str - Path of the coclustering file (extension ``.khc``, or ``.khcj``). + Path of the coclustering file (extension `.khc`, or `.khcj`). simplified_coclustering_file_path : str Path (absolute path recommended) of the output coclustering file. Note that a relative path will produce a report file in the current working @@ -1901,19 +1953,22 @@ def simplify_coclustering( Maximum number of parts totaled over all variables. If equal to 0 there is no limit. max_part_numbers : dict, optional - Dictionary that associate variable names to their maximum number of parts to - preserve in the simplified coclustering. If not set there is no limit. - ... : - See :ref:`core-api-common-params`. + Dictionary that associate variable names to their maximum number of + parts to preserve in the simplified coclustering. If not set there is + no limit. + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) Raises ------ - `TypeError` + TypeError Invalid type of an argument. Examples -------- - See the following function of the ``samples.py`` documentation script: + See the following function of the `samples.py` documentation script: - `samples.simplify_coclustering()` """ # Save the task arguments @@ -1983,12 +2038,12 @@ def prepare_coclustering_deployment( Parameters ---------- - dictionary_file_path_or_domain : str or `.DictionaryDomain` + dictionary_file_path_or_domain : str or DictionaryDomain Path of a Khiops dictionary file or a DictionaryDomain object. dictionary_name : str Name of the dictionary to be analyzed. coclustering_file_path : str - Path of the coclustering model file (extension ``.khc`` or ``.khcj``). + Path of the coclustering model file (extension `.khc` or `.khcj`). table_variable : str Name of the table variable in the dictionary. deployed_variable_name : str @@ -2005,28 +2060,30 @@ def prepare_coclustering_deployment( Maximum number of parts totaled over all variables. If equal to 0 there is no limit. max_part_numbers : dict, optional - Dictionary associating variable names to their maximum number of parts to - preserve in the simplified coclustering. For variables not present in - ``max_part_numbers`` there is no limit. - build_cluster_variable : bool, default ``True`` - If ``True`` includes a cluster id variable in the deployment. - build_distance_variables : bool, default ``False`` - If ``True`` includes a cluster distance variable in the deployment. - build_frequency_variables : bool, default ``False`` - If ``True`` includes the frequency variables in the deployment. + Dictionary associating variable names to their maximum number of parts + to preserve in the simplified coclustering. For variables not present + in `max_part_numbers` there is no limit. + build_cluster_variable : bool, default `True` + If `True` includes a cluster id variable in the deployment. + build_distance_variables : bool, default `False` + If `True` includes a cluster distance variable in the deployment. + build_frequency_variables : bool, default `False` + If `True` includes the frequency variables in the deployment. variables_prefix : str, default "" Prefix for the variables in the deployment dictionary. - ... : - See :ref:`core-api-common-params`. + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) Raises ------ - `TypeError` + TypeError Invalid type of an argument Examples -------- - See the following function of the ``samples.py`` documentation script: + See the following function of the `samples.py` documentation script: - `samples.deploy_model_expert()` """ # Save the task arguments @@ -2062,7 +2119,7 @@ def extract_clusters( Parameters ---------- coclustering_file_path : str - Path of the coclustering model file (extension ``.khc`` or ``.khcj``). + Path of the coclustering model file (extension `.khc` or `.khcj`). cluster_variable : str Name of the variable for which the clusters are extracted. clusters_file_path : str @@ -2078,14 +2135,17 @@ def extract_clusters( Maximum number of parts totaled over all variables. If equal to 0 there is no limit. max_part_numbers : dict, optional - Dictionary that associate variable names to their maximum number of parts to - preserve in the simplified coclustering. If not set there is no limit. - ... : - See :ref:`core-api-common-params`. + Dictionary that associate variable names to their maximum number of + parts to preserve in the simplified coclustering. If not set there is + no limit. + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) Examples -------- - See the following function of the ``samples.py`` documentation script: + See the following function of the `samples.py` documentation script: - `samples.extract_clusters()` """ # Save the task arguments @@ -2121,25 +2181,27 @@ def detect_data_table_format( ---------- data_table_path : str Path of the data table file. - dictionary_file_path_or_domain : str or `.DictionaryDomain`, optional + dictionary_file_path_or_domain : str or DictionaryDomain, optional Path of a Khiops dictionary file or a DictionaryDomain object. dictionary_name : str, optional Name of the dictionary. - ... : - See :ref:`core-api-common-params`. + + See Also + -------- + - [Common Parameters](../notes.md#core-api-common-params) Returns ------- tuple A 2-tuple containing: - - the ``header_line`` boolean - - the ``field_separator`` character + - the `header_line` boolean + - the `field_separator` character These are exactly the parameters expected in many Khiops Python API functions. Examples -------- - See the following function of the ``samples.py`` documentation script: + See the following function of the `samples.py` documentation script: - `samples.detect_data_table_format()` """ # Save the task arguments diff --git a/khiops/core/coclustering_results.py b/khiops/core/coclustering_results.py index 7d069d88..de620988 100644 --- a/khiops/core/coclustering_results.py +++ b/khiops/core/coclustering_results.py @@ -12,33 +12,36 @@ are mostly compositions (has-a relations) and we omit native attributes (str, int, float, etc). -The main class of this module is `CoclusteringResults` and it is largely a composition -of sub-reports objects given by the following structure:: - - CoclusteringResults - |- coclustering_report -> CoclusteringReport - - CoclusteringReport - |- dimensions -> list of CoclusteringDimension - |- cells -> list of CoclusteringCell - - CoclusteringDimension - |- parts -> list of CoclusteringDimensionPart - |- inner_variable_dimensions -> list of CoclusteringDimension - |- clusters -> list of CoclusteringCluster - |- root_cluster -> CoclusteringCluster - - CoclusteringDimensionPartValueGroup - |- values -> list of CoclusteringDimensionPartValue - - CoclusteringCluster - |- leaf_part -> CoclusteringDimensionPart or None - |- parent_cluster | - |- child_cluster1 |-> CoclusteringCluster or None - |- child_cluster2 | +The main class of this module is [CoclusteringResults][] and it is largely a +composition +of sub-reports objects given by the following structure: + +```text +CoclusteringResults +|- coclustering_report -> CoclusteringReport + +CoclusteringReport +|- dimensions -> list of CoclusteringDimension +|- cells -> list of CoclusteringCell + +CoclusteringDimension +|- parts -> list of CoclusteringDimensionPart +|- inner_variable_dimensions -> list of CoclusteringDimension +|- clusters -> list of CoclusteringCluster +|- root_cluster -> CoclusteringCluster + +CoclusteringDimensionPartValueGroup +|- values -> list of CoclusteringDimensionPartValue + +CoclusteringCluster +|- leaf_part -> CoclusteringDimensionPart or None +|- parent_cluster | +|- child_cluster1 |-> CoclusteringCluster or None +|- child_cluster2 | +``` To have a complete illustration of the access to the information of all classes in this -module look at their ``to_dict`` methods which write Python dictionaries in the +module look at their [to_dict][] methods which write Python dictionaries in the same format as the Khiops JSON reports. """ import functools @@ -63,8 +66,9 @@ class CoclusteringResults(KhiopsJSONObject): Python dictionary representing the data of a Khiops Coclustering JSON report file. If not specified it returns an empty instance. - .. note:: - Prefer either the the `read_coclustering_results_file` function from the + !!! note + + Prefer the [read_coclustering_results_file][] function from the core API to obtain an instance of this class from a Khiops Coclustering JSON file. @@ -74,7 +78,7 @@ class CoclusteringResults(KhiopsJSONObject): Name of the Khiops tool that generated the JSON file. version : str Version of the Khiops tool that generated the JSON file. - coclustering_report : `CoclusteringReport` + coclustering_report : CoclusteringReport Coclustering modeling report. """ @@ -230,9 +234,10 @@ def to_dict(self): def write_report_file(self, report_file_path): # pragma: no cover """Writes a TSV report file with the object's information - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- @@ -250,13 +255,14 @@ def write_report_file(self, report_file_path): # pragma: no cover def write_report(self, stream_or_writer): # pragma: no cover """Writes the instance's TSV report to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - stream_or_writer : `io.IOBase` or `.KhiopsOutputWriter` + stream_or_writer : io.IOBase or KhiopsOutputWriter Output stream or writer. """ # Warn the user that this method is deprecated and will be removed @@ -294,7 +300,7 @@ def read_coclustering_results_file(json_file_path): Returns ------- - `.CoclusteringResults` + CoclusteringResults An instance of CoclusteringResults containing the report's information. """ return CoclusteringResults(json_data=flexible_json_load(json_file_path)) @@ -325,7 +331,7 @@ class CoclusteringReport: Parameters ---------- json_data : dict, optional - JSON data of the ``coclusteringReport`` field of a Khiops Coclustering JSON + JSON data of the `coclusteringReport` field of a Khiops Coclustering JSON report file. If not specified it returns an empty instance. Attributes @@ -339,9 +345,9 @@ class CoclusteringReport: level : float Measure between 0 and 1 measuring the information gain over the null model. initial_dimension_number : int - Initial number of dimensions. The number of dimensions (``len(dimensions)``) may + Initial number of dimensions. The number of dimensions (`len(dimensions)`) may be less than this quantity after a simplification (see - `~.api.simplify_coclustering`). + [api.simplify_coclustering][]). frequency_variable : str Name of the variable to be aggregated in the cells. By default is the number of individuals. @@ -356,10 +362,10 @@ class CoclusteringReport: selection_variable : str Variable used to select instances for training. selection_value : str - Value of ``selection_variable`` to select instances for training. - dimensions : list of `CoclusteringDimension` + Value of `selection_variable` to select instances for training. + dimensions : list of CoclusteringDimension Coclustering dimensions (variable). - cells : list of `CoclusteringCell` + cells : list of CoclusteringCell Coclustering cells. """ @@ -480,12 +486,12 @@ def get_dimension(self, dimension_name): Returns ------- - `CoclusteringDimension` + CoclusteringDimension The specified dimension. Raises ------ - `KeyError` + KeyError If no dimension with the specified names exist. """ return self._dimensions_by_name[dimension_name] @@ -533,13 +539,14 @@ def to_dict(self): def write_report(self, writer): # pragma: no cover """Writes the instance's TSV report to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output stream or writer. """ # Warn the user that this method is deprecated and will be removed @@ -557,13 +564,14 @@ def write_report(self, writer): # pragma: no cover def write_dimensions(self, writer): # pragma: no cover """Writes the "dimensions" section of the TSV report to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer for the report file. """ # Warn the user that this method is deprecated and will be removed @@ -579,13 +587,14 @@ def write_dimensions(self, writer): # pragma: no cover def write_coclustering_stats(self, writer): # pragma: no cover """Writes the "stats" section of the TSV report to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer for the report file. """ # Warn the user that this method is deprecated and will be removed @@ -613,13 +622,14 @@ def write_coclustering_stats(self, writer): # pragma: no cover def write_bounds(self, writer): # pragma: no cover """Writes the "bounds" section of the TSV report to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer for the report file. """ # Warn the user that this method is deprecated and will be removed @@ -645,13 +655,14 @@ def write_bounds(self, writer): # pragma: no cover def write_hierarchies(self, writer): # pragma: no cover """Writes the dimension reports' "hierarchy" sections to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer for the report file. """ # Warn the user that this method is deprecated and will be removed @@ -664,13 +675,14 @@ def write_hierarchies(self, writer): # pragma: no cover def write_compositions(self, writer): # pragma: no cover """Writes the dimensions' "composition" sections to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer for the report file. """ # Warn the user that this method is deprecated and will be removed @@ -683,13 +695,14 @@ def write_compositions(self, writer): # pragma: no cover def write_cells(self, writer): # pragma: no cover """Writes the "cells" section of the TSV report to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer for the report file. """ # Warn the user that this method is deprecated and will be removed @@ -711,13 +724,14 @@ def write_cells(self, writer): # pragma: no cover def write_annotations(self, writer): # pragma: no cover """Writes the dimensions' "annotation" sections to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer for the report file. """ # Warn the user that this method is deprecated and will be removed @@ -745,11 +759,12 @@ class CoclusteringDimension: It only has a no-parameter constructor. - .. note:: - The instance information is initialized with the `init_summary`, - `init_partition` and `init_hierarchy` methods. Its owner object (class - `CoclusteringReport`) uses the information found in the fields - ``dimensionSummaries``, ``dimensionPartitions`` and ``dimensionHierarchies`` to + !!! note + + The instance information is initialized with the [init_summary][], + [init_partition][] and [init_hierarchy][] methods. Its owner object (class + [CoclusteringReport][]) uses the information found in the fields + `dimensionSummaries`, `dimensionPartitions` and `dimensionHierarchies` to coherently initialize the all dimensions with these methods. Attributes @@ -757,15 +772,15 @@ class CoclusteringDimension: name : str Name of the variable associated to this dimension. is_variable_part : bool - ``True`` if the dimension is a part of a variable in an instance-variable + `True` if the dimension is a part of a variable in an instance-variable coclustering. type : "Numerical" or "Categorical" Dimension type. part_number : int Number of parts of the variable associated to this dimension. initial_part_number : int - Number of initial parts. Note that ``part_number`` <= ``initial_part_number`` - after a coclustering simplification (see `~.api.simplify_coclustering`). + Number of initial parts. Note that `part_number` <= `initial_part_number` + after a coclustering simplification (see [api.simplify_coclustering][]). value_number : int Number of values of the dimension's variable. interest : float @@ -776,19 +791,19 @@ class CoclusteringDimension: Minimum value of a numerical dimension/variable. max : float Maximum value of a numerical dimension/variable. - parts : list of `CoclusteringDimensionPart` + parts : list of CoclusteringDimensionPart Partition of this dimension. - inner_variable_dimensions : list of `CoclusteringDimension` - Variable part instance-variable coclustering dimensions. ``None`` for + inner_variable_dimensions : list of CoclusteringDimension + Variable part instance-variable coclustering dimensions. `None` for variable-variable clustering. Contains the dimensions of the variables involved in the instances x variables coclustering model. This model includes two dimensions: one for instances and one for variable - parts (``isVarPart`` set to ``True`` for this dimension). - clusters : list of `CoclusteringCluster` + parts (`isVarPart` set to `True` for this dimension). + clusters : list of CoclusteringCluster Clusters of this dimension's hierarchy. Note that includes intermediary clusters. - root_cluster : `CoclusteringCluster` + root_cluster : CoclusteringCluster Root cluster of the hierarchy. """ @@ -834,7 +849,7 @@ def init_summary(self, json_data=None): ---------- json_data : dict, optional Dictionary representing the data of an element of the list found at the - ``dimensionSummaries`` field of a Khiops Coclustering JSON report file. If + `dimensionSummaries` field of a Khiops Coclustering JSON report file. If not specified it leaves the object as-is. Returns @@ -870,7 +885,7 @@ def init_partition(self, json_data=None): ---------- json_data : dict, optional Python dictionary representing the data of an element of the list found at - the ``dimensionPartitions`` field of a Khiops Coclustering JSON report file. + the `dimensionPartitions` field of a Khiops Coclustering JSON report file. If not specified it leaves the object as-is. Returns @@ -990,7 +1005,7 @@ def init_hierarchy(self, json_data): ---------- json_data : dict, optional Python dictionary representing the data of an element of the list found at - the ``dimensionHierarchies`` field of a Khiops Coclustering JSON report + the `dimensionHierarchies` field of a Khiops Coclustering JSON report file. If not specified it leaves the object as-is. Returns @@ -1058,12 +1073,12 @@ def get_part(self, part_name): Returns ------- - `CoclusteringDimensionPart` + CoclusteringDimensionPart The part with the specified name. Raises ------ - `KeyError` + KeyError If there is no part with the specified name. """ return self._parts_by_name[part_name] @@ -1078,12 +1093,12 @@ def get_cluster(self, cluster_name): Returns ------- - `CoclusteringCluster` + CoclusteringCluster The specified cluster. Raises ------ - `KeyError` + KeyError If there is no cluster with the specified name. """ return self._clusters_by_name[cluster_name] @@ -1155,13 +1170,14 @@ def to_dict(self, report_type): def write_dimension_header_line(self, writer): # pragma: no cover """Writes the "dimensions" section header to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer for the report file. """ # Warn the user that this method is deprecated and will be removed @@ -1182,13 +1198,14 @@ def write_dimension_header_line(self, writer): # pragma: no cover def write_dimension_line(self, writer): # pragma: no cover """Writes the "dimensions" section line to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer for the report file. """ # Warn the user that this method is deprecated and will be removed @@ -1207,13 +1224,14 @@ def write_dimension_line(self, writer): # pragma: no cover def write_hierarchy(self, writer): # pragma: no cover """Writes the "hierarchy" section to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer for the report file. """ # Warn the user that this method is deprecated and will be removed @@ -1232,13 +1250,14 @@ def write_hierarchy(self, writer): # pragma: no cover def write_composition(self, writer): # pragma: no cover """Writes the "composition" section to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer for the report file. """ # Warn the user that this method is deprecated and will be removed @@ -1273,13 +1292,14 @@ def write_composition(self, writer): # pragma: no cover def write_annotation(self, writer): # pragma: no cover """Writes the "annotation" section to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer for the report file. """ # Warn the user that this method is deprecated and will be removed @@ -1316,9 +1336,10 @@ def write_hierarchy_structure_report_file( This method is mainly a test of the encoding of the cluster hierarchy. - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- @@ -1379,13 +1400,13 @@ class CoclusteringDimensionPartInterval(CoclusteringDimensionPart): ---------- json_data : dict, optional Python dictionary representing an element of type "Numerical" of the list at the - ``dimensionPartitions`` field of a Khiops Coclustering JSON report file. If not + `dimensionPartitions` field of a Khiops Coclustering JSON report file. If not specified it returns an empty instance. Raises ------ - `.KhiopsJSONError` - If ``json_data`` does not contain a "cluster" key. + KhiopsJSONError + If `json_data` does not contain a "cluster" key. Attributes @@ -1398,12 +1419,12 @@ class CoclusteringDimensionPartInterval(CoclusteringDimensionPart): Upper bound of the interval. is_missing : bool True if the instance's represent the missing values. In this case - ``lower_bound`` and ``upper_bound`` are set to ``None``. + `lower_bound` and `upper_bound` are set to `None`. is_left_open : bool - True if the interval is unbounded below ``lower_bound`` may contain the minimum + True if the interval is unbounded below `lower_bound` may contain the minimum value of the training data. is_right_open : bool - True if the interval is unbounded above ``upper_bound`` may contain the maximum + True if the interval is unbounded above `upper_bound` may contain the maximum value of training data. """ @@ -1482,19 +1503,19 @@ class CoclusteringDimensionPartValueGroup(CoclusteringDimensionPart): ---------- json_data : dict, optional Python dictionary representing an element of type "Categorical" of the list at - the ``dimensionPartitions`` field of a Khiops Coclustering JSON report file. If + the `dimensionPartitions` field of a Khiops Coclustering JSON report file. If None it returns an empty instance. Raises ------ - `.KhiopsJSONError` - If ``json_data`` does not contain a "cluster" key. + KhiopsJSONError + If `json_data` does not contain a "cluster" key. Attributes ---------- cluster_name : str Name of the cluster containing this group. - values : list of `CoclusteringDimensionPartValue` + values : list of CoclusteringDimensionPartValue The singleton parts composing this group part. is_default_part : bool True if the instance represents the "unknown values" group. @@ -1584,7 +1605,8 @@ def part_type(self): class CoclusteringDimensionPartValue: """A specific value of a variable in a dimension value group. - .. note:: + !!! note + This class has only a no-parameter constructor initializing an instance with the default values. @@ -1612,8 +1634,8 @@ class CoclusteringCluster: Parameters ---------- json_data : dict, optional - JSON data of an element of the list at the ``dimensionHierarchies`` field within - the ``coclusteringReport`` field of a Khiops Coclustering JSON report file. If + JSON data of an element of the list at the `dimensionHierarchies` field within + the `coclusteringReport` field of a Khiops Coclustering JSON report file. If not specified it returns an empty instance. Attributes @@ -1636,19 +1658,19 @@ class CoclusteringCluster: Rank of clusters in the hierarchy, with the smallest ranks being the closest from the root of the hierarchy. is_leaf : bool - ``True`` if the cluster is a leaf of the hierarchy. + `True` if the cluster is a leaf of the hierarchy. short_description : str Succinct cluster description. description : str Cluster description. - leaf_part : `CoclusteringDimensionPart` - On a leaf cluster: Its unique associated partition element. Otherwise ``None``. - parent_cluster : `CoclusteringCluster` - On a non-root cluster: Its unique parent cluster. Otherwise ``None``. - child_cluster1 : `CoclusteringCluster` - On a non-leaf cluster : The first child cluster. Otherwise ``None``. - child_cluster2 : `CoclusteringCluster` - On a non-leaf cluster : The second child cluster. Otherwise ``None``. + leaf_part : CoclusteringDimensionPart + On a leaf cluster: Its unique associated partition element. Otherwise `None`. + parent_cluster : CoclusteringCluster + On a non-root cluster: Its unique parent cluster. Otherwise `None`. + child_cluster1 : CoclusteringCluster + On a non-leaf cluster : The first child cluster. Otherwise `None`. + child_cluster2 : CoclusteringCluster + On a non-leaf cluster : The second child cluster. Otherwise `None`. """ def __init__(self, json_data=None): @@ -1708,13 +1730,14 @@ def to_dict(self): def write_hierarchy_header_line(self, writer): # pragma: no cover """Writes the "hierarchy" section's header to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer for the report file. """ # Warn the user that this method is deprecated and will be removed @@ -1734,13 +1757,14 @@ def write_hierarchy_header_line(self, writer): # pragma: no cover def write_hierarchy_line(self, writer): # pragma: no cover """Writes a line of the "hierarchy" section to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer for the report file. """ # Warn the user that this method is deprecated and will be removed @@ -1758,13 +1782,14 @@ def write_hierarchy_line(self, writer): # pragma: no cover def write_annotation_header_line(self, writer): # pragma: no cover """Writes the "annotation" section's header to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer for the report file. """ # Warn the user that this method is deprecated and will be removed @@ -1782,13 +1807,14 @@ def write_annotation_header_line(self, writer): # pragma: no cover def write_annotation_line(self, writer): # pragma: no cover """Writes a line of the "annotation" section to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer for the report file. """ # Warn the user that this method is deprecated and will be removed @@ -1807,13 +1833,14 @@ def write_hierarchy_structure_report(self, writer): # pragma: no cover This method is mainly a test of the encoding of the cluster hierarchy. - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in - Khiops 12. Use the `.to_dict` method instead. + Khiops 12. Use the [to_dict][] method instead. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer for the report file. """ # Warn the user that this method is deprecated and will be removed @@ -1838,13 +1865,14 @@ def write_hierarchy_structure_report(self, writer): # pragma: no cover class CoclusteringCell: """A coclustering cell - .. note:: + !!! note + This class has only a no-parameter constructor initializing an instance with the default values. Attributes ---------- - parts : list of `CoclusteringDimensionPart` + parts : list of CoclusteringDimensionPart Parts for each coclustering dimension. part_indexes : list of int Part indexes for each coclustering dimension. @@ -1861,13 +1889,14 @@ def __init__(self): def write_line(self, writer): # pragma: no cover """Writes a line of the instance's report to a writer object - .. warning:: + !!! warning + This method is *deprecated* since Khiops 11.0.0 and will be removed in Khiops 12. Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Warn the user that this method is deprecated and will be removed diff --git a/khiops/core/dictionary.py b/khiops/core/dictionary.py index 0c144de9..7a10931e 100644 --- a/khiops/core/dictionary.py +++ b/khiops/core/dictionary.py @@ -6,10 +6,11 @@ ###################################################################################### """Classes to manipulate Khiops Dictionary files -.. note:: +!!! note + To have a complete illustration of the access to the information of all classes in - this module look at their ``write`` methods which write them in Khiops Dictionary - file format (``.kdic``). + this module look at their `write` methods which write them in Khiops Dictionary + file format (`.kdic`). """ import io @@ -36,7 +37,7 @@ def _format_name(name): - """Formats a name of a dictionary or variable to a valid ``.kdic`` file identifier + """Formats a name of a dictionary or variable to a valid `.kdic` file identifier Returns unchanged the names that contain only "identifier" characters: - underscore @@ -95,7 +96,7 @@ def _check_name(name): Name to be validated. Raises ------ - `ValueError` + ValueError If the provided name does not comply with the formatting constraints. """ # Check that the type of name is string or bytes @@ -145,7 +146,8 @@ def _is_object_type(type_str): class DictionaryDomain: """Main class containing the information of a Khiops dictionary file - A DictionaryDomainain is a collection of `Dictionary` objects. These dictionaries + A `DictionaryDomain` is a collection of [Dictionary][] objects. These + dictionaries usually represent either a database schema or a predictor model. Parameters @@ -154,10 +156,12 @@ class DictionaryDomain: Python dictionary representing the data of a Khiops Dictionary JSON file. If not specified it returns an empty instance. - .. note:: - Prefer the `.read_dictionary_file` function from the core API to obtain an - instance of this class from a Khiops Dictionary file (``kdic`` or - ``kdicj``). + !!! note + + Prefer the [read_dictionary_file][] function from the core API to obtain + an + instance of this class from a Khiops Dictionary file (`kdic` or + `kdicj`). Attributes ---------- @@ -165,7 +169,7 @@ class DictionaryDomain: Name of the Khiops tool that generated the dictionary file. version : str Version of the Khiops tool that generated the dictionary file. - dictionaries : list of `Dictionary` + dictionaries : list of Dictionary The domain's dictionaries. """ @@ -218,7 +222,7 @@ def copy(self): Returns ------- - `DictionaryDomain` + DictionaryDomain A copy of this instance. """ dictionary_domain_copy = DictionaryDomain() @@ -239,8 +243,8 @@ def get_dictionary(self, dictionary_name): Returns ------- - `Dictionary` - The specified dictionary. ``None`` is returned if the dictionary name + Dictionary + The specified dictionary. `None` is returned if the dictionary name is not found. """ return self._dictionaries_by_name.get(dictionary_name) @@ -250,13 +254,13 @@ def add_dictionary(self, dictionary): Parameters ---------- - dictionary : `Dictionary` + dictionary : Dictionary The dictionary to be added. Raises ------ - `TypeError` - If ``dictionary`` is not of type ``Dictionary``. + TypeError + If `dictionary` is not of type `Dictionary`. """ if not isinstance(dictionary, Dictionary): raise TypeError(type_error_message("dictionary", dictionary, Dictionary)) @@ -268,12 +272,12 @@ def remove_dictionary(self, dictionary_name): Returns ------- - `Dictionary` + Dictionary The removed dictionary. Raises ------ - `KeyError` + KeyError If no dictionary with the specified name exists. """ dictionary = self._dictionaries_by_name.pop(dictionary_name) @@ -283,7 +287,8 @@ def remove_dictionary(self, dictionary_name): def extract_data_paths(self, source_dictionary_name): """Extracts the data paths for a dictionary in a multi-table schema - See :doc:`/multi_table_primer` for more details about data paths. + See [Multi-Table Learning Primer](../multi_table_primer.md) + for more details about data paths. Parameters ---------- @@ -307,7 +312,7 @@ def _extract_data_paths( ): """Builds the path for secondary tables and updates the entity list - `current_dictionary_alias` contains: + [current_dictionary_alias][] contains: - in the traversal, the name of the dictionary as it was named by the variable that referenced it; - or, otherwise, the name of an external dictionary (for Entity tables). @@ -372,16 +377,16 @@ def get_dictionary_at_data_path(self, data_path): ---------- data_path : str A data path for the specified table. Usually the output of - `extract_data_paths`. + [extract_data_paths][]. Returns ------- - `Dictionary` + Dictionary The dictionary object pointed by this data path. Raises ------ - `ValueError` + ValueError If the path is not found. """ # If data_path includes "`" and starts with an existing dictionary, @@ -483,12 +488,12 @@ def _get_dictionary_at_data_path(self, data_path): return dictionary def export_khiops_dictionary_file(self, kdic_file_path): - """Exports the domain in ``.kdic`` format + """Exports the domain in `.kdic` format Parameters ---------- kdic_file_path : str - Path of the output dictionary file (``.kdic``). + Path of the output dictionary file (`.kdic`). """ with io.BytesIO() as kdic_contents_stream: kdic_file_writer = self._json_object.create_output_file_writer( @@ -498,11 +503,11 @@ def export_khiops_dictionary_file(self, kdic_file_path): fs.write(kdic_file_path, kdic_contents_stream.getvalue()) def write(self, stream_or_writer): - """Writes the domain to a file writer in ``.kdic`` format + """Writes the domain to a file writer in `.kdic` format Parameters ---------- - stream_or_writer : `io.IOBase` or `.KhiopsOutputWriter` + stream_or_writer : io.IOBase or KhiopsOutputWriter Output stream or writer. """ if isinstance(stream_or_writer, io.IOBase): @@ -529,24 +534,24 @@ def read_dictionary_file(dictionary_file_path): Parameters ---------- - dictionary_file : str + dictionary_file_path : str Path of the file to be imported. The file can be either Khiops Dictionary - (extension ``kdic``) or Khiops JSON Dictionary (extension ``.json`` or - ``.kdicj``). + (extension `kdic`) or Khiops JSON Dictionary (extension `.json` or + `.kdicj`). Returns ------- - `.DictionaryDomain` + DictionaryDomain An dictionary domain representing the information in the dictionary file. Raises ------ - `ValueError` - When the file has an extension other than ``.kdic``, ``.kdicj`` or ``.json``. + ValueError + When the file has an extension other than `.kdic`, `.kdicj` or `.json`. Examples -------- - See the following functions of the ``samples.py`` documentation script: + See the following functions of the `samples.py` documentation script: - `samples.export_dictionary_files()` - `samples.train_predictor_with_cross_validation()` - `samples.multiple_train_predictor()` @@ -597,7 +602,7 @@ class Dictionary: Parameters ---------- json_data : dict, optional - Python dictionary representing an element of the list at the ``dictionaries`` + Python dictionary representing an element of the list at the `dictionaries` field of a Khiops Dictionary JSON file. If not specified returns an empty instance. @@ -609,9 +614,9 @@ class Dictionary: True if the dictionary is the root of an dictionary hierarchy. key : list of str Names of the key variables. - variables : list of `Variable` + variables : list of Variable The dictionary variables. - variable_blocks : list of `VariableBlock` + variable_blocks : list of VariableBlock The dictionary variable blocks. label : str Dictionary label. @@ -619,7 +624,7 @@ class Dictionary: List of dictionary comments. internal_comments : list of str List of internal dictionary comments. - meta_data : `MetaData` + meta_data : MetaData MetaData object of the dictionary. """ @@ -688,7 +693,7 @@ def copy(self): Returns ------- - `Dictionary` + Dictionary A copy of this instance. """ # Create an empty dictionary @@ -737,19 +742,19 @@ def get_value(self, key): Returns ------- - `MetaData` - Metadata value associated to the specified key. ``None`` is returned + MetaData + Metadata value associated to the specified key. `None` is returned if the metadata key is not found. """ return self.meta_data.get_value(key) def use_all_variables(self, is_used): - """Sets the ``used`` flag of all dictionary variables to the specified value + """Sets the `used` flag of all dictionary variables to the specified value Parameters ---------- is_used : bool - Sets the ``used`` field to ``is_used`` for all the `Variable` objects in + Sets the `used` field to `is_used` for all the [Variable][] objects in this dictionary. """ for variable in self.variables: @@ -765,8 +770,8 @@ def get_variable(self, variable_name): Returns ------- - `Variable` - The specified variable. ``None`` is returned if the variable name is + Variable + The specified variable. `None` is returned if the variable name is not found. """ return self._variables_by_name.get(variable_name) @@ -781,8 +786,8 @@ def get_variable_block(self, variable_block_name): Returns ------- - `VariableBlock` - The specified variable block. ``None`` is returned if the variable + VariableBlock + The specified variable block. `None` is returned if the variable block name is not found. """ return self._variable_blocks_by_name.get(variable_block_name) @@ -792,15 +797,15 @@ def add_variable(self, variable): Parameters ---------- - variable : `Variable` + variable : Variable The variable to be added. Raises ------ - `TypeError` - If variable is not of type `Variable` + TypeError + If variable is not of type [Variable][] - `ValueError` + ValueError If the name is empty or if there is already a variable with that name. """ if not isinstance(variable, Variable): @@ -835,10 +840,10 @@ def add_variable_from_spec( name : str Variable name. type : str - Variable type. See `Variable`. + Variable type. See [Variable][]. label : str, default "" Label of the variable. - used : bool, default ``True`` + used : bool, default `True` Usage status of the variable. object_type : str, optional Object type. Ignored if variable type not in ["Entity", "Table"]. @@ -852,7 +857,7 @@ def add_variable_from_spec( Raises ------ - `ValueError` + ValueError - If the variable name is empty or does not comply with the formatting constraints. - If there is already a variable with the same name. @@ -923,12 +928,12 @@ def remove_variable(self, variable_name): Returns ------- - `Variable` + Variable The removed variable. Raises ------ - `KeyError` + KeyError If no variable with the specified name exists. """ variable = self._variables_by_name.pop(variable_name) @@ -944,15 +949,15 @@ def add_variable_block(self, variable_block): Parameters ---------- - variable_block : `VariableBlock` + variable_block : VariableBlock The variable block to be added. Raises ------ - `TypeError` - If variable is not of type `VariableBlock` + TypeError + If variable is not of type [VariableBlock][] - `ValueError` + ValueError If the name is empty or if there is already a variable block with that name. """ @@ -979,29 +984,30 @@ def remove_variable_block( ): """Removes the specified variable block from this dictionary - .. note:: + !!! note + Non-native block variables (those created from block rules) are never kept in the dictionary. Parameters ---------- - variable_name : str + variable_block_name : str Name of the variable block to be removed. - keep_native_block_variables : bool, default ``True`` - If ``True`` and the block is native then only the block structure is + keep_native_block_variables : bool, default `True` + If `True` and the block is native then only the block structure is removed from the dictionary but the variables are kept in it; neither the variables point to the block nor the removed block points to the variables. - If ``False`` the variables are removed from the dictionary; the block + If `False` the variables are removed from the dictionary; the block preserves the references to their variables. Returns ------- - `VariableBlock` + VariableBlock The removed variable block. Raises ------ - `KeyError` + KeyError If no variable block with the specified name exists. """ removed_block = self.get_variable_block(variable_block_name) @@ -1026,26 +1032,26 @@ def remove_variable_block( return removed_block def is_key_variable(self, variable): - """Returns ``True`` if a variable belongs to this dictionary's key + """Returns `True` if a variable belongs to this dictionary's key Parameters ---------- - variable : `Variable` + variable : Variable The variable for the query. Returns ------- bool - ``True`` if the variable belong to the key. + `True` if the variable belong to the key. """ return variable.name in self.key def write(self, writer): - """Writes the dictionary to a file writer in ``.kdic`` format + """Writes the dictionary to a file writer in `.kdic` format Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output dictionary file. """ # Check file object type @@ -1104,7 +1110,7 @@ class Variable: Parameters ---------- json_data : dict, optional - Python dictionary representing an element of the list at the ``variables`` field + Python dictionary representing an element of the list at the `variables` field of dictionaries found in a Khiops Dictionary JSON file. If not specified it returns an empty instance. @@ -1116,21 +1122,21 @@ class Variable: True if the variable is used. type : str Variable type. - It can be either native (``Categorical``, ``Numerical``, ``Time``, - ``Date``, ``Timestamp``, ``TimestampTZ``, ``Text``), - internal (``TextList``, ``Structure``) + It can be either native (`Categorical`, `Numerical`, `Time`, + `Date`, `Timestamp`, `TimestampTZ`, `Text`), + internal (`TextList`, `Structure`) - See https://khiops.org/11.0.0-b.0/api-docs/kdic/text-list-rules/ - See https://khiops.org/11.0.0-b.0/api-docs/kdic/structures-introduction/ - or relational (``Entity`` - 0-1 relationship, ``Table`` - 0-n relationship) + or relational (`Entity` - 0-1 relationship, `Table` - 0-n relationship) - See https://khiops.org/11.0.0-b.0/tutorials/kdic_multi_table/ object_type : str - Type complement for the ``Table`` and ``Entity`` types. + Type complement for the `Table` and `Entity` types. structure_type : str - Type complement for the ``Structure`` type. Set to "" for other types. + Type complement for the `Structure` type. Set to "" for other types. rule : str Derivation rule or external table reference. Set to "" if there is no rule associated to this variable. Examples: @@ -1138,19 +1144,19 @@ class Variable: - standard rule: "Sum(Var1, Var2)" - reference rule: "[TableName]" - variable_block : `VariableBlock` + variable_block : VariableBlock Block to which the variable belongs. Not set if the variable does not belong to a block. label : str Variable label. comments : list of str List of variable comments. - meta_data : `MetaData` + meta_data : MetaData Variable metadata. Examples -------- - See the following function of the ``samples.py`` documentation script: + See the following function of the `samples.py` documentation script: - `samples.create_dictionary_domain()` """ @@ -1251,7 +1257,7 @@ def copy(self): Returns ------- - `Variable` + Variable A copy of this instance. """ variable = Variable() @@ -1271,14 +1277,14 @@ def get_value(self, key): Returns ------- - `MetaData` - Metadata value associated to the specified key. ``None`` is returned + MetaData + Metadata value associated to the specified key. `None` is returned if the metadata key is not found. """ return self.meta_data.get_value(key) def is_native(self): - """Returns ``True`` if the variable comes directly from a data column + """Returns `True` if the variable comes directly from a data column Variables are **not native** if they come from a derivation rule, an external entity, a sub-table or structures. @@ -1286,7 +1292,7 @@ def is_native(self): Returns ------- bool - ``True`` if a variables comes directly from a data column. + `True` if a variables comes directly from a data column. """ base_types = [ @@ -1304,7 +1310,7 @@ def is_native(self): return self.variable_block.rule == "" def is_relational(self): - """Returns ``True`` if the variable is of relational type + """Returns `True` if the variable is of relational type Relational variables reference other tables or external entities. @@ -1316,14 +1322,14 @@ def is_relational(self): return self.type in ["Entity", "Table"] def is_reference_rule(self): - """Returns ``True`` if the special reference rule is used + """Returns `True` if the special reference rule is used The reference rule is used to make reference to an external entity. Returns ------- bool - ``True`` if the special reference rule is used. + `True` if the special reference rule is used. """ if self.rule: if isinstance(self.rule, str): @@ -1352,11 +1358,11 @@ def full_type(self): return full_type def write(self, writer): - """Writes the domain to a file writer in ``.kdic`` format + """Writes the domain to a file writer in `.kdic` format Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Check file object type @@ -1406,9 +1412,9 @@ class VariableBlock: Parameters ---------- json_data : dict, optional - Python dictionary representing an element of the list at the ``variables`` field + Python dictionary representing an element of the list at the `variables` field of a dictionary object in a Khiops Dictionary JSON file. The element must have a - ``blockName`` field. If not specified it returns an empty instance. + `blockName` field. If not specified it returns an empty instance. Attributes ---------- @@ -1489,13 +1495,13 @@ def add_variable(self, variable): Parameters ---------- - variable : `Variable` + variable : Variable The variable to be added. Raises ------ - `TypeError` - If the variable is not of type `Variable`. + TypeError + If the variable is not of type [Variable][]. """ if not isinstance(variable, Variable): raise TypeError(type_error_message("variable", variable, Variable)) @@ -1511,13 +1517,13 @@ def remove_variable(self, variable): Parameters ---------- - variable : `Variable` + variable : Variable The variable to be removed. Raises ------ - `TypeError` - If the variable is not of type `Variable`. + TypeError + If the variable is not of type [Variable][]. """ # Check input if not isinstance(variable, Variable): @@ -1531,18 +1537,18 @@ def get_value(self, key): Returns ------- - `MetaData` - Metadata value associated to the specified key. ``None`` is returned + MetaData + Metadata value associated to the specified key. `None` is returned if the metadata key is not found. """ return self.meta_data.get_value(key) def write(self, writer): - """Writes the variable block to a file writer in ``.kdic`` format + """Writes the variable block to a file writer in `.kdic` format Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Output writer. """ # Check file object type @@ -1594,11 +1600,11 @@ class Rule: This object is a convenience feature which eases rule creation and serialization, especially in complex cases (rule operands which are - variables or rules themselves, sometimes upper-scoped). A `Rule` instance - must be converted to `str` before setting it in a `Variable` or - `VariableBlock` instance. + variables or rules themselves, sometimes upper-scoped). A [Rule][] instance + must be converted to [str][] before setting it in a [Variable][] or + [VariableBlock][] instance. - `Rule` instances can be created either from full operand specifications, or + [Rule][] instances can be created either from full operand specifications, or from verbatim rules. The latter is useful when the rule is retrieved from an existing variable or variable block and is used as an operand in another rule. @@ -1612,26 +1618,26 @@ class Rule: - bytes - int - float - - `Variable` - - `Rule` - - upper-scoped `Variable` - - upper-scoped `Rule` + - [Variable][] + - [Rule][] + - upper-scoped [Variable][] + - upper-scoped [Rule][] - The first element of the ``name_and_operands`` tuple is the name of the + The first element of the `name_and_operands` tuple is the name of the rule and must be str or bytes and non-empty for a standard rule, i.e. if - ``is_reference`` is not set. + `is_reference` is not set. verbatim : str or bytes, optional - Verbatim representation of an entire rule. If set, then ``names_and_operands`` + Verbatim representation of an entire rule. If set, then `names_and_operands` must be empty. - is_reference : bool, default ``False`` - If set to ``True``, then the rule is serialized as a reference rule: - ``Rule(Operand1, Operand2, ...)`` is serialized as - ``[Operand1, Operand2, ...]``. + is_reference : bool, default `False` + If set to `True`, then the rule is serialized as a reference rule: + `Rule(Operand1, Operand2, ...)` is serialized as + `[Operand1, Operand2, ...]`. Attributes ---------- - name : str or bytes or ``None`` - Name of the rule. It is ``None`` for reference rules. + name : str or bytes or `None` + Name of the rule. It is `None` for reference rules. operands : tuple of operands Each operand has one of the following types: @@ -1639,100 +1645,104 @@ class Rule: - bytes - int - float - - `Variable` - - `Rule` - - upper-scoped `Variable` - - upper-scoped `Rule` + - [Variable][] + - [Rule][] + - upper-scoped [Variable][] + - upper-scoped [Rule][] is_reference : bool The reference status of the rule. - .. note:: - This attribute cannot be changed on a `Rule` instance. + !!! note + + This attribute cannot be changed on a [Rule][] instance. Examples -------- - - basic rule, with variables as operands: - - verbatim: - .. code-block:: - - Product(PetalLength, PetalWidth) - - - object construction: - .. highlight:: python - .. code-block:: python - - petal_length_var = kh.Variable() - petal_length_var.name = "PetalLength" - petal_length_var.type = "Numerical" - petal_width_var = kh.Variable() - petal_width_var.name = "PetalWidth" - petal_width_var.type = "Numerical" - rule = kh.Rule("Product", petal_length_var, petal_width_var) - - - multi-table rule: - - verbatim: - .. code-block:: - - TableCount( - TableSelection( - Vehicles, - EQ(PassengerNumber, 1) - ) - ) + - basic rule, with variables as operands: + - verbatim: + + ``` + Product(PetalLength, PetalWidth) + ``` + + - object construction: + + ```python + petal_length_var = kh.Variable() + petal_length_var.name = "PetalLength" + petal_length_var.type = "Numerical" + petal_width_var = kh.Variable() + petal_width_var.name = "PetalWidth" + petal_width_var.type = "Numerical" + rule = kh.Rule("Product", petal_length_var, petal_width_var) + ``` + + - multi-table rule: + - verbatim: + + ``` + TableCount( + TableSelection( + Vehicles, + EQ(PassengerNumber, 1) + ) + ) + ``` - - object construction: - .. highlight:: python - .. code-block:: python + - object construction: - vehicles_var = accidents_dictionary.get_variable("Vehicles") - passenger_number_var = vehicles_dictionary.get_variable( - "PassengerNumber" - ) - rule = kh.Rule( - "TableCount", - kh.Rule( - "TableSelection", - vehicles_var, - kh.Rule("EQ", passenger_number_var, 1) - ) - ) + ```python + vehicles_var = accidents_dictionary.get_variable("Vehicles") + passenger_number_var = vehicles_dictionary.get_variable( + "PassengerNumber" + ) + rule = kh.Rule( + "TableCount", + kh.Rule( + "TableSelection", + vehicles_var, + kh.Rule("EQ", passenger_number_var, 1) + ) + ) + ``` - - multi-table rule with upper-scoped operands (advanced usage): - - verbatim: - .. code-block:: + - multi-table rule with upper-scoped operands (advanced usage): + - verbatim: - TableSelection( - Vehicles, - EQ( - PassengerNumber, - .TableMax(Vehicles, PassengerNumber) - ) - ) + ``` + TableSelection( + Vehicles, + EQ( + PassengerNumber, + .TableMax(Vehicles, PassengerNumber) + ) + ) + ``` - - object construction: - .. highlight:: python - .. code-block:: python + - object construction: - vehicles_var = accidents_dictionary.get_variable("Vehicles") - passenger_number_var = vehicles_dictionary.get_variable( - "PassengerNumber" - ) - rule = kh.Rule( - "TableSelection", - vehicles_var, + ```python + vehicles_var = accidents_dictionary.get_variable("Vehicles") + passenger_number_var = vehicles_dictionary.get_variable( + "PassengerNumber" + ) + rule = kh.Rule( + "TableSelection", + vehicles_var, + kh.Rule( + "EQ", + passenger_number_var, + kh.upper_scope( kh.Rule( - "EQ", - passenger_number_var, - kh.upper_scope( - kh.Rule( - "TableMax", - vehicle_var, - passenger_number_var - ) - ) + "TableMax", + vehicle_var, + passenger_number_var ) ) + ) + ) + ``` """ @@ -1808,28 +1818,29 @@ def copy(self): Returns ------- - `Rule` + Rule A copy of this instance. """ return Rule(self.name, *self.operands) def write(self, writer): - """Writes the rule to a file writer in the ``.kdic`` format + """Writes the rule to a file writer in the `.kdic` format - This method ensures proper `Rule` serialization, automatically handling: + This method ensures proper [Rule][] serialization, automatically handling: - back-quote recoding in variable names - double-quote recoding in categorical constants - - missing data (``inf``, ``-inf``, ``NaN``) serialization as ``#Missing`` - - upper-scope operator serialization as ``.`` + - missing data (`inf`, `-inf`, `NaN`) serialization as `#Missing` + - upper-scope operator serialization as `.` Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : [KhiopsOutputWriter][] Output writer. - .. note:: - ``self.name`` is not included in the serialization of reference rules. + !!! note + + `self.name` is not included in the serialization of reference rules. """ # Check the type of the writer if not isinstance(writer, KhiopsOutputWriter): @@ -1907,24 +1918,25 @@ def __repr__(self): def upper_scope(operand): - """Applies the upper-scope operator ``.`` to an operand + """Applies the upper-scope operator `.` to an operand Parameters ---------- - operand : `Variable`, `Rule`, upper-scoped `Variable` or upper-scoped `Rule` + operand : Variable, Rule, upper-scoped Variable or upper-scoped Rule Operand that is upper-scoped. Raises ------ - `TypeError` - If the type of ``operand`` is not `Variable`, `Rule`, upper-scoped `Variable` - or upper-scoped `Rule`. + TypeError + If the type of `operand` is not [Variable][], [Rule][], upper-scoped + [Variable][] + or upper-scoped [Rule][]. Returns ------- upper-scoped operand - The upper-scoped operand, as if the upper-scope operator ``.`` were - applied to an operand in a rule in the ``.kdic`` dictionary language. + The upper-scoped operand, as if the upper-scope operator `.` were + applied to an operand in a rule in the `.kdic` dictionary language. """ if not isinstance(operand, (Variable, Rule, _ScopedOperand)): @@ -1941,12 +1953,12 @@ class MetaData: The metadata for both dictionaries and variables is a list of key-value pairs. The values can be set either to a string, to a number, or to the boolean value True. The - latter represents flag metadata: they are either present (``True``) or absent. + latter represents flag metadata: they are either present (`True`) or absent. Parameters ---------- json_data : dict, optional - Python dictionary representing the object at a ``metaData`` field of a + Python dictionary representing the object at a `metaData` field of a dictionary domain, dictionary or variable in a Khiops Dictionary JSON file. If None it returns an empty instance. @@ -1955,7 +1967,7 @@ class MetaData: keys : list of str The metadata keys. values : list - Metadata values for each key in ``keys`` (synchronized lists). They can be + Metadata values for each key in `keys` (synchronized lists). They can be either str, int or float. """ @@ -1984,7 +1996,7 @@ def copy(self): Returns ------- - `MetaData` + MetaData A copy of this instance. """ new_meta_data = MetaData() @@ -1993,12 +2005,12 @@ def copy(self): return new_meta_data def is_empty(self): - """Returns ``True`` if the meta-data is empty + """Returns `True` if the meta-data is empty Returns ------- bool - Returns ``True`` if the meta-data is empty + Returns `True` if the meta-data is empty """ return len(self.keys) == 0 @@ -2008,13 +2020,13 @@ def get_value(self, key): Returns ------- int, str or float - The value at the specified key. ``None`` is returned if the key is + The value at the specified key. `None` is returned if the key is not found. Raises ------ - `TypeError` - If ``key`` is not str. + TypeError + If `key` is not str. """ # Check the argument types if not is_string_like(key): @@ -2040,10 +2052,10 @@ def add_value(self, key, value): Raises ------ - `TypeError` + TypeError - If the key is not a valid string - If the value is not a valid string or if is not bool, int, float. - `ValueError` + ValueError If the key is already stored. """ # Check that the type of key is string-like @@ -2078,9 +2090,9 @@ def remove_key(self, key): Raises ------ - `TypeError` + TypeError If the key is not str. - `KeyError` + KeyError If the key is not contained in this metadata. """ # Check that the type of key is string-like @@ -2097,11 +2109,11 @@ def remove_key(self, key): raise KeyError(key) def write(self, writer): - """Writes the metadata to a file writer in ``.kdic`` format + """Writes the metadata to a file writer in `.kdic` format Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : [KhiopsOutputWriter][] Output writer. """ if not isinstance(writer, KhiopsOutputWriter): diff --git a/khiops/core/helpers.py b/khiops/core/helpers.py index c900df08..599cbe3c 100644 --- a/khiops/core/helpers.py +++ b/khiops/core/helpers.py @@ -31,26 +31,26 @@ def _build_multi_table_dictionary_domain( """Builds a multi-table dictionary domain from a dictionary with a key Parameters ---------- - dictionary_domain : `.DictionaryDomain` + dictionary_domain : DictionaryDomain DictionaryDomain object. Its root dictionary must have its key set. root_dictionary_name : str Name for the new root dictionary secondary_table_variable_name : str Name, in the root dictionary, for the "table" variable of the secondary table. - update_secondary_table_name : bool, default `False` - If ``True``, then update the secondary table name according to the + update_secondary_table_name : bool, default False + If `True`, then update the secondary table name according to the secondary table variable name. If not set, keep original table name. Returns ------- - `.DictionaryDomain` + DictionaryDomain The new dictionary domain Raises ------ - `TypeError` + TypeError Invalid type of an argument - `ValueError` + ValueError Invalid values of an argument: - the dictionary domain doesn't contain at least a dictionary - the dictionary domain's root dictionary doesn't have a key set @@ -164,22 +164,22 @@ def deploy_coclustering( r"""Deploys a coclustering on a data table This procedure generates the following files: - - ``coclustering_dictionary_file_path``: A multi-table dictionary file for + - `coclustering_dictionary_file_path`: A multi-table dictionary file for further deployments of the coclustering with deploy_model - - ``output_data_table_path``: A data table file containing the deployed + - `output_data_table_path`: A data table file containing the deployed coclustering model Parameters ---------- - dictionary_file_path_or_domain : str or `.DictionaryDomain` + dictionary_file_path_or_domain : str or DictionaryDomain Path of a Khiops dictionary file or a DictionaryDomain object. dictionary_name : str Name of the dictionary to be analyzed. data_table_path : str Path of the data table file. coclustering_file_path : str - Path of the coclustering model file (extension ``.khc`` or ``.khcj``). - .. note:: + Path of the coclustering model file (extension `.khc` or `.khcj`). + !!! note Instance-variable coclustering is not currently supported. @@ -191,16 +191,16 @@ def deploy_coclustering( Path of the coclustering dictionary file to deploy. output_data_table_path : str Path of the output data file. - detect_format : bool, default ``True`` + detect_format : bool, default `True` If True detects automatically whether the data table file has a header and its - field separator. It's ignored if ``header_line`` or ``field_separator`` are set. - header_line : bool, optional (default ``True`` if ``detect_format`` is False) + field separator. It's ignored if `header_line` or `field_separator` are set. + header_line : bool, optional (default `True` if `detect_format` is False) If True it uses the first line of the data as column names. Overrides - ``detect_format`` if set. - field_separator : str, optional (default "\\t" if ``detect_format`` is False) - A field separator character, overrides ``detect_format`` if set ("" counts + `detect_format` if set. + field_separator : str, optional (default "\\t" if `detect_format` is False) + A field separator character, overrides `detect_format` if set ("" counts as "\\t"). - output_header_line : bool, default ``True`` + output_header_line : bool, default `True` If True writes a header line containing the column names in the output table. output_field_separator : str, default "\\t" A field separator character (empty string counts as tab). @@ -214,10 +214,10 @@ def deploy_coclustering( Maximum number of parts totaled over all variables. If equal to 0 there is no limit. max_part_numbers : dict, optional - Dictionary associating variable names to their maximum number of parts to - preserve in the simplified coclustering. For variables not present in - ``max_part_numbers`` there is no limit. - build_cluster_variable : bool, default ``True`` + Dictionary associating variable names to their maximum number of parts + to preserve in the simplified coclustering. For variables not present + in `max_part_numbers` there is no limit. + build_cluster_variable : bool, default `True` If True includes a cluster id variable in the deployment. build_distance_variables : bool, default False If True includes a cluster distance variable in the deployment. @@ -225,8 +225,10 @@ def deploy_coclustering( If True includes the frequency variables in the deployment. variables_prefix : str, default "" Prefix for the variables in the deployment dictionary. - ... : - Options of the `.KhiopsRunner.run` method from the class `.KhiopsRunner`. + + See Also + -------- + - [KhiopsRunner.run][] : Additional options. Returns ------- @@ -238,16 +240,16 @@ def deploy_coclustering( Raises ------ - `TypeError` - Invalid type ``dictionary_file_path_or_domain`` or ``key_variable_names`` - `ValueError` - If the type of the dictionary key variables is not equal to ``Categorical`` - `NotImplementedError` + TypeError + Invalid type `dictionary_file_path_or_domain` or `key_variable_names` + ValueError + If the type of the dictionary key variables is not equal to `Categorical` + NotImplementedError If the coclustering to be deployed is of the instance-variable type Examples -------- - See the following function of the ``samples.py`` documentation script: + See the following function of the `samples.py` documentation script: - `samples.deploy_coclustering()` """ # Fail early for instance-variable coclustering, which is not supported @@ -392,13 +394,14 @@ def deploy_predictor_for_metrics( - The predicted value of the target variable - The probabilities of each value of the target variable *(classifier only)* - .. note:: + !!! note + To obtain the data of the default Khiops test dataset use ``sample_percentage = - 70`` and ``sampling_mode = "Exclude sample"``. + 70` and `sampling_mode = "Exclude sample"``. Parameters ---------- - dictionary_file_path_or_domain : str or `.DictionaryDomain` + dictionary_file_path_or_domain : str or DictionaryDomain Path of a Khiops dictionary file or a DictionaryDomain object. dictionary_name : str Name of the predictor dictionary. @@ -406,31 +409,34 @@ def deploy_predictor_for_metrics( Path of the data table file. output_data_table_path : str Path of the scores output data file. - detect_format : bool, default ``True`` + detect_format : bool, default `True` If True detects automatically whether the data table file has a header and its - field separator. It's ignored if ``header_line`` or ``field_separator`` are set. - header_line : bool, optional (default ``True`` if ``detect_format`` is ``False``) + field separator. It's ignored if `header_line` or `field_separator` are set. + header_line : bool, optional (default `True` if `detect_format` is `False`) If True it uses the first line of the data as column names. Overrides - ``detect_format`` if set. - field_separator : str, optional (default "\\t" if ``detect_format`` is ``False``) - A field separator character, overrides ``detect_format`` if set ("" counts + `detect_format` if set. + field_separator : str, optional (default "\\t" if `detect_format` is `False`) + A field separator character, overrides `detect_format` if set ("" counts as "\\t"). sample_percentage : int, default 70 - See ``sampling_mode`` option below. + See `sampling_mode` option below. sampling_mode : "Include sample" or "Exclude sample", default "Include sample" - If equal to "Include sample" deploys the predictor on ``sample_percentage`` + If equal to "Include sample" deploys the predictor on `sample_percentage` percent of data and if equal to "Exclude sample" on the complementary ``100 - sample_percentage`` percent of data. additional_data_tables : dict, optional A dictionary containing the data paths and file paths for a multi-table - dictionary file. For more details see :doc:`/multi_table_primer` documentation. - output_header_line : bool, default ``True`` + dictionary file. For more details see + [Multi-Table Learning Primer](../multi_table_primer.md) documentation. + output_header_line : bool, default `True` If True writes a header line containing the column names in the output table. output_field_separator : str, default "\\t" A field separator character ("" counts as "\\t"). - ... : - Options of the `.KhiopsRunner.run` method from the class `.KhiopsRunner`. - """ + + See Also + -------- + - [KhiopsRunner.run][] : Additional options. + -""" # Check the dictionary domain api._check_dictionary_file_path_or_domain(dictionary_file_path_or_domain) @@ -489,7 +495,7 @@ def visualize_report(report_path): Before using this function, make sure you have installed the Khiops Visualization app and/or the Khiops Co-Visualization app. More info at - ``_ + Parameters ---------- @@ -499,11 +505,11 @@ def visualize_report(report_path): Raises ------ - `ValueError` + ValueError If the report file path does not have extension '.khj' or '.khcj'. - `FileNotFoundError` + FileNotFoundError If the report file does not exist. - `RuntimeError` + RuntimeError If the report file is executable. """ # Check that the report path: diff --git a/khiops/core/internals/common.py b/khiops/core/internals/common.py index 4a62318b..5d2a5a71 100644 --- a/khiops/core/internals/common.py +++ b/khiops/core/internals/common.py @@ -11,8 +11,9 @@ class SystemSettings: """Khiops system settings - .. note:: - These settings are not available in the `CommandLineOptions`. + !!! note + + These settings are not available in the [CommandLineOptions][]. """ def __init__( @@ -42,10 +43,10 @@ def check(self): Raises ------ - `TypeError` + TypeError If any of the system settings does not have the proper type. - `ValueError` - If ``max_cores`` or ``memory_limit_mb`` are set to negative numbers. + ValueError + If `max_cores` or `memory_limit_mb` are set to negative numbers. """ # Check the field types and ranges where applicable if self.max_cores is not None: @@ -88,14 +89,14 @@ class CommandLineOptions: Attributes ---------- log_file_path : str, default "" - Path of the log file for the Khiops process (command line option ``-e`` of the + Path of the log file for the Khiops process (command line option `-e` of the desktop app). If equal to "" then it writes no log file. output_scenario_path : str, default "" - Path of the output Khiops scenario file (command line option ``-o`` of the + Path of the output Khiops scenario file (command line option `-o` of the desktop app). If the empty string is specified no output scenario file is generated. task_file_path : str, default "" - Path of the task file for the Khiops process (command line option ``-p`` of the + Path of the task file for the Khiops process (command line option `-p` of the desktop app). If equal to "" then it writes no task file. """ @@ -149,7 +150,7 @@ def check(self): Raises ------ - `TypeError` + TypeError If any of the command line options does not have the proper type. """ if self.output_scenario_path and not is_string_like(self.output_scenario_path): @@ -183,7 +184,7 @@ def type_error_message(variable_name, variable, *target_types): variable : any Actual variable for whom the type error is signaled. target_types : list - Expected types for ``variable``, either as a type or as a string. + Expected types for `variable`, either as a type or as a string. Returns ------- @@ -268,7 +269,8 @@ def is_string_like(test_object): def is_list_like(list_like): """Returns True if an object is list-like - An object is ``list-like`` if and only if inherits from `collections.abc.Sequence` + An object is `list-like` if and only if inherits from + [collections.abc.Sequence][] and it is not `string-like ` """ return isinstance(list_like, Sequence) and not is_string_like(list_like) @@ -277,8 +279,8 @@ def is_list_like(list_like): def is_dict_like(test_object): """Returns True if an object is dict-like - An object is ``dict-like`` if and only if inherits from the - `collections.abc.Mapping`. + An object is `dict-like` if and only if inherits from the + [collections.abc.Mapping][]. """ return isinstance(test_object, Mapping) diff --git a/khiops/core/internals/filesystems.py b/khiops/core/internals/filesystems.py index 287ea395..49558a11 100644 --- a/khiops/core/internals/filesystems.py +++ b/khiops/core/internals/filesystems.py @@ -63,13 +63,17 @@ def _parent_path(path): Notes ----- This function always returns a posix path ("/" as separator). For example for the - windows path:: + windows path: - C:\Program Files\khiops + ```text + C:\Program Files\khiops + ``` - this method returns:: + this method returns: - C:/Program Files + ```text + C:/Program Files + ``` """ return Path(path).parent.as_posix() @@ -79,12 +83,12 @@ def _parent_uri_info(uri_info): Parameters ---------- - uri_info : `urllib.parse.ParseResult` - URI info structure (output of `urllib.parse.urlparse`) + uri_info : urllib.parse.ParseResult + URI info structure (output of [urllib.parse.urlparse][]) Returns ------- - `urllib.parse.ParseResult` + urllib.parse.ParseResult URI info structure for the parent URI """ @@ -97,13 +101,18 @@ def _child_path(path, child_name): Notes ----- This function always returns a posix path ("/" as separator). For example for the - windows path and child name:: + windows path and child name: - parent: C:\Program Files - child: khiops + ```text + parent: C:\Program Files + child: khiops + ``` - this method returns:: - C:/Program Files/khiops + this method returns: + + ```text + C:/Program Files/khiops + ``` """ return Path(path).joinpath(child_name).as_posix() @@ -113,15 +122,15 @@ def _child_uri_info(uri_info, child_name): Parameters ---------- - uri_info : `urllib.parse.ParseResult` - URI info structure (output of `urllib.parse.urlparse`) + uri_info : urllib.parse.ParseResult + URI info structure (output of [urllib.parse.urlparse][]) - child_name : `str` + child_name : str Name of the new child node Returns ------- - `urllib.parse.ParseResult` + urllib.parse.ParseResult URI info structure for the child URI """ return uri_info._replace(path=_child_path(uri_info.path, child_name)) @@ -135,15 +144,16 @@ def _child_uri_info(uri_info, child_name): def is_local_resource(uri_or_path): r"""Checks if a URI or path is effectively a local path - .. note:: + !!! note + An URI with scheme of size 1 will be considered a local path. This is to take - into account Windows paths such as ``C:\Some\Windows\Path``. + into account Windows paths such as `C:\Some\Windows\Path`. Returns ------- - `bool` - `True` if a URI refers to a local path + bool + [True][] if a URI refers to a local path """ if (index := uri_or_path.find("://")) > 0: scheme = uri_or_path[:index] @@ -160,14 +170,14 @@ def create_resource(uri_or_path): uri_or_path : str The resource's URI . Supported protocols/schemes: - - ``file`` or empty: Local filesystem resource - - ``s3``: Amazon S3 resource - - ``gs``: Google Cloud Storage resource - - ``https``: Azure Storage resource (files or blobs) + - `file` or empty: Local filesystem resource + - `s3`: Amazon S3 resource + - `gs`: Google Cloud Storage resource + - `https`: Azure Storage resource (files or blobs) Returns ------- - `FilesystemResource` + FilesystemResource The URI resource object, its class depends on the URI. """ # Case where the URI scheme separator `://` is contained in the uri/path @@ -233,7 +243,7 @@ def read(uri_or_path, size=None): Returns ------- - `bytes` + bytes A buffer containing the read contents. Raises @@ -251,7 +261,7 @@ def write(uri_or_path, data): ---------- uri_or_path : str The resource's URI or local filesystem path. - data : str or `bytes` + data : str or bytes The data to be written. Raises @@ -272,7 +282,7 @@ def exists(uri_or_path): Returns ------- - `bool` + bool True if the resource exists. """ return create_resource(uri_or_path).exists() @@ -439,7 +449,7 @@ def create_child(self, file_name): Returns ------- - `FilesystemResource` + FilesystemResource The specific resource type is that of the caller """ @@ -449,7 +459,7 @@ def create_parent(self): Returns ------- - `FilesystemResource` + FilesystemResource The specific resource type is that of the caller """ @@ -610,25 +620,26 @@ class AmazonS3Resource(FilesystemResource): The default configuration and credentials are read from the paths - - ``~/.aws/configuration`` - - ``~/.aws/credentials`` + - `~/.aws/configuration` + - `~/.aws/credentials` The location of the configuration and credentials files may be overridden using the following environment variables: - - ``AWS_CONFIG_FILE``: location of the configuration file - - ``AWS_SHARED_CREDENTIALS_FILE``: location of the credentials file + - `AWS_CONFIG_FILE`: location of the configuration file + - `AWS_SHARED_CREDENTIALS_FILE`: location of the credentials file If no configuration/credentials files are usable, Amazon SDK defaults apply. Individual settings such as endpoint URL or region can be used to override any of the available settings. - Other relevant environment variables:: + Other relevant environment variables: - - AWS_S3_ENDPOINT_URL: sets the service endpoint URL - - AWS_DEFAULT_REGION: sets the region to send requests to + - `AWS_S3_ENDPOINT_URL`: sets the service endpoint URL + - `AWS_DEFAULT_REGION`: sets the region to send requests to + + !!! note - .. note:: Operations with the s3 client are only verified by checking that the HTTP response code is in the 200 range. @@ -769,7 +780,8 @@ def create_parent(self): class AzureStorageResourceMixin: """Azure compatible Storage Resource Mixin - See `AzureStorageFileResource` and `AzureStorageBlobResource` for more details. + See [AzureStorageFileResource][] and [AzureStorageBlobResource][] for more + details. """ @@ -878,9 +890,9 @@ def exists(self): # Calls to `ShareFileClient` and `ShareDirectoryClient` are required # because : - # - the check against `file_share_client` is ``False`` + # - the check against `file_share_client` is `False` # if the target is a directory. - # - the check against `directory_share_client` is ``False`` + # - the check against `directory_share_client` is `False` # if the target is a file. return self.file_share_client.exists() or self.directory_share_client.exists() @@ -918,7 +930,8 @@ def copy_to_local(self, local_path): def list_dir(self): """List the files (not the directories) of the current directory - .. note:: + !!! note + This is not a recursive listing operation. """ diff --git a/khiops/core/internals/io.py b/khiops/core/internals/io.py index ad2590ed..dfe0f089 100644 --- a/khiops/core/internals/io.py +++ b/khiops/core/internals/io.py @@ -34,18 +34,18 @@ def encode_file_path(file_path): Returns ------- - `bytes` - If ``file_path`` is str + bytes + If `file_path` is str - In Windows : The path decoded to UTF-8 excepting the "ANSI" Unicode characters. - In Linux/Unix/Mac : The path decoded to UTF-8. - If ``file_path`` is `bytes`: - It just returns the input ``file_path`` + If `file_path` is [bytes][]: + It just returns the input `file_path` Raises ------ - `TypeError` - If ``file_path`` is not str or bytes + TypeError + If `file_path` is not str or bytes """ # Check input type if not is_string_like(file_path): @@ -120,12 +120,12 @@ class KhiopsJSONObject: Parameters ---------- json_data : dict, optional - Python dictionary representing the data of a Khiops JSON file. If None an empty - it returns an empty object. + Python dictionary representing the data of a Khiops JSON file. + If [None][] or empty it returns an empty object. Raises ------ - `~.KhiopsJSONError` + KhiopsJSONError If the JSON data is invalid. Attributes @@ -136,7 +136,7 @@ class KhiopsJSONObject: Version of the Khiops tool that generated the file. khiops_encoding : str, optional Custom encoding used by Khiops in the file. Valid values: - - ``None`` : for backwards compatibility + - `None` : for backwards compatibility - "ascii": ASCII encoding - "ansi": ANSI encoding - "utf8": UTF-8 encoding @@ -147,12 +147,13 @@ class KhiopsJSONObject: family such as PataText or Enneade. json_key_sort_spec : dict, optional Dictionary that specifies the order of the keys in the Khiops JSON report. - Its values are `None`, except when they are dictionaries themselves. + Its values are [None][], except when they are dictionaries themselves. + + !!! note - .. note:: This is a class attribute that can be set in subclasses, to specify a key order when serializing the report in a JSON file, via the - ``write_khiops_json_file`` method. + [write_khiops_json_file][] method. json_data : dict Python dictionary extracted from the Khiops JSON report file. @@ -246,12 +247,12 @@ def create_output_file_writer(self, stream): Parameters ---------- - stream : `io.IOBase` + stream : io.IOBase An output stream object. Returns ------- - `.KhiopsOutputWriter` + KhiopsOutputWriter An output file object. """ if self.khiops_encoding is None or self.khiops_encoding in ["ascii", "utf8"]: @@ -330,7 +331,7 @@ def write_khiops_json_file(self, json_file_path, _ensure_ascii=False): """Write the JSON data of the object to a Khiops JSON file The JSON keys are sorted according to the - ``KhiopsJSONObject.json_key_sort_spec`` class attribute, if set. + `KhiopsJSONObject.json_key_sort_spec` class attribute, if set. Otherwise, the JSON keys are not sorted. Parameters @@ -360,15 +361,15 @@ class KhiopsOutputWriter: Parameters ---------- - stream : `io.IOBase` + stream : io.IOBase A writable output stream. Special text transformations in buffers inheriting - from `io.TextIOBase` are ignored. + from [io.TextIOBase][] are ignored. force_ansi : bool, default False All output written will be transformed back ANSI characters in that range that were recoded to UTF-8. ansi_unicode_chars : list of str, optional A list of UTF-8 characters with equivalents in the ANSI 128-256 range which will - be encoded back to ANSI when writing with ``force_ansi`` is ``True``. By default + be encoded back to ANSI when writing with `force_ansi` is `True`. By default all UTF-8 equivalents of the ANSI 128-256 will be encoded back. """ diff --git a/khiops/core/internals/runner.py b/khiops/core/internals/runner.py index e9d7cb71..89ed8ddf 100644 --- a/khiops/core/internals/runner.py +++ b/khiops/core/internals/runner.py @@ -54,11 +54,11 @@ def get_default_samples_dir(): """Returns the default samples directory The default samples directory is computed according to the following priorities: - - all systems: ``KHIOPS_SAMPLES_DIR/khiops_data/samples`` if set + - all systems: `KHIOPS_SAMPLES_DIR/khiops_data/samples` if set - Windows: - - ``%PUBLIC%\\khiops_data\\samples`` if ``%PUBLIC%`` is defined - - ``%USERPROFILE%\\khiops_data\\samples`` otherwise - - Linux/macOS: ``$HOME/khiops_data/samples`` + - `%PUBLIC%\\khiops_data\\samples` if `%PUBLIC%` is defined + - `%USERPROFILE%\\khiops_data\\samples` otherwise + - Linux/macOS: `$HOME/khiops_data/samples` """ if "KHIOPS_SAMPLES_DIR" in os.environ and os.environ["KHIOPS_SAMPLES_DIR"]: samples_dir = os.environ["KHIOPS_SAMPLES_DIR"] @@ -119,7 +119,7 @@ def _check_samples_dir(samples_dir): def _khiops_env_file_exists(env_dir): - """Check ``khiops_env`` exists relative to the specified environment dir""" + """Check `khiops_env` exists relative to the specified environment dir""" khiops_env_path = os.path.join(env_dir, "khiops_env") if platform.system() == "Windows": khiops_env_path += ".cmd" @@ -135,7 +135,8 @@ def _infer_env_bin_dir_for_conda_based_installations(): str absolute path of the 'bin' dir where the khiops binaries are installed - .. note:: + !!! note + Borderline case : if no Conda-based Khiops installation is found this function will return 'bin' """ @@ -167,11 +168,12 @@ def _infer_base_dir_for_conda_based_or_pip_installations(): str An absolute path to the base directory - .. note:: + !!! note + It returns an empty string if it detects a borderline installation """ assert os.path.basename(Path(__file__).parents[2]) == "khiops", ( - "Please fix the `Path.parents` in this method " + "Please fix the [Path.parents][] in this method " "so it finds environment directory of this module" ) @@ -243,7 +245,8 @@ def _infer_khiops_installation_method(trace=False): - 'conda-based' environment is similar to 'conda' except that it was not activated previously nor during the execution and thus the CONDA_PREFIX environment variable is undefined - and the path to the `bin` directory inside the conda environment is not in PATH + and the path to the `bin/` directory inside the Conda environment is not in + PATH - 'pip' environment containing binaries, shared libraries and the Python libraries can either be: - system-wide (strongly discouraged) @@ -365,9 +368,9 @@ def _initialize_root_temp_dir(self): It tries to set a proper root temporary directory. It tries the following strategies in order: - - Check that ``$TEMP/khiops/python`` exists and use it + - Check that `$TEMP/khiops/python` exists and use it - Try to create ``$TEMP/khiops/python` and use it - - Create a ``$TEMP/khiops_/python`` and use it + - Create a `$TEMP/khiops_/python` and use it """ # Create the directory if it doesn't exists @@ -386,22 +389,22 @@ def _initialize_root_temp_dir(self): @property def root_temp_dir(self): - r"""str: The runner's temporary directory + r"""[str][]: The runner's temporary directory The temporary scenarios/templates and dictionary files created by the Khiops Python library are stored here. Default value: - - Windows: ``%TEMP%\khiops\python`` - - Linux: ``$TMP/khiops/python`` + - Windows: `%TEMP%\khiops\python` + - Linux: `$TMP/khiops/python` When set to a local path it tries to create the specified directory if it doesn't exist. Raises ------ - `.KhiopsEnvironmentError` - If set to a local path: if it is a file or if it does not have ``+rwx`` + KhiopsEnvironmentError + If set to a local path: if it is a file or if it does not have `+rwx` permissions. """ return self._root_temp_dir @@ -429,7 +432,8 @@ def root_temp_dir(self, dir_path): def create_temp_file(self, prefix, suffix): """Creates a unique temporary file in the runner's root temporary directory - .. note:: + !!! note + For remote filesystems no actual file is created, just a (highly probable) unique path is returned. @@ -490,7 +494,10 @@ def create_temp_dir(self, prefix): @property def samples_dir(self): - r"""str: Location of the Khiops' sample datasets directory. May be an URL/URI""" + """[str][]: Location of the Khiops' sample datasets directory + + Can be an URI. + """ return self._get_samples_dir() def _get_samples_dir(self): @@ -511,7 +518,7 @@ def _set_samples_dir(self, samples_dir): @property def khiops_version(self): - """`.KhiopsVersion`: The version of the Khiops backend of this runner""" + """[KhiopsVersion][]: The version of the Khiops backend of this runner""" return self._get_khiops_version() def _get_khiops_version(self): @@ -543,7 +550,7 @@ def _build_status_message(self): # is still in the 'khiops.core.internals' package assert ( os.path.basename(Path(__file__).parents[2]) == "khiops" - ), "Please fix the `Path.parents` in this method " + ), "Please fix the [Path.parents][] in this method " library_root_dir_path = Path(__file__).parents[2] status_msg = "Khiops Python library settings\n" @@ -606,18 +613,18 @@ def run( Parameters ---------- - task : `.KhiopsTask` + task : KhiopsTask Khiops task to be run. task_args : dict Arguments for the task. - command_line_options : `.CommandLineOptions`, optional + command_line_options : CommandLineOptions, optional Command line options for all tasks. If not set the default values are used. - See the `.CommandLineOptions` for more information. - trace : bool, default ``False`` + See the [CommandLineOptions][] for more information. + trace : bool, default `False` If True prints the command line executed of the process and does not delete any temporary files created. - system_settings : `.SystemSettings`, optional - *Advanced:* System settings for all tasks. See the `.SystemSettings` + system_settings : SystemSettings, optional + *Advanced:* System settings for all tasks. See the [SystemSettings][] class for more information. stdout_file_path : str, default "" *Advanced* Path to a file where the Khiops process writes its stdout stream. @@ -629,19 +636,19 @@ class for more information. Normally Khiops should not write to this stream but MPI, filesystems plugins or debug versions may do it. The stream is captured with a UTF-8 encoding and replacing encoding errors. If equal to "" then it writes no file. - force_ansi_scenario : bool, default ``False`` + force_ansi_scenario : bool, default `False` *Advanced:* If True the internal scenario generated by Khiops will force characters such as accentuated ones to be decoded with the UTF8->ANSI khiops transformation. Raises ------ - `ValueError` + ValueError - Unknown keyword argument - Files or executable not found - Errors in the execution of the Khiops tool - `TypeError` + TypeError - Invalid type of a keyword argument - When the search/replace pairs are not strings """ @@ -906,7 +913,7 @@ def _run( Raises ------ - `.KhiopsRuntimeError` + KhiopsRuntimeError If there were any errors in the Khiops execution. """ @@ -917,25 +924,25 @@ class KhiopsLocalRunner(KhiopsRunner): Requires either: - This library installed through Conda and run from a Conda environment, or - - the ``khiops-core`` Linux native library installed on the local machine, or + - the `khiops-core` Linux native library installed on the local machine, or - the Windows Khiops desktop application installed on the local machine - .. rubric:: Samples directory settings + **Samples directory settings** - Default values for the ``samples_dir`` attribute: + Default values for the `samples_dir` attribute: - - The value of the ``KHIOPS_SAMPLES_DIR`` environment variable (path to the Khiops + - The value of the `KHIOPS_SAMPLES_DIR` environment variable (path to the Khiops sample datasets directory). - Otherwise: - Windows: - - ``%PUBLIC%\khiops_data\samples%`` if ``%PUBLIC%`` is defined - - ``%USERPROFILE%\khiops_data\samples%`` otherwise + - `%PUBLIC%\khiops_data\samples%` if `%PUBLIC%` is defined + - `%USERPROFILE%\khiops_data\samples%` otherwise - Linux and macOS: - - ``$HOME/khiops_data/samples`` + - `$HOME/khiops_data/samples` """ @@ -1122,7 +1129,7 @@ def _initialize_khiops_version(self): def _detect_library_installation_incompatibilities(self, library_root_dir_path): """Detects known incompatible installations of this library - in the 3 installation modes see `_infer_khiops_installation_method` + in the 3 installation modes see [_infer_khiops_installation_method][] (pip, conda, conda-based) The error_list or warning_list collections @@ -1376,9 +1383,9 @@ def mpi_command_args(self): @property def khiops_path(self): - """str: Path to the ``MODL*`` Khiops binary + """str: Path to the `MODL*` Khiops binary - Set by the ``khiops_env`` script from the ``khiops-core`` package. + Set by the `khiops_env` script from the `khiops-core` package. """ return self._khiops_path @@ -1398,9 +1405,9 @@ def khiops_path(self, modl_path): @property def khiops_coclustering_path(self): - """str: Path to the ``MODL_Coclustering`` Khiops Coclustering binary + """str: Path to the `MODL_Coclustering` Khiops Coclustering binary - Set by the ``khiops_env`` script from the ``khiops-core`` package. + Set by the `khiops_env` script from the `khiops-core` package. """ return self._khiops_coclustering_path @@ -1461,15 +1468,20 @@ def raw_run(self, tool_name, command_line_args=None, use_mpi=True, trace=False): use_mpi : bool, optional Whether to execute the application with MPI trace : bool, default False - If ``True`` print the trace of the process. + If `True` print the trace of the process. Examples -------- + + ```pycon >>> raw_run("khiops", ["-b", "-i" , "scenario._kh"]) + ``` - is equivalent to execute in a shell:: + is equivalent to execute in a shell: - > khiops -b -i scenario._kh + ```shell + > khiops -b -i scenario._kh + ``` """ # Check command_line_args type if command_line_args and not isinstance(command_line_args, list): @@ -1546,7 +1558,7 @@ def _run( def set_runner(runner): - """Sets the current KhiopsRunner of the module""" + """Sets the current [KhiopsRunner][] of the module""" if not isinstance(runner, KhiopsRunner): raise TypeError(type_error_message("runner", runner, KhiopsRunner)) global _khiops_runner @@ -1558,7 +1570,7 @@ def get_runner(): Returns ------- - `.KhiopsRunner` + KhiopsRunner The current Khiops Python runner of the module. """ # Define and initialize a runner for a local Khiops installation diff --git a/khiops/core/internals/scenario.py b/khiops/core/internals/scenario.py index f836a0c4..de5e3ce2 100644 --- a/khiops/core/internals/scenario.py +++ b/khiops/core/internals/scenario.py @@ -233,7 +233,7 @@ def write(self, writer, scenario_args): Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter The output writer for the scenario file. scenario_args : dict Values of the scenario template arguments. diff --git a/khiops/core/internals/task.py b/khiops/core/internals/task.py index 2f5948ef..ec4a98e1 100644 --- a/khiops/core/internals/task.py +++ b/khiops/core/internals/task.py @@ -30,9 +30,9 @@ def encode_path_valued_arg(arg, arg_name, arg_type): The value of a path valued argument. arg_name : str The name of the argument. - arg_type : `.KhiopsTaskArgumentType` - The type of the argument. Must be either `.StringLikeType` or - `.DictType` `.StringLikeType` , `.StringLikeType` . + arg_type : KhiopsTaskArgumentType + The type of the argument. Must be either [StringLikeType][] or + [DictType][][ [StringLikeType][] , [StringLikeType][]] . See Also -------- @@ -61,28 +61,28 @@ class KhiopsTask: Name of the task. tool_name : str Name of the tool to execute the task (ex. "khiops", "khiops_coclustering"). - intro_version : `.KhiopsVersion` or str + intro_version : KhiopsVersion or str Khiops version where this task object was introduced. args_signature : list A list of 2-tuples for each mandatory parameter of the task, containing: - its name (str) - - its type (`.KhiopsTaskArgumentType`) + - its type ([KhiopsTaskArgumentType][]) kwargs_signature : list A list of 3-tuples for each optional parameter of the task, containing: - its name (str) - - its type (`.KhiopsTaskArgumentType`) + - its type ([KhiopsTaskArgumentType][]) - its default value path_valued_arg_names : list of str A list of the parameters that contain paths. They must be contained either in args_signature or kwargs_signature. The only accepted types are - `.StringLikeType` or containers thereof. + [StringLikeType][] or containers thereof. scenario_template : str A Khiops scenario template. The template mini-language is described above. Attributes ---------- - scenario : `.ConfigurableKhiopsScenario` - Scenario object built from ``scenario_template``. + scenario : ConfigurableKhiopsScenario + Scenario object built from `scenario_template`. """ def __init__( @@ -249,7 +249,7 @@ def write_execution_scenario(self, writer, args): Parameters ---------- - writer : `.KhiopsOutputWriter` + writer : KhiopsOutputWriter Writer object for the scenario file. args : dict Task arguments. Includes mandatory and optional arguments. @@ -327,7 +327,7 @@ class KhiopsTaskFamily: ---------- task_name : str The name of the task defining this family. - end_version : str or `.KhiopsVersion` + end_version : str or KhiopsVersion The Khiops version where the support for this task ended. """ @@ -339,10 +339,10 @@ def __init__(self, task_name, end_version=None): @property def end_version(self): - """`.KhiopsVersion` : Khiops version where support for this task ended + """[KhiopsVersion][] : Khiops version where support for this task ended - It is ``None`` if the task is still supported. May be set with either str or - `.KhiopsVersion` or ``None``. + It is `None` if the task is still supported. May be set with either str or + [KhiopsVersion][] or `None`. """ return self._end_version @@ -360,11 +360,11 @@ def end_version(self, version): @property def start_version(self): - """`.KhiopsVersion` : Khiops version where the support for this task started + """[KhiopsVersion][] : Khiops version where the support for this task started Raises ------ - `ValueError` + ValueError If there are no task registered in this family. """ if not self.tasks: @@ -379,15 +379,15 @@ def register_task(self, task, overwrite=False): Parameters ---------- - task : `.KhiopsTask` + task : KhiopsTask The task to be registered. Must have the same name of the family. - overwrite : bool, default ``False`` - If ``True`` it does not raise an error if a task with the same version is + overwrite : bool, default `False` + If `True` it does not raise an error if a task with the same version is already registered. Raises ------ - `ValueError` + ValueError If: - The task name is not the same as that of this family. @@ -416,12 +416,12 @@ def unregister_task(self, intro_version): Returns ------- - `KhiopsTask` + KhiopsTask The removed task object. Raises ------ - `ValueError` + ValueError If there is no task with the specified introduction version. """ if intro_version not in self.tasks: @@ -436,18 +436,18 @@ def get_task(self, target_version): Parameters ---------- - target_version : str or `.KhiopsVersion` + target_version : str or KhiopsVersion The target Khiops version. Returns ------- - `.KhiopsTask` - The latest task object compatible with the ``target_version``. + KhiopsTask + The latest task object compatible with the `target_version`. Raises ------ - `TypeError` - If target_version is not str or `.KhiopsVersion`. + TypeError + If target_version is not str or [KhiopsVersion][]. """ # Check the argument type @@ -485,7 +485,8 @@ def get_task(self, target_version): @property def all_intro_versions(self): - """list of `.KhiopsVersion` : A sorted list of the task introduction versions. + """list of [KhiopsVersion][] : A sorted list of the task introduction + versions. The list is sorted in reverse order. """ @@ -493,7 +494,7 @@ def all_intro_versions(self): @property def latest_intro_version(self): - """`.KhiopsVersion` : The latest introduction version of the task. + """[KhiopsVersion][] : The latest introduction version of the task. Raises ------ @@ -530,17 +531,17 @@ def get_task(self, task_name, target_version): ---------- task_name : str The name of the task to be retrieved. - target_version : str or `.KhiopsVersion` + target_version : str or KhiopsVersion The target Khiops version. Returns ------- - `.KhiopsTask` + KhiopsTask The latest task object for the specified task and version. Raises ------ - `ValueError` + ValueError If there are no tasks registered with the specified name. """ # Check the argument type @@ -558,13 +559,13 @@ def get_tasks(self, target_version): Parameters ---------- - target_version : str or `.KhiopsVersion` + target_version : str or KhiopsVersion The target Khiops version for the tasks to be retrieved. Returns ------- - list of `.KhiopsTask` - The list of compatible tasks with ``target_version``. + list of KhiopsTask + The list of compatible tasks with `target_version`. """ # Check the argument type if not isinstance(target_version, (str, KhiopsVersion)): @@ -597,14 +598,14 @@ def get_task_end_version(self, task_name): Returns ------- - `.KhiopsVersion` or ``None`` - Either the end version for this task or ``None`` if it is still supported. + KhiopsVersion or None + Either the end version for this task or [None][] if it is still supported. Raises ------ - `TypeError` - If ``task_name`` is not of type str. - `ValueError` + TypeError + If `task_name` is not of type str. + ValueError If there are no registered tasks with the specified name. """ if not isinstance(task_name, str): @@ -620,15 +621,15 @@ def set_task_end_version(self, task_name, end_version): ---------- task_name : str Name of the task. - end_version : str or `.KhiopsVersion` + end_version : str or KhiopsVersion Version where the support of the specified task ended. Raises ------ - `TypeError` - - If ``task_name`` is not of type str. - - If ``end_version`` is not of type str or `.KhiopsVersion`. - `ValueError` + TypeError + - If `task_name` is not of type str. + - If `end_version` is not of type str or [KhiopsVersion][]. + ValueError If there are no registered tasks with the specified name. """ # Check input types @@ -641,7 +642,7 @@ def set_task_end_version(self, task_name, end_version): @property def latest_intro_version(self): - """`.KhiopsVersion` : Latest introduction version overall tasks""" + """[KhiopsVersion][] : Latest introduction version overall tasks""" latest_intro_version = KhiopsVersion("1.0") for task_family in self.task_families: latest_intro_version = max( diff --git a/khiops/core/internals/types.py b/khiops/core/internals/types.py index 9f2df2a3..f5b8c668 100644 --- a/khiops/core/internals/types.py +++ b/khiops/core/internals/types.py @@ -110,7 +110,7 @@ def to_scenario_arg(cls, arg): class StringLikeType(KhiopsTaskArgumentType): """String like argument type - The string-like type is defined as the union of ``str`` and ``bytes``. + The string-like type is defined as the union of `str` and `bytes`. """ @classmethod @@ -129,7 +129,7 @@ def to_scenario_arg(cls, arg): class AbstractListType(KhiopsTaskArgumentType): """Base class for ListType containers - See the factory method `ListType`. + See the factory method [ListType][]. """ registry = {} @@ -178,18 +178,18 @@ def to_scenario_arg(cls, arg): def ListType(value_type): # pylint: disable=invalid-name """ListType factory method - Lists are themselves of type ``list`` and they may contain a variable number of + Lists are themselves of type `list` and they may contain a variable number of elements of a single type. Parameters ---------- - value_type : `KhiopsTaskArgumentType` + value_type : KhiopsTaskArgumentType The type for the values contained in the list. Returns ------- type - A class which inherits from `AbstractListType`. + A class which inherits from [AbstractListType][]. """ # Check the type of the list values @@ -222,7 +222,7 @@ def get_value_type(cls): class AbstractDictType(KhiopsTaskArgumentType): """Base class for DictType containers - See the factory method `DictType`. + See the factory method [DictType][]. """ registry = {} @@ -287,20 +287,20 @@ def to_scenario_arg(cls, arg): def DictType(key_type, value_type): # pylint: disable=invalid-name """DictType factory method - Dicts are themselves of type ``dict`` and they contain key-value relations with + Dicts are themselves of type `dict` and they contain key-value relations with fixed key and value types. Parameters ---------- - key_type : `KhiopsTaskArgumentType` + key_type : KhiopsTaskArgumentType Type of the dictionary's keys. - value_type : `KhiopsTaskArgumentType` + value_type : KhiopsTaskArgumentType Type of the dictionary's values. Returns ------- type - A class which inherits from `AbstractDictType`. + A class which inherits from [AbstractDictType][]. """ # Check the type of the dictionary's key and value if not issubclass(key_type, KhiopsTaskArgumentType): @@ -342,7 +342,7 @@ def get_value_type(cls): class AbstractTupleType(KhiopsTaskArgumentType): """Base class for TupleTypes - See the factory method `TupleType`. + See the factory method [TupleType][]. """ registry = {} @@ -395,19 +395,19 @@ def to_scenario_arg(cls, arg): def TupleType(*value_types): # pylint: disable=invalid-name """TupleType factory method - Tuples are themselves of type ``tuple`` and they may contain a fixed number of + Tuples are themselves of type `tuple` and they may contain a fixed number of elements each one with a fixed type. Parameters ---------- - value_types : list of `KhiopsTaskArgumentType` + value_types : list of KhiopsTaskArgumentType Type of the tuples value types. The resulting tuple type will admit only tuples - of the same size of ``value_types``. + of the same size of `value_types`. Returns ------- type - A class which inherits from `AbstractTupleType`. + A class which inherits from [AbstractTupleType][]. """ # Check the type of the list values for value_type in value_types: diff --git a/khiops/core/internals/version.py b/khiops/core/internals/version.py index 579b7b8a..d57c16d0 100644 --- a/khiops/core/internals/version.py +++ b/khiops/core/internals/version.py @@ -12,8 +12,9 @@ def _is_simple_number(string): """Tests if a string contains only characters [0-9] and no left zeroes - note:: - We do not use str.isdigit() because it returns ``True`` for digit-like UTF-8 + !!! note + + We do not use str.isdigit() because it returns `True` for digit-like UTF-8 characters (fractions and superscripts for example). """ if string: diff --git a/khiops/extras/docker.py b/khiops/extras/docker.py index da8bebaa..cd8d4df6 100644 --- a/khiops/extras/docker.py +++ b/khiops/extras/docker.py @@ -36,8 +36,8 @@ class KhiopsDockerRunner(KhiopsRunner): URL for the Docker Khiops server. shared_dir : str Location of the shared directory. May be an URL/URI. - insecure : bool, default ``False`` - If ``True`` the target server an HTTPS URL connection requires a certificate. + insecure : bool, default `False` + If `True` the target server an HTTPS URL connection requires a certificate. """ def __init__(self, url, shared_dir, insecure=False): diff --git a/khiops/samples/samples.ipynb b/khiops/samples/samples.ipynb index db124b64..ed1adb36 100644 --- a/khiops/samples/samples.ipynb +++ b/khiops/samples/samples.ipynb @@ -64,7 +64,7 @@ "metadata": {}, "source": [ "### `create_dictionary_domain()`\n\n", - "Creates a dictionary domain from scratch\n\n This dictionary domain contains a set of dictionaries,\n with all possible variable types.\n \n" + "Creates a dictionary domain from scratch\n\nThis dictionary domain contains a set of dictionaries,\nwith all possible variable types.\n" ] }, { @@ -145,7 +145,7 @@ "metadata": {}, "source": [ "### `detect_data_table_format()`\n\n", - "Detects the format of a data table with and without a dictionary file\n\n The user may provide a dictionary file or dictionary domain object specifying the\n table schema. The detection heuristic is more accurate with this information.\n \n" + "Detects the format of a data table with and without a dictionary file\n\nThe user may provide a dictionary file or dictionary domain object specifying the\ntable schema. The detection heuristic is more accurate with this information.\n" ] }, { @@ -201,7 +201,7 @@ "metadata": {}, "source": [ "### `check_database()`\n\n", - "Runs an integrity check of a database\n\n The results are stored in the specified log file with at most 50 error messages.\n \n" + "Runs an integrity check of a database\n\nThe results are stored in the specified log file with at most 50 error messages.\n" ] }, { @@ -408,7 +408,7 @@ "metadata": {}, "source": [ "### `train_predictor_error_handling()`\n\n", - "Shows how to handle errors when training a predictor\n\n Trains the predictor and handles the errors by printing a custom message. When the\n Khiops application fails the Khiops Python library will raise a\n KhiopsRuntimeError reporting the errors encountered by Khiops.\n\n If the latter information is not enough to diagnose the problem, it is possible to\n save the temporary log file by activating the \"trace\" flag in the call to\n `~.api.train_predictor`. The path of the log file will be printed to the standard\n output, as well as that of the dictionary and scenario files (note that the \"trace\"\n keyword argument is available in all functions of the `khiops.core.api`\n submodule).\n \n" + "Shows how to handle errors when training a predictor\n\nTrains the predictor and handles the errors by printing a custom message. When the\nKhiops application fails the Khiops Python library will raise a\nKhiopsRuntimeError reporting the errors encountered by Khiops.\n\nIf the latter information is not enough to diagnose the problem, it is\npossible to save the temporary log file by activating the \"trace\" flag in\nthe call to [train_predictor][khiops.core.api.train_predictor].\nThe path of the log file will be printed to the standard output, as well as\nthat of the dictionary and scenario files (note that the \"trace\" keyword\nargument is available in all functions of the [khiops.core.api][]\nsubmodule).\n" ] }, { @@ -463,7 +463,7 @@ "metadata": {}, "source": [ "### `train_predictor_mt()`\n\n", - "Trains a multi-table predictor in the simplest way possible\n\n It is a call to `~.api.train_predictor` with additional parameters to handle\n multi-table learning\n \n" + "Trains a multi-table predictor in the simplest way possible\n\nIt is a call to [train_predictor][khiops.core.api.train_predictor] with\nadditional parameters to handle multi-table learning.\n" ] }, { @@ -505,7 +505,7 @@ "metadata": {}, "source": [ "### `train_predictor_mt_with_specific_rules()`\n\n", - "Trains a multi-table predictor with specific construction rules\n\n It is the same as `.train_predictor_mt` but with the specification of the allowed\n variable construction rules. The list of available rules is found in the field\n ``kh.ALL_CONSTRUCTION_RULES``\n \n" + "Trains a multi-table predictor with specific construction rules\n\nIt is the same as [train_predictor_mt][] but with the specification of the allowed\nvariable construction rules. The list of available rules is found in the field\n`kh.ALL_CONSTRUCTION_RULES`.\n" ] }, { @@ -600,7 +600,7 @@ "metadata": {}, "source": [ "### `train_predictor_with_train_percentage()`\n\n", - "Trains a predictor with a 90%-10% train-test split\n\n Note: The default is a 70%-30% split\n \n" + "Trains a predictor with a 90%-10% train-test split\n\nNote: The default is a 70%-30% split\n" ] }, { @@ -726,7 +726,7 @@ "metadata": {}, "source": [ "### `train_predictor_with_multiple_parameters()`\n\n", - "Trains a predictor with various additional parameters\n\n Some of these parameters are specific to `~.api.train_predictor` and others generic\n to any Khiops execution.\n\n In this example, we specify the following parameters in the call:\n - A main target value\n - The path where to store the \"Khiops scenario\" script\n - The path where to store the log of the process\n - The flag to show the execution trace (generic to any `khiops.core.api`\n function)\n\n Additionally the Khiops runner is set such that the learning is executed with only\n 1000 MB of memory.\n \n" + "Trains a predictor with various additional parameters\n\nSome of these parameters are specific to [khiops.core.api.train_predictor][]\nand others generic to any Khiops execution.\n\nIn this example, we specify the following parameters in the call:\n - A main target value\n - The path where to store the \"Khiops scenario\" script\n - The path where to store the log of the process\n - The flag to show the execution trace (generic to any [khiops.core.api][]\n function)\n\nAdditionally the Khiops runner is set such that the learning is executed with only\n1000 MB of memory.\n" ] }, { @@ -957,7 +957,7 @@ "metadata": {}, "source": [ "### `interpret_predictor()`\n\n", - "Builds interpretation model for existing predictor\n\n It calls `~.api.train_predictor` and `~.api.interpret_predictor` only with\n their mandatory parameters.\n \n" + "Builds interpretation model for existing predictor\n\nIt calls [khiops.core.api.train_predictor][] and\n[khiops.core.api.interpret_predictor][] only with their mandatory parameters.\n" ] }, { @@ -996,7 +996,7 @@ "metadata": {}, "source": [ "### `reinforce_predictor()`\n\n", - "Builds reinforced predictor for existing predictor\n\n The reinforced predictor produces the following reinforcement variables for the\n specified target value to reinforce (i.e. whose probability of occurrence is\n tentatively increased):\n\n - initial score, containing the conditional probability of the target value before\n reinforcement\n - four variables are output in decreasing reinforcement value: name of the lever\n variable, reinforcement part, final score after reinforcement, and class change\n tag.\n\n It calls `~.api.train_predictor` and `~.api.reinforce_predictor` only with\n their mandatory parameters.\n \n" + "Builds reinforced predictor for existing predictor\n\nThe reinforced predictor produces the following reinforcement variables for the\nspecified target value to reinforce (i.e. whose probability of occurrence is\ntentatively increased):\n\n- initial score, containing the conditional probability of the target value before\n reinforcement\n- four variables are output in decreasing reinforcement value: name of the lever\n variable, reinforcement part, final score after reinforcement, and class change\n tag.\n\nIt calls [khiops.core.api.train_predictor][] and\n[khiops.core.api.reinforce_predictor][] only with their mandatory parameters.\n" ] }, { @@ -1040,7 +1040,7 @@ "metadata": {}, "source": [ "### `multiple_train_predictor()`\n\n", - "Trains a sequence of models with a decreasing number of variables\n\n This example illustrates the use of the khiops classes `.DictionaryDomain` (for\n reading dictionary files) and `.AnalysisResults` (for reading training/evaluation\n results from JSON)\n \n" + "Trains a sequence of models with a decreasing number of variables\n\nThis example illustrates the use of the khiops classes\n[DictionaryDomain][khiops.core.dictionary.DictionaryDomain] (for reading\ndictionary files) and\n[AnalysisResults][khiops.core.analysis_results.AnalysisResults] (for reading\ntraining/evaluation results from JSON).\n" ] }, { @@ -1131,7 +1131,7 @@ "metadata": {}, "source": [ "### `evaluate_predictor()`\n\n", - "Evaluates a predictor in the simplest way possible\n\n It calls `~.api.evaluate_predictor` with only its mandatory parameters.\n \n" + "Evaluates a predictor in the simplest way possible\n\nIt calls [khiops.core.api.evaluate_predictor][] with only its mandatory parameters.\n" ] }, { @@ -1177,7 +1177,7 @@ "metadata": {}, "source": [ "### `access_predictor_evaluation_report()`\n\n", - "Shows the performance metrics of a predictor\n\n See `evaluate_predictor` or `train_predictor_with_train_percentage` to see examples\n on how to evaluate a model.\n \n" + "Shows the performance metrics of a predictor\n\nSee [evaluate_predictor][] or [train_predictor_with_train_percentage][] to see\nexamples\non how to evaluate a model.\n" ] }, { @@ -1263,7 +1263,7 @@ "metadata": {}, "source": [ "### `train_recoder()`\n\n", - "Train a database recoder in the simplest way possible\n\n It is a call to `~.api.train_recoder` with only its mandatory parameters.\n \n" + "Train a database recoder in the simplest way possible\n\nIt is a call to [khiops.core.api.train_recoder][] with only its mandatory\nparameters.\n" ] }, { @@ -1292,7 +1292,7 @@ "metadata": {}, "source": [ "### `train_recoder_with_multiple_parameters()`\n\n", - "Trains a recoder that transforms variable values to their respective part labels\n\n It also creates 10 pair features.\n \n" + "Trains a recoder that transforms variable values to their respective part labels\n\nIt also creates 10 pair features.\n" ] }, { @@ -1332,7 +1332,7 @@ "metadata": {}, "source": [ "### `train_recoder_mt_flatten()`\n\n", - "Trains a recoder that flattens a multi-table database into a single table\n\n The constructed variables are all kept and no recoding is performed on their values\n \n" + "Trains a recoder that flattens a multi-table database into a single table\n\nThe constructed variables are all kept and no recoding is performed on their values\n" ] }, { @@ -1380,7 +1380,7 @@ "metadata": {}, "source": [ "### `deploy_model()`\n\n", - "Deploys a model in the simplest way possible\n\n It is a call to `~.api.deploy_model` with its mandatory parameters.\n\n In this example, a Selective Naive Bayes (SNB) model is deployed by applying its\n associated dictionary to the input database. The model predictions are written to\n the output database.\n \n" + "Deploys a model in the simplest way possible\n\nIt is a call to [khiops.core.api.deploy_model][] with its mandatory parameters.\n\nIn this example, a Selective Naive Bayes (SNB) model is deployed by applying its\nassociated dictionary to the input database. The model predictions are written to\nthe output database.\n" ] }, { @@ -1422,7 +1422,7 @@ "metadata": {}, "source": [ "### `deploy_model_text()`\n\n", - "Deploys a model learned on textual data\n It is a call to `~.api.deploy_model` with its mandatory parameters, plus\n text-specific parameters.\n\n In this example, a Selective Naive Bayes (SNB) model is deployed by applying its\n associated dictionary to the input database. The model predictions are written to\n the output database.\n \n" + "Deploys a model learned on textual data\nIt is a call to [khiops.core.api.deploy_model][] with its mandatory parameters, plus\ntext-specific parameters.\n\nIn this example, a Selective Naive Bayes (SNB) model is deployed by applying its\nassociated dictionary to the input database. The model predictions are written to\nthe output database.\n" ] }, { @@ -1473,7 +1473,7 @@ "metadata": {}, "source": [ "### `deploy_model_mt()`\n\n", - "Deploys a multi-table classifier in the simplest way possible\n\n It is a call to `~.api.deploy_model` with additional parameters to handle\n multi-table deployment.\n\n In this example, a Selective Naive Bayes (SNB) model is deployed by applying its\n associated dictionary to the input database. The model predictions are written to\n the output database.\n \n" + "Deploys a multi-table classifier in the simplest way possible\n\nIt is a call to [khiops.core.api.deploy_model][] with additional parameters\nto handle multi-table deployment.\n\nIn this example, a Selective Naive Bayes (SNB) model is deployed by applying its\nassociated dictionary to the input database. The model predictions are written to\nthe output database.\n" ] }, { @@ -1523,7 +1523,7 @@ "metadata": {}, "source": [ "### `deploy_model_mt_with_interpretation()`\n\n", - "Deploys a multi-table interpretor in the simplest way possible\n\n It is a call to `~.api.deploy_model` with additional parameters related to\n the variable importances.\n\n In this example, a Selective Naive Bayes (SNB) interpretation model is\n deployed by applying its associated dictionary to the input database.\n The model variable importances are written to the output data table.\n \n" + "Deploys a multi-table interpretor in the simplest way possible\n\nIt is a call to [khiops.core.api.deploy_model][] with additional parameters\nrelated to the variable importances.\n\nIn this example, a Selective Naive Bayes (SNB) interpretation model is\ndeployed by applying its associated dictionary to the input database.\nThe model variable importances are written to the output data table.\n" ] }, { @@ -1586,7 +1586,7 @@ "metadata": {}, "source": [ "### `deploy_reinforced_model_mt()`\n\n", - "Deploys a multi-table reinforced model in the simplest way possible\n\n It is a call to `~.api.deploy_model` with additional parameters related to\n the lever variables.\n\n In this example, a reinforced Selective Naive Bayes (SNB) model is\n deployed by applying its associated dictionary to the input database.\n The reinforced model predictions are written to the output data table.\n \n" + "Deploys a multi-table reinforced model in the simplest way possible\n\nIt is a call to [khiops.core.api.deploy_model][] with additional parameters\nrelated to the lever variables.\n\nIn this example, a reinforced Selective Naive Bayes (SNB) model is\ndeployed by applying its associated dictionary to the input database.\nThe reinforced model predictions are written to the output data table.\n" ] }, { @@ -1709,7 +1709,7 @@ "metadata": {}, "source": [ "### `deploy_model_expert()`\n\n", - "Deploys a model with a specification of additional variables to be included\n\n In this example, a Selective Naive Bayes (SNB) model is deployed by applying its\n associated dictionary to the input database. Specifically, the output file contains:\n\n - The model predictions\n - The probabilities of all modalities of the target variable.\n\n The \"expert\" part of this example is the use of the khiops dictionary interface\n and the `.DictionaryDomain` class\n \n" + "Deploys a model with a specification of additional variables to be included\n\nIn this example, a Selective Naive Bayes (SNB) model is deployed by applying its\nassociated dictionary to the input database. Specifically, the output file contains:\n\n- The model predictions\n- The probabilities of all modalities of the target variable.\n\nThe \"expert\" part of this example is the use of the khiops dictionary interface\nand the [DictionaryDomain][khiops.core.dictionary.DictionaryDomain] class\n" ] }, { @@ -1919,7 +1919,7 @@ "metadata": {}, "source": [ "### `sort_data_table()`\n\n", - "Sorts a database in the simplest way possible\n\n It is a call to `~.api.sort_data_table` with only its mandatory parameters. This\n sorts a data table by its default key variable (specified in the table's\n dictionary).\n \n" + "Sorts a database in the simplest way possible\n\nIt is a call to [khiops.core.api.sort_data_table][] with only its mandatory\nparameters. This sorts a data table by its default key variable (specified\nin the table's dictionary).\n" ] }, { @@ -1953,7 +1953,7 @@ "metadata": {}, "source": [ "### `sort_data_table_expert()`\n\n", - "Sorts a database by a field other than the default table key\n\n It is a call to `~.api.sort_data_table` with additional parameters to specify the\n sorting fields.\n \n" + "Sorts a database by a field other than the default table key\n\nIt is a call to [khiops.core.api.sort_data_table][] with additional parameters\nto specify the sorting fields.\n" ] }, { @@ -1990,7 +1990,7 @@ "metadata": {}, "source": [ "### `extract_keys_from_data_table()`\n\n", - "Extracts the keys from a database\n\n It is a call to `~.api.extract_keys_from_data_table` with only its mandatory\n parameters.\n\n Pre-requisite: the database must be sorted by its key.\n \n" + "Extracts the keys from a database\n\nIt is a call to [khiops.core.api.extract_keys_from_data_table][] with only\nits mandatory parameters.\n\nPre-requisite: the database must be sorted by its key.\n" ] }, { @@ -2027,7 +2027,7 @@ "metadata": {}, "source": [ "### `train_coclustering()`\n\n", - "Trains a coclustering model in the simplest way possible\n\n It is a call to `~.api.train_coclustering` with only its mandatory parameters.\n \n" + "Trains a coclustering model in the simplest way possible\n\nIt is a call to [khiops.core.api.train_coclustering][] with only its mandatory\nparameters.\n" ] }, { @@ -2067,7 +2067,7 @@ "metadata": {}, "source": [ "### `train_instance_variable_coclustering()`\n\n", - "Trains an instance-variable coclustering model in the simplest way possible\n\n It is a call to `~.api.train_instance_variable_coclustering` with only its mandatory\n parameters.\n \n" + "Trains an instance-variable coclustering model in the simplest way possible\n\nIt is a call to [khiops.core.api.train_instance_variable_coclustering][] with\nonly its mandatory parameters.\n" ] }, { @@ -2247,7 +2247,7 @@ "metadata": {}, "source": [ "### `deploy_coclustering_expert()`\n\n", - "Deploys a coclustering step-by-step\n\n The `.api.prepare_coclustering_deployment` method is called twice to prepare the\n deployment at two granularity levels. Then, the model is deployed and the respective\n deployment dictionary is built.\n\n This is one of the most complex workflows of the Khiops suite.\n \n" + "Deploys a coclustering step-by-step\n\nThe [khiops.core.api.prepare_coclustering_deployment][] method is called\ntwice to prepare the deployment at two granularity levels. Then, the model\nis deployed and the respective deployment dictionary is built.\n\nThis is one of the most complex workflows of the Khiops suite.\n" ] }, { @@ -2334,7 +2334,7 @@ "metadata": {}, "source": [ "### `scenario_prologue()`\n\n", - "Trains a simple model with a prologue written in the Khiops scenario language\n\n .. note::\n This is an **advanced** feature.\n \n" + "Trains a simple model with a prologue written in the Khiops scenario language\n\n!!! note\n\n This is an **advanced** feature.\n" ] }, { diff --git a/khiops/samples/samples.py b/khiops/samples/samples.py index be9c9fd1..6ac1601a 100644 --- a/khiops/samples/samples.py +++ b/khiops/samples/samples.py @@ -347,11 +347,12 @@ def train_predictor_error_handling(): Khiops application fails the Khiops Python library will raise a KhiopsRuntimeError reporting the errors encountered by Khiops. - If the latter information is not enough to diagnose the problem, it is possible to - save the temporary log file by activating the "trace" flag in the call to - `~.api.train_predictor`. The path of the log file will be printed to the standard - output, as well as that of the dictionary and scenario files (note that the "trace" - keyword argument is available in all functions of the `khiops.core.api` + If the latter information is not enough to diagnose the problem, it is + possible to save the temporary log file by activating the "trace" flag in + the call to [train_predictor][khiops.core.api.train_predictor]. + The path of the log file will be printed to the standard output, as well as + that of the dictionary and scenario files (note that the "trace" keyword + argument is available in all functions of the [khiops.core.api][] submodule). """ # Imports @@ -398,8 +399,8 @@ def train_predictor_error_handling(): def train_predictor_mt(): """Trains a multi-table predictor in the simplest way possible - It is a call to `~.api.train_predictor` with additional parameters to handle - multi-table learning + It is a call to [train_predictor][khiops.core.api.train_predictor] with + additional parameters to handle multi-table learning. """ # Imports import os @@ -432,9 +433,9 @@ def train_predictor_mt(): def train_predictor_mt_with_specific_rules(): """Trains a multi-table predictor with specific construction rules - It is the same as `.train_predictor_mt` but with the specification of the allowed + It is the same as [train_predictor_mt][] but with the specification of the allowed variable construction rules. The list of available rules is found in the field - ``kh.ALL_CONSTRUCTION_RULES`` + `kh.ALL_CONSTRUCTION_RULES`. """ # Imports import os @@ -601,14 +602,14 @@ def train_predictor_with_pairs(): def train_predictor_with_multiple_parameters(): """Trains a predictor with various additional parameters - Some of these parameters are specific to `~.api.train_predictor` and others generic - to any Khiops execution. + Some of these parameters are specific to [khiops.core.api.train_predictor][] + and others generic to any Khiops execution. In this example, we specify the following parameters in the call: - A main target value - The path where to store the "Khiops scenario" script - The path where to store the log of the process - - The flag to show the execution trace (generic to any `khiops.core.api` + - The flag to show the execution trace (generic to any [khiops.core.api][] function) Additionally the Khiops runner is set such that the learning is executed with only @@ -812,9 +813,11 @@ def train_predictor_with_cross_validation(): def multiple_train_predictor(): """Trains a sequence of models with a decreasing number of variables - This example illustrates the use of the khiops classes `.DictionaryDomain` (for - reading dictionary files) and `.AnalysisResults` (for reading training/evaluation - results from JSON) + This example illustrates the use of the khiops classes + [DictionaryDomain][khiops.core.dictionary.DictionaryDomain] (for reading + dictionary files) and + [AnalysisResults][khiops.core.analysis_results.AnalysisResults] (for reading + training/evaluation results from JSON). """ # Imports import os @@ -894,8 +897,8 @@ def display_test_results(json_result_file_path): def interpret_predictor(): """Builds interpretation model for existing predictor - It calls `~.api.train_predictor` and `~.api.interpret_predictor` only with - their mandatory parameters. + It calls [khiops.core.api.train_predictor][] and + [khiops.core.api.interpret_predictor][] only with their mandatory parameters. """ # Imports import os @@ -935,8 +938,8 @@ def reinforce_predictor(): variable, reinforcement part, final score after reinforcement, and class change tag. - It calls `~.api.train_predictor` and `~.api.reinforce_predictor` only with - their mandatory parameters. + It calls [khiops.core.api.train_predictor][] and + [khiops.core.api.reinforce_predictor][] only with their mandatory parameters. """ # Imports import os @@ -973,7 +976,7 @@ def reinforce_predictor(): def evaluate_predictor(): """Evaluates a predictor in the simplest way possible - It calls `~.api.evaluate_predictor` with only its mandatory parameters. + It calls [khiops.core.api.evaluate_predictor][] with only its mandatory parameters. """ # Imports import os @@ -1010,7 +1013,8 @@ def evaluate_predictor(): def access_predictor_evaluation_report(): """Shows the performance metrics of a predictor - See `evaluate_predictor` or `train_predictor_with_train_percentage` to see examples + See [evaluate_predictor][] or [train_predictor_with_train_percentage][] to see + examples on how to evaluate a model. """ # Imports @@ -1088,7 +1092,8 @@ def access_predictor_evaluation_report(): def train_recoder(): """Train a database recoder in the simplest way possible - It is a call to `~.api.train_recoder` with only its mandatory parameters. + It is a call to [khiops.core.api.train_recoder][] with only its mandatory + parameters. """ # Imports import os @@ -1180,7 +1185,7 @@ def train_recoder_mt_flatten(): def deploy_model(): """Deploys a model in the simplest way possible - It is a call to `~.api.deploy_model` with its mandatory parameters. + It is a call to [khiops.core.api.deploy_model][] with its mandatory parameters. In this example, a Selective Naive Bayes (SNB) model is deployed by applying its associated dictionary to the input database. The model predictions are written to @@ -1216,7 +1221,7 @@ def deploy_model(): def deploy_model_text(): """Deploys a model learned on textual data - It is a call to `~.api.deploy_model` with its mandatory parameters, plus + It is a call to [khiops.core.api.deploy_model][] with its mandatory parameters, plus text-specific parameters. In this example, a Selective Naive Bayes (SNB) model is deployed by applying its @@ -1263,8 +1268,8 @@ def deploy_model_text(): def deploy_model_mt(): """Deploys a multi-table classifier in the simplest way possible - It is a call to `~.api.deploy_model` with additional parameters to handle - multi-table deployment. + It is a call to [khiops.core.api.deploy_model][] with additional parameters + to handle multi-table deployment. In this example, a Selective Naive Bayes (SNB) model is deployed by applying its associated dictionary to the input database. The model predictions are written to @@ -1309,8 +1314,8 @@ def deploy_model_mt(): def deploy_model_mt_with_interpretation(): """Deploys a multi-table interpretor in the simplest way possible - It is a call to `~.api.deploy_model` with additional parameters related to - the variable importances. + It is a call to [khiops.core.api.deploy_model][] with additional parameters + related to the variable importances. In this example, a Selective Naive Bayes (SNB) interpretation model is deployed by applying its associated dictionary to the input database. @@ -1368,8 +1373,8 @@ def deploy_model_mt_with_interpretation(): def deploy_reinforced_model_mt(): """Deploys a multi-table reinforced model in the simplest way possible - It is a call to `~.api.deploy_model` with additional parameters related to - the lever variables. + It is a call to [khiops.core.api.deploy_model][] with additional parameters + related to the lever variables. In this example, a reinforced Selective Naive Bayes (SNB) model is deployed by applying its associated dictionary to the input database. @@ -1482,7 +1487,7 @@ def deploy_model_expert(): - The probabilities of all modalities of the target variable. The "expert" part of this example is the use of the khiops dictionary interface - and the `.DictionaryDomain` class + and the [DictionaryDomain][khiops.core.dictionary.DictionaryDomain] class """ # Imports import os @@ -1659,9 +1664,9 @@ def deploy_regressor_for_metrics(): def sort_data_table(): """Sorts a database in the simplest way possible - It is a call to `~.api.sort_data_table` with only its mandatory parameters. This - sorts a data table by its default key variable (specified in the table's - dictionary). + It is a call to [khiops.core.api.sort_data_table][] with only its mandatory + parameters. This sorts a data table by its default key variable (specified + in the table's dictionary). """ # Imports import os @@ -1686,8 +1691,8 @@ def sort_data_table(): def sort_data_table_expert(): """Sorts a database by a field other than the default table key - It is a call to `~.api.sort_data_table` with additional parameters to specify the - sorting fields. + It is a call to [khiops.core.api.sort_data_table][] with additional parameters + to specify the sorting fields. """ # Imports import os @@ -1715,8 +1720,8 @@ def sort_data_table_expert(): def extract_keys_from_data_table(): """Extracts the keys from a database - It is a call to `~.api.extract_keys_from_data_table` with only its mandatory - parameters. + It is a call to [khiops.core.api.extract_keys_from_data_table][] with only + its mandatory parameters. Pre-requisite: the database must be sorted by its key. """ @@ -1746,7 +1751,8 @@ def extract_keys_from_data_table(): def train_coclustering(): """Trains a coclustering model in the simplest way possible - It is a call to `~.api.train_coclustering` with only its mandatory parameters. + It is a call to [khiops.core.api.train_coclustering][] with only its mandatory + parameters. """ # Imports import os @@ -1777,8 +1783,8 @@ def train_coclustering(): def train_instance_variable_coclustering(): """Trains an instance-variable coclustering model in the simplest way possible - It is a call to `~.api.train_instance_variable_coclustering` with only its mandatory - parameters. + It is a call to [khiops.core.api.train_instance_variable_coclustering][] with + only its mandatory parameters. """ # Imports import os @@ -1913,9 +1919,9 @@ def deploy_coclustering(): def deploy_coclustering_expert(): """Deploys a coclustering step-by-step - The `.api.prepare_coclustering_deployment` method is called twice to prepare the - deployment at two granularity levels. Then, the model is deployed and the respective - deployment dictionary is built. + The [khiops.core.api.prepare_coclustering_deployment][] method is called + twice to prepare the deployment at two granularity levels. Then, the model + is deployed and the respective deployment dictionary is built. This is one of the most complex workflows of the Khiops suite. """ @@ -1995,7 +2001,8 @@ def deploy_coclustering_expert(): def scenario_prologue(): """Trains a simple model with a prologue written in the Khiops scenario language - .. note:: + !!! note + This is an **advanced** feature. """ # Imports diff --git a/khiops/samples/samples_sklearn.ipynb b/khiops/samples/samples_sklearn.ipynb index 484aa109..2ecf2a42 100644 --- a/khiops/samples/samples_sklearn.ipynb +++ b/khiops/samples/samples_sklearn.ipynb @@ -14,7 +14,7 @@ "metadata": {}, "source": [ "### `khiops_classifier()`\n\n", - "Trains a `.KhiopsClassifier` on a monotable dataframe\n" + "Trains a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier]\non a monotable dataframe\n" ] }, { @@ -83,7 +83,7 @@ "metadata": {}, "source": [ "### `khiops_classifier_multiclass()`\n\n", - "Trains a multiclass `.KhiopsClassifier` on a monotable dataframe\n" + "Trains a multiclass\n[KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] on a monotable\ndataframe\n" ] }, { @@ -146,7 +146,7 @@ "metadata": {}, "source": [ "### `khiops_classifier_text()`\n\n", - "Train a `.KhiopsClassifier` on a monotable dataframe with textual data\n" + "Train a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] on\na monotable dataframe with textual data\n" ] }, { @@ -217,7 +217,7 @@ "metadata": {}, "source": [ "### `khiops_classifier_multitable_star()`\n\n", - "Trains a `.KhiopsClassifier` on a star multi-table dataset\n" + "Trains a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier]\non a star multi-table dataset\n" ] }, { @@ -284,7 +284,7 @@ "metadata": {}, "source": [ "### `khiops_classifier_multitable_snowflake()`\n\n", - "Trains a `.KhiopsClassifier` on a snowflake multi-table dataset\n" + "Trains a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier]\non a snowflake multi-table dataset\n" ] }, { @@ -354,7 +354,7 @@ "metadata": {}, "source": [ "### `khiops_classifier_sparse()`\n\n", - "Trains a `.KhiopsClassifier` on a monotable sparse matrix\n" + "Trains a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier]\non a monotable sparse matrix\n" ] }, { @@ -420,7 +420,7 @@ "metadata": {}, "source": [ "### `khiops_classifier_pickle()`\n\n", - "Shows the serialization and deserialization of a `.KhiopsClassifier`\n" + "Shows the serialization and deserialization of a\n[KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier]\n" ] }, { @@ -475,7 +475,7 @@ "metadata": {}, "source": [ "### `khiops_classifier_with_hyperparameters()`\n\n", - "Trains a `.KhiopsClassifier` on a star multi-table dataset\n (advanced version with more hyperparameters)\n \n" + "Trains a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier]\non a star multi-table dataset (advanced version with more hyperparameters)\n" ] }, { @@ -574,7 +574,7 @@ "metadata": {}, "source": [ "### `khiops_regressor()`\n\n", - "Trains a `.KhiopsRegressor` on a monotable dataframe\n" + "Trains a [KhiopsRegressor][khiops.sklearn.estimators.KhiopsRegressor] on\na monotable dataframe\n" ] }, { @@ -628,7 +628,7 @@ "metadata": {}, "source": [ "### `khiops_encoder()`\n\n", - "Trains a `.KhiopsEncoder` on a monotable dataframe\n\n The Khiops encoder is a supervised feature encoder. It discretizes numerical\n features and groups categorical features in a way that the resulting interval/groups\n have the highest class-purity.\n\n .. note::\n For simplicity we train from the whole dataset. To assess the performance one\n usually splits the dataset into train and test subsets.\n \n" + "Trains a [KhiopsEncoder][khiops.sklearn.estimators.KhiopsEncoder] on a\nmonotable dataframe\n\nThe Khiops encoder is a supervised feature encoder. It discretizes numerical\nfeatures and groups categorical features in a way that the resulting interval/groups\nhave the highest class-purity.\n\n!!! note\n\n For simplicity we train from the whole dataset. To assess the performance one\n usually splits the dataset into train and test subsets.\n" ] }, { @@ -676,7 +676,7 @@ "metadata": {}, "source": [ "### `khiops_encoder_multitable_star()`\n\n", - "Trains a `.KhiopsEncoder` on a star multi-table dataset\n" + "Trains a [KhiopsEncoder][khiops.sklearn.estimators.KhiopsEncoder] on a\nstar multi-table dataset\n" ] }, { @@ -726,7 +726,7 @@ "metadata": {}, "source": [ "### `khiops_encoder_multitable_snowflake()`\n\n", - "Trains a `.KhiopsEncoder` on a snowflake multi-table dataset\n" + "Trains a [KhiopsEncoder][khiops.sklearn.estimators.KhiopsEncoder] on a\nsnowflake multi-table dataset\n" ] }, { @@ -779,7 +779,7 @@ "metadata": {}, "source": [ "### `khiops_encoder_pipeline_with_hgbc()`\n\n", - "Uses a `.KhiopsEncoder` with a `~sklearn.ensemble.HistGradientBoostingClassifier`\n" + "Uses a [KhiopsEncoder][khiops.sklearn.estimators.KhiopsEncoder] with a\n[HistGradientBoostingClassifier][sklearn.ensemble.HistGradientBoostingClassifier]\n" ] }, { @@ -848,7 +848,7 @@ "metadata": {}, "source": [ "### `khiops_encoder_with_hyperparameters()`\n\n", - "Trains a `.KhiopsEncoder` on a star multi-table dataset\n (advanced version with more hyperparameters)\n \n" + "Trains a [KhiopsEncoder][khiops.sklearn.estimators.KhiopsEncoder] on a\nstar multi-table dataset (advanced version with more hyperparameters)\n" ] }, { @@ -908,7 +908,7 @@ "metadata": {}, "source": [ "### `khiops_coclustering()`\n\n", - "Trains a `.KhiopsCoclustering` on a dataframe\n" + "Trains a [KhiopsCoclustering][khiops.sklearn.estimators.KhiopsCoclustering]\non a dataframe\n" ] }, { @@ -955,7 +955,7 @@ "metadata": {}, "source": [ "### `khiops_coclustering_simplify()`\n\n", - "Simplifies a `.KhiopsCoclustering` already trained on a dataframe\n" + "Simplifies a [KhiopsCoclustering][khiops.sklearn.estimators.KhiopsCoclustering]\nalready trained on a dataframe\n" ] }, { diff --git a/khiops/samples/samples_sklearn.py b/khiops/samples/samples_sklearn.py index a808da3a..a4635997 100644 --- a/khiops/samples/samples_sklearn.py +++ b/khiops/samples/samples_sklearn.py @@ -25,7 +25,9 @@ def khiops_classifier(): - """Trains a `.KhiopsClassifier` on a monotable dataframe""" + """Trains a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] + on a monotable dataframe + """ # Imports import os import pandas as pd @@ -82,7 +84,9 @@ def khiops_classifier(): def khiops_classifier_multiclass(): - """Trains a multiclass `.KhiopsClassifier` on a monotable dataframe""" + """Trains a multiclass + [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] on a monotable + dataframe""" # Imports import os import pandas as pd @@ -135,7 +139,9 @@ def khiops_classifier_multiclass(): def khiops_classifier_text(): - """Train a `.KhiopsClassifier` on a monotable dataframe with textual data""" + """Train a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] on + a monotable dataframe with textual data + """ # Imports import os import pandas as pd @@ -196,7 +202,9 @@ def khiops_classifier_text(): def khiops_classifier_multitable_star(): - """Trains a `.KhiopsClassifier` on a star multi-table dataset""" + """Trains a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] + on a star multi-table dataset + """ # Imports import os import pandas as pd @@ -253,7 +261,9 @@ def khiops_classifier_multitable_star(): def khiops_classifier_multitable_snowflake(): - """Trains a `.KhiopsClassifier` on a snowflake multi-table dataset""" + """Trains a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] + on a snowflake multi-table dataset + """ # Imports import os import pandas as pd @@ -315,7 +325,9 @@ def khiops_classifier_multitable_snowflake(): def khiops_classifier_sparse(): - """Trains a `.KhiopsClassifier` on a monotable sparse matrix""" + """Trains a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] + on a monotable sparse matrix + """ # Imports from khiops.sklearn import KhiopsClassifier from sklearn import metrics @@ -369,7 +381,9 @@ def khiops_classifier_sparse(): def khiops_classifier_pickle(): - """Shows the serialization and deserialization of a `.KhiopsClassifier`""" + """Shows the serialization and deserialization of a + [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] + """ # Imports import os import pandas as pd @@ -412,8 +426,8 @@ def khiops_classifier_pickle(): def khiops_classifier_with_hyperparameters(): - """Trains a `.KhiopsClassifier` on a star multi-table dataset - (advanced version with more hyperparameters) + """Trains a [KhiopsClassifier][khiops.sklearn.estimators.KhiopsClassifier] + on a star multi-table dataset (advanced version with more hyperparameters) """ # Imports import os @@ -501,7 +515,9 @@ def khiops_classifier_with_hyperparameters(): def khiops_regressor(): - """Trains a `.KhiopsRegressor` on a monotable dataframe""" + """Trains a [KhiopsRegressor][khiops.sklearn.estimators.KhiopsRegressor] on + a monotable dataframe + """ # Imports import os import pandas as pd @@ -545,13 +561,15 @@ def khiops_regressor(): def khiops_encoder(): - """Trains a `.KhiopsEncoder` on a monotable dataframe + """Trains a [KhiopsEncoder][khiops.sklearn.estimators.KhiopsEncoder] on a + monotable dataframe The Khiops encoder is a supervised feature encoder. It discretizes numerical features and groups categorical features in a way that the resulting interval/groups have the highest class-purity. - .. note:: + !!! note + For simplicity we train from the whole dataset. To assess the performance one usually splits the dataset into train and test subsets. """ @@ -590,7 +608,9 @@ def khiops_encoder(): def khiops_encoder_multitable_star(): - """Trains a `.KhiopsEncoder` on a star multi-table dataset""" + """Trains a [KhiopsEncoder][khiops.sklearn.estimators.KhiopsEncoder] on a + star multi-table dataset + """ # Imports import os import pandas as pd @@ -630,7 +650,9 @@ def khiops_encoder_multitable_star(): def khiops_encoder_multitable_snowflake(): - """Trains a `.KhiopsEncoder` on a snowflake multi-table dataset""" + """Trains a [KhiopsEncoder][khiops.sklearn.estimators.KhiopsEncoder] on a + snowflake multi-table dataset + """ # Imports import os import pandas as pd @@ -677,7 +699,9 @@ def khiops_encoder_multitable_snowflake(): # Disable line too long just to have a title linking the sklearn documentation # pylint: disable=line-too-long def khiops_encoder_pipeline_with_hgbc(): - """Uses a `.KhiopsEncoder` with a `~sklearn.ensemble.HistGradientBoostingClassifier`""" + """Uses a [KhiopsEncoder][khiops.sklearn.estimators.KhiopsEncoder] with a + [HistGradientBoostingClassifier][sklearn.ensemble.HistGradientBoostingClassifier] + """ # Imports import os import pandas as pd @@ -736,8 +760,8 @@ def khiops_encoder_pipeline_with_hgbc(): def khiops_encoder_with_hyperparameters(): - """Trains a `.KhiopsEncoder` on a star multi-table dataset - (advanced version with more hyperparameters) + """Trains a [KhiopsEncoder][khiops.sklearn.estimators.KhiopsEncoder] on a + star multi-table dataset (advanced version with more hyperparameters) """ # Imports import os @@ -793,7 +817,9 @@ def khiops_encoder_with_hyperparameters(): def khiops_coclustering(): - """Trains a `.KhiopsCoclustering` on a dataframe""" + """Trains a [KhiopsCoclustering][khiops.sklearn.estimators.KhiopsCoclustering] + on a dataframe + """ # Imports import os import pandas as pd @@ -828,7 +854,9 @@ def khiops_coclustering(): def khiops_coclustering_simplify(): - """Simplifies a `.KhiopsCoclustering` already trained on a dataframe""" + """Simplifies a [KhiopsCoclustering][khiops.sklearn.estimators.KhiopsCoclustering] + already trained on a dataframe + """ # Imports import os import pandas as pd diff --git a/khiops/sklearn/dataset.py b/khiops/sklearn/dataset.py index f899de20..1d8d36da 100644 --- a/khiops/sklearn/dataset.py +++ b/khiops/sklearn/dataset.py @@ -46,7 +46,8 @@ def check_dataset_spec(ds_spec): Parameters ---------- ds_spec : dict - A specification of a multi-table dataset (see :doc:`/multi_table_primer`). + A specification of a multi-table dataset + (see [Multi-Table Learning Primer](../multi_table_primer.md)). Raises ------ @@ -247,20 +248,21 @@ def get_khiops_type(numpy_type, categorical_str_max_size=None): Parameters ---------- - numpy_type : `numpy.dtype` + numpy_type : numpy.dtype Numpy type of the column - categorical_str_max_size : `int`, optional - Maximum length of the entries of the column whose type is ``numpy_type``. + categorical_str_max_size : int, optional + Maximum length of the entries of the column whose type is `numpy_type`. Returns ------- str Khiops type name. Either "Categorical", "Text", "Numerical" or "Timestamp". - .. note:: - The "Text" Khiops type is inferred if the Numpy type is "string" - and the maximum length of the entries of that type is greater than 100. + Note + ---- + The "Text" Khiops type is inferred if the NumPy type is "string" + and the maximum length of the entries of that type is greater than 100. """ # Check categorical_str_max_size type if categorical_str_max_size is not None and not isinstance( @@ -314,7 +316,7 @@ def read_internal_data_table(file_path_or_stream, column_dtypes=None): - Use tab as separator - Read the column names from the first line - Use '"' as quote character - - Use `csv.QUOTE_MINIMAL` + - Use [csv.QUOTE_MINIMAL][] - double quoting enabled (quotes within quotes can be escaped with '""') - UTF-8 encoding - User-specified dtypes (optional) @@ -325,13 +327,13 @@ def read_internal_data_table(file_path_or_stream, column_dtypes=None): The path of the internal data table file to be read or a readable file object. column_dtypes : dict, optional - Dictionary linking column names with dtypes. See ``dtype`` parameter of the - `pandas.read_csv` function. If not set, then the column types are detected + Dictionary linking column names with dtypes. See `dtype` parameter of the + [pandas.read_csv][] function. If not set, then the column types are detected automatically by pandas. Returns ------- - `pandas.DataFrame` + pandas.DataFrame The dataframe representation of the data table. """ # Change the 'U' types (Unicode strings) to 'O' because pandas does not support them @@ -365,7 +367,7 @@ def write_internal_data_table(dataframe, file_path_or_stream): - Use tab as separator - Write the column names on the first line - Use '"' as quote character - - Use `csv.QUOTE_MINIMAL` + - Use [csv.QUOTE_MINIMAL][] - double quoting enabled (quotes within quotes can be escaped with '""') - UTF-8 encoding - The index is not written @@ -376,7 +378,7 @@ def write_internal_data_table(dataframe, file_path_or_stream): Parameters ---------- - dataframe : `pandas.DataFrame` + dataframe : pandas.DataFrame The dataframe to write. file_path_or_stream : str or file object The path of the internal data table file to be written or a writable file @@ -401,7 +403,7 @@ def write_internal_data_table(dataframe, file_path_or_stream): def _column_or_1d_with_dtype(y, dtype=None): """Checks the data is of the provided `dtype`. If a problem is detected a warning is printed or an error raised, - otherwise the pandas object is transformed into a numpy.array + otherwise the pandas object is transformed into a [numpy.ndarray][] """ return column_or_1d(y, warn=True, dtype=dtype) @@ -411,15 +413,17 @@ class Dataset: Parameters ---------- - X : `pandas.DataFrame` or dict + X : pandas.DataFrame or dict Either: + - A single dataframe - - A ``dict`` dataset specification - y : `pandas.Series`, `pandas.DataFrame` or `numpy.ndarray`, optional + + - A `dict` dataset specification + y : pandas.Series, pandas.DataFrame or numpy.ndarray, optional The target column. categorical_target : bool, default True - ``True`` if the vector ``y`` should be considered as a categorical variable. If - ``False`` it is considered as numeric. Ignored if ``y`` is ``None``. + `True` if the vector `y` should be considered as a categorical variable. If + `False` it is considered as numeric. Ignored if `y` is `None`. """ def __init__(self, X, y=None, categorical_target=True): @@ -531,7 +535,7 @@ def _init_tables_from_mapping(self, X): "11.0.1", replacement=( "the new data-path-based format, as documented in " - ":doc:`multi_table_primer`." + "the 'Multi-Table Learning Primer' documentation." ), quote=False, ) @@ -725,15 +729,15 @@ def table_type(self): Possible values: - - `PandasTable` - - `NumpyTable` - - `SparseTable` + - [PandasTable][] + - [NumpyTable][] + - [SparseTable][] """ return type(self.main_table) @property def is_multitable(self): - """bool : ``True`` if the dataset is multitable""" + """bool : `True` if the dataset is multitable""" return ( self.additional_data_tables is not None and len(self.additional_data_tables) > 0 @@ -757,8 +761,8 @@ def to_spec(self): def copy(self): """Creates a copy of the dataset - Referenced pandas.DataFrame's, numpy.nparray's and scipy.sparse.spmatrix's in - tables are copied as references. + Referenced [pandas.DataFrame][], [numpy.ndarray][] and + [scipy.sparse.spmatrix][] objects in tables are copied as references. """ return Dataset(self.to_spec()) @@ -772,12 +776,12 @@ def get_table(self, table_name): Returns ------- - `DatasetTable` + DatasetTable The table object for the specified name. Raises ------ - `KeyError` + KeyError If there is no table with the specified name. """ return self._tables_by_name[table_name] @@ -787,8 +791,9 @@ def create_khiops_dictionary_domain(self): Returns ------- - `.DictionaryDomain` - The dictionary domain object representing this dataset + DictionaryDomain + The [DictionaryDomain][khiops.core.dictionary.DictionaryDomain] + object representing this dataset """ assert self.main_table is not None, "'main_table' must be initialized" @@ -946,8 +951,9 @@ def create_khiops_dictionary(self): Returns ------- - `.Dictionary`: - The Khiops Dictionary object describing this table's schema + Dictionary: + The Khiops [Dictionary][khiops.core.dictionary.Dictionary] object + describing this table's schema """ assert self.column_ids is not None, "Dataset column list is None" @@ -982,7 +988,7 @@ class PandasTable(DatasetTable): ---------- name : str Name for the table. - dataframe : `pandas.DataFrame` + dataframe : pandas.DataFrame The data frame to be encapsulated. It must be non-empty. key : list of str, optional The names of the columns composing the key. @@ -1100,9 +1106,9 @@ class NumpyTable(DatasetTable): ---------- name : str Name for the table. - array : `numpy.ndarray` of shape (n_samples, n_features_in) or Sequence + array : numpy.ndarray of shape (n_samples, n_features_in) or Sequence The data frame to be encapsulated. - key : :external:term`array-like` of int, optional + key : array-like of int, optional The names of the columns composing the key. """ @@ -1183,7 +1189,7 @@ class SparseTable(DatasetTable): ---------- name : str Name for the table. - matrix : `scipy.sparse.spmatrix` + matrix : scipy.sparse.spmatrix The sparse matrix to be encapsulated. key : list of str, optional The names of the columns composing the key. @@ -1229,8 +1235,9 @@ def create_khiops_dictionary(self): Returns ------- - `.Dictionary`: - The Khiops Dictionary object describing this table's schema + Dictionary: + The Khiops [Dictionary][khiops.dictionary.Dictionary] object + describing this table's schema """ diff --git a/khiops/sklearn/estimators.py b/khiops/sklearn/estimators.py index 71990518..d2b28d94 100644 --- a/khiops/sklearn/estimators.py +++ b/khiops/sklearn/estimators.py @@ -8,21 +8,23 @@ Class Overview -------------- -The diagram below describes the relationships in this module:: - - KhiopsEstimator(ABC, BaseEstimator) - | - +- KhiopsCoclustering(ClusterMixin) - | - +- KhiopsSupervisedEstimator - | - +- KhiopsPredictor - | | - | +- KhiopsClassifier(ClassifierMixin) - | | - | +- KhiopsRegressor(RegressorMixin) - | - +- KhiopsEncoder(TransformerMixin) +The diagram below describes the relationships in this module: + +```text +KhiopsEstimator(ABC, BaseEstimator) + | + +- KhiopsCoclustering(ClusterMixin) + | + +- KhiopsSupervisedEstimator + | + +- KhiopsPredictor + | | + | +- KhiopsClassifier(ClassifierMixin) + | | + | +- KhiopsRegressor(RegressorMixin) + | + +- KhiopsEncoder(TransformerMixin) +``` """ import io import warnings @@ -223,7 +225,8 @@ def _cleanup_dir(target_dir): class KhiopsEstimator(ABC, BaseEstimator): """Base class for Khiops Scikit-learn estimators - .. note:: + !!! note + The input features collection X needs to have single-line records so that Khiops can handle them. Hence, multi-line records are preprocessed: @@ -232,13 +235,13 @@ class KhiopsEstimator(ABC, BaseEstimator): Parameters ---------- - verbose : bool, default ``False`` - If ``True`` it prints debug information and it does not erase temporary files + verbose : bool, default `False` + If `True` it prints debug information and it does not erase temporary files when fitting, predicting or transforming. output_dir : str, optional Path of the output directory for the resulting artifacts of Khiops learning tasks. See concrete estimator classes for more information about this parameter. - auto_sort : bool, default ``True`` + auto_sort : bool, default `True` *Advanced.*: See concrete estimator classes for information about this parameter. """ @@ -315,7 +318,7 @@ def export_report_file(self, report_file_path): Raises ------ - `ValueError` + ValueError When the instance is not fitted. """ check_is_fitted(self) @@ -329,7 +332,7 @@ def export_dictionary_file(self, dictionary_file_path): self.model_.export_khiops_dictionary_file(dictionary_file_path) def _import_model(self, kdic_path): - """Sets model instance attribute by importing model from ``.kdic``""" + """Sets model instance attribute by importing model from `.kdic`""" self.model_ = self._read_model_from_dictionary_file(kdic_path) def _get_output_dir(self, fallback_dir): @@ -350,7 +353,7 @@ def fit(self, X, y=None, **kwargs): Returns ------- - self : `KhiopsEstimator` + self : KhiopsEstimator The fitted estimator instance. """ # Check for common sklearn parameters to comply with sklearn's check_estimator @@ -400,7 +403,7 @@ def _fit(self, ds, computation_dir, **kwargs): Parameters ---------- - ds : `Dataset` + ds : Dataset The learning dataset. computation_dir : str Path or URI where the Khiops computation results will be stored. @@ -530,14 +533,16 @@ def _transform_deploy_model( ): """Deploys a generic Khiops transformation model - It allows to implement `predict`, `predict_proba` and `transform` methods in the - sub-classes `KhiopsEncoder`, `KhiopsClassifier`, `KhiopsRegressor`. + It allows to implement [predict][], [predict_proba][] and [transform][] + methods in the + sub-classes [KhiopsEncoder][], [KhiopsClassifier][], [KhiopsRegressor][]. Parameters ---------- - X : :external:term:`array-like` of shape (n_samples, n_features_in) or dict - Training dataset. Either an :external:term:`array-like` or a ``dict`` - specification for multi-table datasets (see :doc:`/multi_table_primer`). + X : array-like of shape (n_samples, n_features_in) or dict + Training dataset. Either an [array-like][] or a [dict][] + specification for multi-table datasets + (see [Multi-Table Learning Primer](../multi_table_primer.md)). root_name : str Name of root table in trained Khiops model .kdic @@ -613,41 +618,41 @@ class KhiopsCoclustering(ClusterMixin, KhiopsEstimator): Parameters ---------- - build_distance_vars : bool, default ``False`` - If ``True`` includes a cluster distance variable in the deployment - build_frequency_vars : bool, default ``False`` - If ``True`` includes the frequency variables in the deployment. - build_name_var : bool, default ``False`` - If ``True`` includes a cluster id variable in the deployment. - verbose : bool, default ``False`` - If ``True`` it prints debug information and it does not erase temporary files + build_distance_vars : bool, default `False` + If `True` includes a cluster distance variable in the deployment + build_frequency_vars : bool, default `False` + If `True` includes the frequency variables in the deployment. + build_name_var : bool, default `False` + If `True` includes a cluster id variable in the deployment. + verbose : bool, default `False` + If `True` it prints debug information and it does not erase temporary files when fitting, predicting or transforming. output_dir : str, optional - Path of the output directory for the ``Coclustering.khcj`` report file and the - ``Coclustering.kdic`` modeling dictionary file. - auto_sort : bool, default ``True`` - *Advanced.* Only for multi-table inputs: If ``True`` input tables are + Path of the output directory for the `Coclustering.khcj` report file and the + `Coclustering.kdic` modeling dictionary file. + auto_sort : bool, default `True` + *Advanced.* Only for multi-table inputs: If `True` input tables are automatically sorted by their key before executing Khiops. If the input - tables are already sorted by their keys set this parameter to ``False`` - to speed up the processing. This affects the `predict` method. + tables are already sorted by their keys set this parameter to `False` + to speed up the processing. This affects the [predict][] method. *Note* The sort by key is performed in a left-to-right, hierarchical, lexicographic manner. Attributes ---------- is_multitable_model_ : bool - ``True`` if the model was fitted on a multi-table dataset. - model_ : `.DictionaryDomain` + `True` if the model was fitted on a multi-table dataset. + model_ : DictionaryDomain The Khiops dictionary domain for the trained coclustering. For coclustering it is a multi-table dictionary even though the model is single-table. model_main_dictionary_name_ : str - The name of the main Khiops dictionary within the ``model_`` domain. - model_report_ : `.CoclusteringResults` + The name of the main Khiops dictionary within the `model_` domain. + model_report_ : CoclusteringResults The Khiops report object. Examples -------- - See the following functions of the ``samples_sklearn.py`` documentation script: + See the following functions of the `samples_sklearn.py` documentation script: - `samples_sklearn.khiops_coclustering()` """ @@ -682,9 +687,10 @@ def fit(self, X, y=None, **kwargs): Parameters ---------- - X : :external:term:`array-like` of shape (n_samples, n_features_in) or dict - Training dataset. Either an :external:term:`array-like` or a ``dict`` - specification for multi-table datasets (see :doc:`/multi_table_primer`). + X : array-like of shape (n_samples, n_features_in) or dict + Training dataset. Either an [array-like][] or a [dict][] + specification for multi-table datasets + (see [Multi-Table Learning Primer](../multi_table_primer.md)). id_column : str The column that contains the id of the instance. columns : list, optional @@ -692,7 +698,7 @@ def fit(self, X, y=None, **kwargs): Returns ------- - self : `KhiopsCoclustering` + self : KhiopsCoclustering The calling estimator instance. """ return super().fit(X, y=y, **kwargs) @@ -704,7 +710,7 @@ def _fit_check_params(self, ds, **kwargs): ): raise ValueError( "One of the parameters 'build_name_var', " - "'build_distance_vars' or 'build_frequency_vars' must be ``True``" + "'build_distance_vars' or 'build_frequency_vars' must be `True`" ) # If 'columns' specified check that: @@ -844,7 +850,7 @@ def _create_coclustering_model_domain( Parameters ---------- - domain : `.DictionaryDomain` + domain : DictionaryDomain Input dictionary domain reflecting the structure of the input dataset. coclustering_file_path : str Path to the coclustering report file. @@ -915,8 +921,8 @@ def _simplify( Returns ------- - self : `KhiopsCoclustering` - A *new*, simplified `.KhiopsCoclustering` estimator instance. + self : KhiopsCoclustering + A *new*, simplified [KhiopsCoclustering][] estimator instance. """ # Check parameters: types and authorized value ranges assert hasattr(self, "model_report_") @@ -1065,8 +1071,8 @@ def simplify( Returns ------- - self : `KhiopsCoclustering` - A *new*, simplified `.KhiopsCoclustering` estimator instance. + self : KhiopsCoclustering + A *new*, simplified [KhiopsCoclustering][] estimator instance. """ # Check that the estimator is fitted: check_is_fitted(self) @@ -1083,9 +1089,10 @@ def predict(self, X): Parameters ---------- - X : :external:term:`array-like` of shape (n_samples, n_features_in) or dict - Training dataset. Either an :external:term:`array-like` or a ``dict`` - specification for multi-table datasets (see :doc:`/multi_table_primer`). + X : array-like of shape (n_samples, n_features_in) or dict + Training dataset. Either an [array-like][] or a [dict][] + specification for multi-table datasets + (see [Multi-Table Learning Primer](../multi_table_primer.md)). Returns ------- @@ -1241,21 +1248,22 @@ def _check_target_type(self, ds): def fit(self, X, y=None, **kwargs): """Fits a supervised estimator according to X,y - Called by the concrete sub-classes `KhiopsEncoder`, `KhiopsClassifier`, - `KhiopsRegressor`. + Called by the concrete sub-classes [KhiopsEncoder][], [KhiopsClassifier][], + [KhiopsRegressor][]. Parameters ---------- - X : :external:term:`array-like` of shape (n_samples, n_features_in) or dict - Training dataset. Either an :external:term:`array-like` or a ``dict`` - specification for multi-table datasets (see :doc:`/multi_table_primer`). + X : array-like of shape (n_samples, n_features_in) or dict + Training dataset. Either an [array-like][] or a [dict][] + specification for multi-table datasets + (see [Multi-Table Learning Primer](../multi_table_primer.md)). - y : :external:term:`array-like` of shape (n_samples,) + y : array-like of shape (n_samples,) The target values. Returns ------- - self : `KhiopsSupervisedEstimator` + self : KhiopsSupervisedEstimator The calling estimator instance. """ if y is None: @@ -1651,18 +1659,19 @@ class KhiopsClassifier(ClassifierMixin, KhiopsPredictor): r"""Khiops Selective Naive Bayes Classifier This classifier supports automatic feature engineering on multi-table datasets. See - :doc:`/multi_table_primer` for more details. + [Multi-Table Learning Primer](../multi_table_primer.md) for more details. - .. note:: + !!! note - Visit `the Khiops site `_ to learn + Visit [the Khiops site](https://khiops.org/learn/understand) to learn about the automatic feature engineering algorithm. Parameters ---------- n_features : int, default 1000 Maximum number of features to construct automatically. See - :doc:`/multi_table_primer` for more details on the multi-table-specific + [Multi-Table Learning Primer](../multi_table_primer.md) + for more details on the multi-table-specific features. n_pairs : int, default 0 Maximum number of pair features to construct. These features are 2D grid @@ -1691,11 +1700,11 @@ class KhiopsClassifier(ClassifierMixin, KhiopsPredictor): specific_pairs : list of tuple, optional User-specified pairs as a list of 2-tuples of feature names. If a given tuple contains only one non-empty feature name, then it generates all the pairs - containing it (within the maximum limit ``n_pairs``). These pairs have top + containing it (within the maximum limit `n_pairs`). These pairs have top priority: they are constructed first. - all_possible_pairs : bool, default ``True`` - If ``True`` tries to create all possible pairs within the limit ``n_pairs``. - Pairs specified with ``specific_pairs`` have top priority: they are constructed + all_possible_pairs : bool, default `True` + If `True` tries to create all possible pairs within the limit `n_pairs`. + Pairs specified with `specific_pairs` have top priority: they are constructed first. construction_rules : list of str, optional Allowed rules for the automatic feature construction. If not set, Khiops @@ -1704,22 +1713,22 @@ class KhiopsClassifier(ClassifierMixin, KhiopsPredictor): n_feature_parts : int, default 0 Maximum number of variable parts produced by preprocessing methods. If equal to 0 it is automatically calculated. - group_target_value : bool, default ``False`` + group_target_value : bool, default `False` Allows grouping of the target values in classification. It can substantially increase the training time. - keep_selected_variables_only : bool, default ``True`` + keep_selected_variables_only : bool, default `True` Keeps only predictor-selected variables in the supervised analysis report. - verbose : bool, default ``False`` - If ``True`` it prints debug information and it does not erase temporary files + verbose : bool, default `False` + If `True` it prints debug information and it does not erase temporary files when fitting, predicting or transforming. output_dir : str, optional - Path of the output directory for the ``AllReports.khj`` report file and the - ``Modeling.kdic`` modeling dictionary file. By default these files are deleted. - auto_sort : bool, default ``True`` - *Advanced.* Only for multi-table inputs: If ``True`` input tables are pre-sorted + Path of the output directory for the `AllReports.khj` report file and the + `Modeling.kdic` modeling dictionary file. By default these files are deleted. + auto_sort : bool, default `True` + *Advanced.* Only for multi-table inputs: If `True` input tables are pre-sorted by their key before executing Khiops. If the input tables are already sorted by - their keys set this parameter to ``False`` to speed up the processing. This - affects the `fit`, `predict` and `predict_proba` methods. + their keys set this parameter to `False` to speed up the processing. This + affects the [fit][], [predict][] and [predict_proba][] methods. *Note* The sort by key is performed in a left-to-right, hierarchical, lexicographic manner. @@ -1729,21 +1738,21 @@ class KhiopsClassifier(ClassifierMixin, KhiopsPredictor): The number of classes seen in training. classes_ : `ndarray ` of shape (n_classes\_,) The list of classes seen in training. Depending on the training target, the - contents are ``int`` or ``str``. + contents are `int` or `str`. n_features_in_ : int The number of features in the main table of the training dataset. is_multitable_model_ : bool - ``True`` if the model was fitted on a multi-table dataset. - model_ : `.DictionaryDomain` + `True` if the model was fitted on a multi-table dataset. + model_ : DictionaryDomain The Khiops dictionary domain for the trained classifier. model_main_dictionary_name_ : str - The name of the main Khiops dictionary within the ``model_`` domain. - model_report_ : `.AnalysisResults` + The name of the main Khiops dictionary within the `model_` domain. + model_report_ : AnalysisResults The Khiops report object. Examples -------- - See the following functions of the ``samples_sklearn.py`` documentation script: + See the following functions of the `samples_sklearn.py` documentation script: - `samples_sklearn.khiops_classifier()` - `samples_sklearn.khiops_classifier_multiclass()` - `samples_sklearn.khiops_classifier_multitable_star()` @@ -1871,16 +1880,17 @@ def fit(self, X, y, **kwargs): Parameters ---------- - X : :external:term:`array-like` of shape (n_samples, n_features_in) or dict - Training dataset. Either an :external:term:`array-like` or a ``dict`` - specification for multi-table datasets (see :doc:`/multi_table_primer`). + X : array-like of shape (n_samples, n_features_in) or dict + Training dataset. Either an [array-like][] or a [dict][] + specification for multi-table datasets + (see [Multi-Table Learning Primer](../multi_table_primer.md)). - y : :external:term:`array-like` of shape (n_samples,) + y : array-like of shape (n_samples,) The target values. Returns ------- - self : `KhiopsClassifier` + self : KhiopsClassifier The calling estimator instance. """ kwargs["categorical_target"] = True @@ -1954,18 +1964,19 @@ def predict(self, X): Parameters ---------- - X : :external:term:`array-like` of shape (n_samples, n_features_in) or dict - Training dataset. Either an :external:term:`array-like` or a ``dict`` - specification for multi-table datasets (see :doc:`/multi_table_primer`). + X : array-like of shape (n_samples, n_features_in) or dict + Training dataset. Either an [array-like][] or a [dict][] + specification for multi-table datasets + (see [Multi-Table Learning Primer](../multi_table_primer.md)). Returns ------- `ndarray ` An array containing the encoded columns. A first column containing key - column ids is added in multi-table mode. The `numpy.dtype` of the array - matches the type of ``y`` used during training. It will be integer, float, - or boolean if the classifier was trained with a ``y`` of the corresponding - type. Otherwise it will be ``str``. + column ids is added in multi-table mode. The [numpy.dtype][] of the array + matches the type of `y` used during training. It will be integer, float, + or boolean if the classifier was trained with a `y` of the corresponding + type. Otherwise it will be `str`. The key columns are added for multi-table tasks. """ @@ -1988,19 +1999,17 @@ def predict_proba(self, X): Parameters ---------- - X : :external:term:`array-like` of shape (n_samples, n_features_in) or dict - Training dataset. Either an :external:term:`array-like` or a ``dict`` - specification for multi-table datasets (see :doc:`/multi_table_primer`). + X : array-like of shape (n_samples, n_features_in) or dict + Training dataset. Either an [array-like][] or a [dict][] + specification for multi-table datasets + (see [Multi-Table Learning Primer](../multi_table_primer.md)). Returns ------- - `numpy.array` or str + numpy.ndarray or str The probability of the samples for each class in the model. The columns are - named with the pattern ``Prob`` for each ```` found in the - training dataset. The output data container depends on ``X``: - - - Dataframe or dataframe-based ``dict`` dataset specification: - `numpy.array` + named with the pattern `Prob` for each `` found in the + training dataset. The output data container is of type [numpy.ndarray][]. The key columns are added for multi-table tasks. """ @@ -2075,18 +2084,19 @@ class KhiopsRegressor(RegressorMixin, KhiopsPredictor): r"""Khiops Selective Naive Bayes Regressor This regressor supports automatic feature engineering on multi-table datasets. See - :doc:`/multi_table_primer` for more details. + [Multi-Table Learning Primer](../multi_table_primer.md) for more details. - .. note:: + !!! note - Visit `the Khiops site `_ to learn + Visit [the Khiops site](https://khiops.org/learn/understand) to learn about the automatic feature engineering algorithm. Parameters ---------- n_features : int, default 1000 Maximum number of features to construct automatically. See - :doc:`/multi_table_primer` for more details on the multi-table-specific + [Multi-Table Learning Primer](../multi_table_primer.md) + for more details on the multi-table-specific features. n_trees : int, default 10 Maximum number of decision tree features to construct. The constructed trees @@ -2113,19 +2123,19 @@ class KhiopsRegressor(RegressorMixin, KhiopsPredictor): n_feature_parts : int, default 0 Maximum number of variable parts produced by preprocessing methods. If equal to 0 it is automatically calculated. - keep_selected_variables_only : bool, default ``True`` + keep_selected_variables_only : bool, default `True` Keeps only predictor-selected variables in the supervised analysis report. - verbose : bool, default ``False`` - If ``True`` it prints debug information and it does not erase temporary files + verbose : bool, default `False` + If `True` it prints debug information and it does not erase temporary files when fitting, predicting or transforming. output_dir : str, optional - Path of the output directory for the ``AllReports.khj`` report file and the - ``Modeling.kdic`` modeling dictionary file. By default these files are deleted. - auto_sort : bool, default ``True`` - *Advanced.* Only for multi-table inputs: If ``True`` input tables are pre-sorted + Path of the output directory for the `AllReports.khj` report file and the + `Modeling.kdic` modeling dictionary file. By default these files are deleted. + auto_sort : bool, default `True` + *Advanced.* Only for multi-table inputs: If `True` input tables are pre-sorted by their key before executing Khiops. If the input tables are already sorted by - their keys set this parameter to ``False`` to speed up the processing. This - affects the `fit` and `predict` methods. + their keys set this parameter to `False` to speed up the processing. This + affects the [fit][] and [predict][] methods. *Note* The sort by key is performed in a left-to-right, hierarchical, lexicographic manner. @@ -2134,17 +2144,17 @@ class KhiopsRegressor(RegressorMixin, KhiopsPredictor): n_features_in_ : int The number of features in the main table of the training dataset. is_multitable_model_ : bool - ``True`` if the model was fitted on a multi-table dataset. - model_ : `.DictionaryDomain` + `True` if the model was fitted on a multi-table dataset. + model_ : DictionaryDomain The Khiops dictionary domain for the trained regressor. model_main_dictionary_name_ : str - The name of the main Khiops dictionary within the ``model_`` domain. - model_report_ : `.AnalysisResults` + The name of the main Khiops dictionary within the `model_` domain. + model_report_ : AnalysisResults The Khiops report object. Examples -------- - See the following functions of the ``samples_sklearn.py`` documentation script: + See the following functions of the `samples_sklearn.py` documentation script: - `samples_sklearn.khiops_regressor()` """ # pylint: enable=line-too-long @@ -2187,22 +2197,24 @@ def __init__( def fit(self, X, y=None, **kwargs): """Fits a Selective Naive Bayes regressor according to X, y - .. warning:: - Make sure that the type of ``y`` is float. This is easily done with ``y = + !!! warning + + Make sure that the type of `y` is float. This is easily done with ``y = y.astype(float)``. Parameters ---------- - X : :external:term:`array-like` of shape (n_samples, n_features_in) or dict - Training dataset. Either an :external:term:`array-like` or a ``dict`` - specification for multi-table datasets (see :doc:`/multi_table_primer`). + X : array-like of shape (n_samples, n_features_in) or dict + Training dataset. Either an [array-like][] or a [dict][] + specification for multi-table datasets + (see [Multi-Table Learning Primer](../multi_table_primer.md)). - y : :external:term:`array-like` of shape (n_samples,) + y : array-like of shape (n_samples,) The target values. Returns ------- - self : `KhiopsRegressor` + self : KhiopsRegressor The calling estimator instance. """ kwargs["categorical_target"] = False @@ -2252,22 +2264,20 @@ def predict(self, X): Parameters ---------- - X : :external:term:`array-like` of shape (n_samples, n_features_in) or dict - Training dataset. Either an :external:term:`array-like` or a ``dict`` - specification for multi-table datasets (see :doc:`/multi_table_primer`). + X : array-like of shape (n_samples, n_features_in) or dict + Training dataset. Either an [array-like][] or a [dict][] + specification for multi-table datasets + (see [Multi-Table Learning Primer](../multi_table_primer.md)). Returns ------- - `numpy.ndarray` or str + numpy.ndarray or str An array containing the encoded columns. A first column containing key column ids is added in multi-table mode. The key columns are added for - multi-table tasks. The array is in the form of: - - - `numpy.ndarray` if X is :external:term:`array-like`, or dataset spec - containing `pandas.DataFrame` table. - - str (a path for the file containing the array) if X is a dataset spec - containing file-path tables. + multi-table tasks. The array is in the form of a [numpy.ndarray][] + if X is [array-like][], or dataset spec containing + [pandas.DataFrame][] tables. """ assert ( self._khiops_baseline_model_prefix is not None @@ -2293,11 +2303,12 @@ class KhiopsEncoder(TransformerMixin, KhiopsSupervisedEstimator): Parameters ---------- - categorical_target : bool, default ``True`` - ``True`` if the target column is categorical. + categorical_target : bool, default `True` + `True` if the target column is categorical. n_features : int, default 1000 Maximum number of features to construct automatically. See - :doc:`/multi_table_primer` for more details on the multi-table-specific + [Multi-Table Learning Primer](../multi_table_primer.md) + for more details on the multi-table-specific features. n_pairs : int, default 0 Maximum number of pair features to construct. These features are 2D grid @@ -2319,11 +2330,11 @@ class KhiopsEncoder(TransformerMixin, KhiopsSupervisedEstimator): specific_pairs : list of tuple, optional User-specified pairs as a list of 2-tuples of feature names. If a given tuple contains only one non-empty feature name, then it generates all the pairs - containing it (within the maximum limit ``n_pairs``). These pairs have top + containing it (within the maximum limit `n_pairs`). These pairs have top priority: they are constructed first. - all_possible_pairs : bool, default ``True`` - If ``True`` tries to create all possible pairs within the limit ``n_pairs``. - Pairs specified with ``specific_pairs`` have top priority: they are constructed + all_possible_pairs : bool, default `True` + If `True` tries to create all possible pairs within the limit `n_pairs`. + Pairs specified with `specific_pairs` have top priority: they are constructed first. construction_rules : list of str, optional Allowed rules for the automatic feature construction. If not set, Khiops @@ -2332,13 +2343,13 @@ class KhiopsEncoder(TransformerMixin, KhiopsSupervisedEstimator): n_feature_parts : int, default 0 Maximum number of variable parts produced by preprocessing methods. If equal to 0 it is automatically calculated. - informative_features_only : bool, default ``True`` - If ``True`` keeps only informative features. - group_target_value : bool, default ``False`` + informative_features_only : bool, default `True` + If `True` keeps only informative features. + group_target_value : bool, default `False` Allows grouping of the target values in classification. It can substantially increase the training time. - keep_initial_variables : bool, default ``False`` - If ``True`` the original columns are kept in the transformed data. + keep_initial_variables : bool, default `False` + If `True` the original columns are kept in the transformed data. transform_type_categorical : str, default "part_id" Type of transformation for categorical features. Valid values: - "part_id" @@ -2346,8 +2357,8 @@ class KhiopsEncoder(TransformerMixin, KhiopsSupervisedEstimator): - "dummies" - "conditional_info" - See the documentation for the ``categorical_recoding_method`` parameter of the - `~.api.train_recoder` function for more details. + See the documentation for the `categorical_recoding_method` parameter of the + [api.train_recoder][khiops.core.api.train_recoder] function for more details. transform_type_numerical : str, default "part_id" One of the following strings are valid: - "part_id" @@ -2358,42 +2369,42 @@ class KhiopsEncoder(TransformerMixin, KhiopsSupervisedEstimator): - "0-1_normalization" - "rank_normalization" - See the documentation for the ``numerical_recoding_method`` parameter of the - `~.api.train_recoder` function for more details. + See the documentation for the `numerical_recoding_method` parameter of the + [api.train_recoder][khiops.core.api.train_recoder] function for more details. transform_type_pairs : str, default "part_id" Type of transformation for bivariate features. Valid values: - "part_id" - "part_label" - "dummies" - "conditional_info" - verbose : bool, default ``False`` - If ``True`` it prints debug information and it does not erase temporary files + verbose : bool, default `False` + If `True` it prints debug information and it does not erase temporary files when fitting, predicting or transforming. output_dir : str, optional - Path of the output directory for the ``AllReports.khj`` report file and the - ``Modeling.kdic`` modeling dictionary file. By default these files are deleted. - auto_sort : bool, default ``True`` - *Advanced.* Only for multi-table inputs: If ``True`` input tables are pre-sorted + Path of the output directory for the `AllReports.khj` report file and the + `Modeling.kdic` modeling dictionary file. By default these files are deleted. + auto_sort : bool, default `True` + *Advanced.* Only for multi-table inputs: If `True` input tables are pre-sorted by their key before executing Khiops. If the input tables are already sorted by - their keys set this parameter to ``False`` to speed up the processing. This - affects the `fit` and `transform` methods. + their keys set this parameter to `False` to speed up the processing. This + affects the [fit][] and [transform][] methods. *Note* The sort by key is performed in a left-to-right, hierarchical, lexicographic manner. Attributes ---------- is_multitable_model_ : bool - ``True`` if the model was fitted on a multi-table dataset. - model_ : `.DictionaryDomain` + `True` if the model was fitted on a multi-table dataset. + model_ : DictionaryDomain The Khiops dictionary domain for the trained encoder. model_main_dictionary_name_ : str - The name of the main Khiops dictionary within the ``model_`` domain. - model_report_ : `.AnalysisResults` + The name of the main Khiops dictionary within the `model_` domain. + model_report_ : AnalysisResults The Khiops report object. Examples -------- - See the following functions of the ``samples_sklearn.py`` documentation script: + See the following functions of the `samples_sklearn.py` documentation script: - `samples_sklearn.khiops_encoder()` - `samples_sklearn.khiops_encoder_multitable_star()` - `samples_sklearn.khiops_encoder_multitable_snowflake()` @@ -2599,16 +2610,17 @@ def fit(self, X, y=None, **kwargs): Parameters ---------- - X : :external:term:`array-like` of shape (n_samples, n_features_in) or dict - Training dataset. Either an :external:term:`array-like` or a ``dict`` - specification for multi-table datasets (see :doc:`/multi_table_primer`). + X : array-like of shape (n_samples, n_features_in) or dict + Training dataset. Either an [array-like][] or a [dict][] + specification for multi-table datasets + (see [Multi-Table Learning Primer](../multi_table_primer.md)). - y : :external:term:`array-like` of shape (n_samples,) + y : array-like of shape (n_samples,) The target values. Returns ------- - self : `KhiopsEncoder` + self : KhiopsEncoder The calling estimator instance. """ kwargs["categorical_target"] = self.categorical_target @@ -2661,19 +2673,21 @@ def _fit_training_post_process(self, ds): def transform(self, X): """Transforms X with a fitted Khiops supervised encoder - .. note:: + !!! note + Numerical features are encoded to categorical ones. See the - ``transform_type_numerical`` parameter for details. + `transform_type_numerical` parameter for details. Parameters ---------- - X : :external:term:`array-like` of shape (n_samples, n_features_in) or dict - Training dataset. Either an :external:term:`array-like` or a ``dict`` - specification for multi-table datasets (see :doc:`/multi_table_primer`). + X : array-like of shape (n_samples, n_features_in) or dict + Training dataset. Either an [array-like][] or a [dict][] + specification for multi-table datasets + (see [Multi-Table Learning Primer](../multi_table_primer.md)). Returns ------- - `ndarray ` + numpy.ndarray An array containing the encoded columns. A first column containing key column ids is added in multi-table mode. """ @@ -2718,16 +2732,17 @@ def fit_transform(self, X, y=None, **kwargs): Parameters ---------- - X : :external:term:`array-like` of shape (n_samples, n_features_in) or dict - Training dataset. Either an :external:term:`array-like` or a ``dict`` - specification for multi-table datasets (see :doc:`/multi_table_primer`). + X : array-like of shape (n_samples, n_features_in) or dict + Training dataset. Either an [array-like][] or a [dict][] + specification for multi-table datasets + (see [Multi-Table Learning Primer](../multi_table_primer.md)). - y : :external:term:`array-like` of shape (n_samples,) + y : array-like of shape (n_samples,) The target values. Returns ------- - self : `KhiopsEncoder` + self : KhiopsEncoder The calling estimator instance. """ return self.fit(X, y, **kwargs).transform(X) diff --git a/khiops/sklearn/helpers.py b/khiops/sklearn/helpers.py index 8f774f8b..93d2a3d9 100644 --- a/khiops/sklearn/helpers.py +++ b/khiops/sklearn/helpers.py @@ -22,31 +22,38 @@ def train_test_split_dataset(ds_spec, y=None, test_size=0.25, **kwargs): Parameters ---------- - ds_spec : ``dict`` - A ``dict`` multi-table dataset specification (see :doc:`/multi_table_primer`). + ds_spec : `dict` + A `dict` multi-table dataset specification + (see [Multi-Table Learning Primer](../multi_table_primer.md)). Only Pandas, NumPy, SciPy tables are accepted in the spec. - y : :external:term:`array-like` of size (n_samples,) , optional - The target values. ``n_samples`` is the number of rows of the main table in - ``ds_spec``. + y : array-like of size (n_samples,) , optional + The target values. `n_samples` is the number of rows of the main table in + `ds_spec`. test_size : float, default 0.25 The proportion of the dataset (between 0.0 and 1.0) to be included in the test split. - ... : - Other optional parameters for `sklearn.model_selection.train_test_split` + + See Also + -------- + - [sklearn.model_selection.train_test_split][train_test_split] : Other + optional parameters. Raises ------ - `TypeError` - If ``ds_spec`` is not dict-like. + TypeError + If `ds_spec` is not dict-like. - `ValueError` - If the tables in ``ds_spec`` are file-paths. + ValueError + If the tables in `ds_spec` are file-paths. Examples -------- - See the following functions of the ``samples_sklearn.py`` documentation script: - - `samples_sklearn.khiops_classifier_multitable_star` - - `samples_sklearn.khiops_classifier_multitable_snowflake` + See the following functions of the `samples_sklearn.py` documentation script: + + - [khiops_classifier_multitable_star]( + ../samples/samples_sklearn.md#khiops_classifier_multitable_star) + - [khiops_classifier_multitable_snowflake]( + ../samples/samples_sklearn.md#khiops_classifier_multitable_snowflake) """ # Check the types diff --git a/khiops/tools.py b/khiops/tools.py index efcd9d7d..82c0a0dd 100644 --- a/khiops/tools.py +++ b/khiops/tools.py @@ -6,8 +6,10 @@ ###################################################################################### """Miscellaneous utility tools -.. warning:: - The entry point functions in this module use `sys.exit`. They are not designed to be +!!! warning + + The entry point functions in this module use [sys.exit][]. They are not designed + to be called from another program or python shell. """ import argparse @@ -116,18 +118,18 @@ def download_datasets( """Downloads the Khiops sample datasets for a given version The datasets are downloaded to: - - all systems: ``KHIOPS_SAMPLES_DIR/khiops_data/samples`` if - ``KHIOPS_SAMPLES_DIR`` is defined and non-empty + - all systems: `KHIOPS_SAMPLES_DIR/khiops_data/samples` if + `KHIOPS_SAMPLES_DIR` is defined and non-empty - Windows: - - ``%PUBLIC%\\khiops_data\\samples`` if ``%PUBLIC%`` is defined - - ``%USERPROFILE%\\khiops_data\\samples`` otherwise - - Linux/macOS: ``$HOME/khiops_data/samples`` + - `%PUBLIC%\\khiops_data\\samples` if `%PUBLIC%` is defined + - `%USERPROFILE%\\khiops_data\\samples` otherwise + - Linux/macOS: `$HOME/khiops_data/samples` Parameters - ========== - force_overwrite : bool, default ``False`` - If ``True`` it always overwrites the local samples directory even if it exists. - version : str, default "10.2.0" + ---------- + force_overwrite : bool, default `False` + If `True` it always overwrites the local samples directory even if it exists. + version : str, default "11.0.0" The version of the samples datasets. """ # Note: The hidden parameter _called_from_shell is just to change the user messages. diff --git a/pyproject.toml b/pyproject.toml index 5f3fe936..ef04b5f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -146,5 +146,5 @@ kh-samples = "khiops.tools:kh_samples_entry_point" kh-download-datasets = "khiops.tools:kh_download_datasets_entry_point" [build-system] -requires = ["setuptools", "tomli; python_version<'3.11'", "toml; python_version>='3.12'"] +requires = ["setuptools", "tomli; python_version<'3.11'"] build-backend = "setuptools.build_meta" diff --git a/tests/test_core.py b/tests/test_core.py index 59e645aa..84a413fb 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -970,7 +970,7 @@ def test_std_stream_errors(self): class MockedRunnerContext: - """A context to mock the `~.KhiopsLocalRunner.raw_run` function""" + """A context to mock the `KhiopsLocalRunner.raw_run` function""" def __init__(self, mocked_raw_run): self.mocked_raw_run = mocked_raw_run diff --git a/tests/test_khiops_integrations.py b/tests/test_khiops_integrations.py index 2b851420..ee3132db 100644 --- a/tests/test_khiops_integrations.py +++ b/tests/test_khiops_integrations.py @@ -306,7 +306,8 @@ def test_runner_environment_for_openmpi5(self): def test_runner_environment_initialization(self): """Test that local runner initializes/ed its environment properly - .. note:: + !!! note + To test a real initialization this test should be executed alone. """ diff --git a/uv.lock b/uv.lock new file mode 100644 index 00000000..4a84e56c --- /dev/null +++ b/uv.lock @@ -0,0 +1,1507 @@ +version = 1 +revision = 3 +requires-python = ">=3.10" +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version < '3.11'", +] + +[[package]] +name = "azure-core" +version = "1.41.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/f3/b416179e408990df5db0d516283022dde0f5d0111d98c1a848e41853e81c/azure_core-1.41.0.tar.gz", hash = "sha256:f46ff5dfcd230f25cf1c19e8a34b8dc08a337b2503e268bb600a16c00db8ad5a", size = 381042, upload-time = "2026-05-07T23:30:54.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/db/325c6d7312d2200251c52323878281045aaffcb5586612296484e4280eaa/azure_core-1.41.0-py3-none-any.whl", hash = "sha256:522b4011e8180b1a3dcd2024396a4e7fe9ac37fb8597db47163d230b5efe892d", size = 220920, upload-time = "2026-05-07T23:30:56.357Z" }, +] + +[[package]] +name = "azure-storage-blob" +version = "12.30.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "cryptography" }, + { name = "isodate" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/48/84a820d898267f662b5c06f7cd76fdb8a9e272b44aa9376cef3ec0f6a294/azure_storage_blob-12.30.0.tar.gz", hash = "sha256:2cd74d4d5731e5eb6b8d5c5056ee115a5e88f8fdf22517b739836fda685018be", size = 618229, upload-time = "2026-06-08T11:45:35.575Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5e/0b/e106f0fd7fa785867d9ffcc47dc9e6237c0e58f51058473b777487a98edc/azure_storage_blob-12.30.0-py3-none-any.whl", hash = "sha256:d415ac50b67a8da6b3ae7e9f1014b1b55cd7aafa0b8d4ca9b380568dc7360423", size = 435610, upload-time = "2026-06-08T11:45:37.213Z" }, +] + +[[package]] +name = "azure-storage-file-share" +version = "12.26.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "azure-core" }, + { name = "cryptography" }, + { name = "isodate" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/80/35/50eb9772dba52fe921f75858ec5a5f0bf43cc5544b7247936bddf56dc976/azure_storage_file_share-12.26.0.tar.gz", hash = "sha256:08cf8bc436df532cdb9bfdfce7bb10c5f1d83d51360954cd8eef56cbccf4300a", size = 383344, upload-time = "2026-06-08T12:45:38.107Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/99/90b557d7ab2601f9d313cfa9d166bd2e892881d69fbbff4ad3454a128dae/azure_storage_file_share-12.26.0-py3-none-any.whl", hash = "sha256:c4117d311ed0fe15a963118fe934f33db0c91d706a13a45c7c8dcb969b2e9578", size = 319081, upload-time = "2026-06-08T12:45:39.827Z" }, +] + +[[package]] +name = "boto3" +version = "1.35.69" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, + { name = "jmespath" }, + { name = "s3transfer" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/72/f80a131ac5e069b12975c1054243713c46617cc769dc78348e1d59a3f945/boto3-1.35.69.tar.gz", hash = "sha256:40db86c7732a310b282f595251995ecafcbd62009a57e47a22683862e570cc7a", size = 110976, upload-time = "2024-11-25T20:32:18.041Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/31/e185d967e7a311d1ed4d4eb03c1606bcd2b9cfcdc85cfe9c8183c1beafbd/boto3-1.35.69-py3-none-any.whl", hash = "sha256:20945912130cca1505f45819cd9b7183a0e376e91a1221a0b1f50c80d35fd7e2", size = 139180, upload-time = "2024-11-25T20:32:15.303Z" }, +] + +[[package]] +name = "botocore" +version = "1.35.99" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jmespath" }, + { name = "python-dateutil" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/9c/1df6deceee17c88f7170bad8325aa91452529d683486273928eecfd946d8/botocore-1.35.99.tar.gz", hash = "sha256:1eab44e969c39c5f3d9a3104a0836c24715579a455f12b3979a31d7cde51b3c3", size = 13490969, upload-time = "2025-01-14T20:20:11.419Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/dd/d87e2a145fad9e08d0ec6edcf9d71f838ccc7acdd919acc4c0d4a93515f8/botocore-1.35.99-py3-none-any.whl", hash = "sha256:b22d27b6b617fc2d7342090d6129000af2efd20174215948c0d7ae2da0fab445", size = 13293216, upload-time = "2025-01-14T20:20:06.427Z" }, +] + +[[package]] +name = "certifi" +version = "2026.7.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a3/c2/24167ea9858356b47a87a50d39908bfdb72ceeefe0041586e704e5376b3a/certifi-2026.7.22.tar.gz", hash = "sha256:741e2c3b351ddf169a738da9f2c048608ff7f2c5cc02f1ebc6b118bb090d5d55", size = 138112, upload-time = "2026-07-22T03:35:12.644Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/a7/71ac2cff56fec219ed242bb11b8efb69fcc4bec75db06fb7bfe35de520e6/certifi-2026.7.22-py3-none-any.whl", hash = "sha256:62f22742b58a1a33014a2b6b706588a8d7e2a88ae7bd1a6ebe8c992928483775", size = 136983, upload-time = "2026-07-22T03:35:11.276Z" }, +] + +[[package]] +name = "cffi" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/ef/008a1939e372c06329a3fce4279c02f328488f3526744906eeec3da7ad5f/cffi-2.1.1.tar.gz", hash = "sha256:dd31f52ea1086513bb9df30f8fcee9b8918323ae067a3d5b78bc826a000712be", size = 530807, upload-time = "2026-08-03T21:21:18.939Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/d2/2cde336b375f55c76ca670f0be3978cc048e31e24f3b4d7ce8473150a388/cffi-2.1.1-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:baed1e86cc735622097354b9d1281406caf42ff42a886d29faa8e8d1630333be", size = 183779, upload-time = "2026-08-03T21:19:15.602Z" }, + { url = "https://files.pythonhosted.org/packages/94/1a/4b2f7c92293ba05cbd4a9a1b28faaf0326272d9488e6354657571c48a7aa/cffi-2.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca82be1a1d406ecfe1d25dc16cb33488e5a16bf4438c9fb590484ea29d92478b", size = 184178, upload-time = "2026-08-03T21:19:16.67Z" }, + { url = "https://files.pythonhosted.org/packages/17/0b/ba385d8ccedf926c3cd06e8e2f327027da5afe5f0eb30f1f7bc43ac55125/cffi-2.1.1-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:42e2f76b9455f5a9a844f770bf3e200ed3da0e15f5df3db9c31fe80b04b3d004", size = 211037, upload-time = "2026-08-03T21:19:17.705Z" }, + { url = "https://files.pythonhosted.org/packages/a3/b9/0f2e58b2cefa33255bff36935d42b13180fe559bba82596540eb404bde7d/cffi-2.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5a59cc1c4442bc3d5c703bf720b51138d0bfc173618807c9ee2490a7541dd3d9", size = 218652, upload-time = "2026-08-03T21:19:18.735Z" }, + { url = "https://files.pythonhosted.org/packages/37/15/180e0dab27b9312c7479003d14c9e547634b7dcb934e2cc4650e1b131a7a/cffi-2.1.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:9f8d177621de5cb38ee3e731eda45d421db093ec0739f46a5594babda7987a98", size = 205422, upload-time = "2026-08-03T21:19:19.96Z" }, + { url = "https://files.pythonhosted.org/packages/18/d4/03026f0c850cbbaa9030750490225b4a7f4d524ea4df72c3cc740a90f4ef/cffi-2.1.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:75f80557d1389eddbd0de2681f6a390a0c5338c31ddaa821381c203fc3fd50d9", size = 205444, upload-time = "2026-08-03T21:19:21.246Z" }, + { url = "https://files.pythonhosted.org/packages/75/77/60bebf6f818bec84210ac5b6979ce4eeadce6fbbaabc9c7ab23e506d1ce5/cffi-2.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:194cffa889098ced9976c3fc6340305e43f6303657d298da55366907c05c22d6", size = 218742, upload-time = "2026-08-03T21:19:22.523Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ae/679bf47e73fd77b352171727f07de559a003f14de5d02b904a6ec1fa73ca/cffi-2.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5bb4e7ea95dcd6a014a6fef62e62467d67d8e582326443f3d68e71d6320a9fcf", size = 221054, upload-time = "2026-08-03T21:19:23.694Z" }, + { url = "https://files.pythonhosted.org/packages/09/b8/eefc0e06913b70aa153bf74c946094a18f58fd4aff11b7f372bfdfdca050/cffi-2.1.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3d22a20b1fb1632cc72c22f95f7b0d2961c3e1c235f245ba4c606c4771035659", size = 213489, upload-time = "2026-08-03T21:19:24.922Z" }, + { url = "https://files.pythonhosted.org/packages/6f/13/4e56852824a03cdf68523a35686f1c28eacd4bd30a7b0a78e682e6e6e1d3/cffi-2.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1dea0e4d7d4f11f619fe8c1d76caf49e24405b4b5743c0e3be16a500ecd930c9", size = 220241, upload-time = "2026-08-03T21:19:26.214Z" }, + { url = "https://files.pythonhosted.org/packages/99/7f/040f9e163e4acac3ee3d85b02d00b2576e7ca980d8785f0a3a5f1a9bf7f5/cffi-2.1.1-cp310-cp310-win32.whl", hash = "sha256:7ce713ace7c0e4520535b42b77eaa742c16dab813978064913e5a3cf82973b41", size = 174578, upload-time = "2026-08-03T21:19:27.338Z" }, + { url = "https://files.pythonhosted.org/packages/ba/0b/644a2ec1a4eaba49c2939410bb1eb1d25b09d6d0582f5d2f95c537043725/cffi-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:a48d62ab9d6f4f98c983223a547af44be6ca3691074c31cecced6facd3ba2dc1", size = 185082, upload-time = "2026-08-03T21:19:28.409Z" }, + { url = "https://files.pythonhosted.org/packages/70/d2/16d99a0c4948febc0ebd133a13b2f688ff7f8cb04da971e1128872ce0c03/cffi-2.1.1-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:c8d2c9fd1f2d16f780d15127abb050d13d1a76c03a4bd87d7e4980e45e511e12", size = 183838, upload-time = "2026-08-03T21:19:29.637Z" }, + { url = "https://files.pythonhosted.org/packages/cd/95/31b535a9f0220ae9f357de4a08d57ce89cb417653c2fd9f075f50822a388/cffi-2.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:398aff33cee2767e3e781d2554c54bd0dff386bb437581e0d8011fde1a942ec1", size = 184168, upload-time = "2026-08-03T21:19:30.764Z" }, + { url = "https://files.pythonhosted.org/packages/ad/5a/4707a0dc1f203f5dde5a907b0d4e3c25d71120241048bd5bc6f1bb9d4e71/cffi-2.1.1-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:154852545011f779917b11c78db2358d095da62a9a172b78ad0a583ee5adc0d0", size = 211805, upload-time = "2026-08-03T21:19:31.867Z" }, + { url = "https://files.pythonhosted.org/packages/ad/66/c19feabb28485b6e0bbaaafa90837a1ef5d302e90f2178bd33f17a49879b/cffi-2.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3311ed60d36f83378794e1009ac6258bafbf81f7888b4caa7b35a521e3f95813", size = 218716, upload-time = "2026-08-03T21:19:32.896Z" }, + { url = "https://files.pythonhosted.org/packages/a7/92/500760486c8baab49a7a8a58ba7fc3355ec3974b454b8a09e528efde9e1d/cffi-2.1.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6e192623c49c94421616a5778fba35cf0d5a8d000650c1967ef4448ee5cdd990", size = 205569, upload-time = "2026-08-03T21:19:34.142Z" }, + { url = "https://files.pythonhosted.org/packages/a5/a7/a67c733254d6e7373f7822f8082d8d6beade791e0cf12a7611f376fa61c7/cffi-2.1.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a6e721d4b0e45d5b65e87534470e67b18dcd092c83f68fba09f152b9cbc061af", size = 204907, upload-time = "2026-08-03T21:19:35.174Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a4/4399daaf8f7dfee9d7c3327fdb0426ee041cc63edc358b93911ceb2bfc7a/cffi-2.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:34e261f78cb6ceaaa36f42f2613f4380d94d9c759a9c73c769ee6e0247364632", size = 217807, upload-time = "2026-08-03T21:19:36.286Z" }, + { url = "https://files.pythonhosted.org/packages/28/f7/dabe6da2466ecbd82dc62e7342dc6b1065dad990c06f00f0ede9ebf2a0ed/cffi-2.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7225e4514edb64eb6740324353e0da0711954fd8d7da4576755b1c6e09b697cd", size = 221252, upload-time = "2026-08-03T21:19:37.416Z" }, + { url = "https://files.pythonhosted.org/packages/ce/87/616202d8e51342c07d2534c510111c4cc37201775ce8f60802c9335d1edd/cffi-2.1.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df913725b79db7bcf03448f36b7bf8815363417d5b58deecf9305e3e30f0f21a", size = 214214, upload-time = "2026-08-03T21:19:38.507Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c6/ab025d75d2c26c19b087c0124e75ee31cb65032f4fe345d356d8c507ab97/cffi-2.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f5cfbc5fe74540d335175b656c725d74d90e3730c626d92575eea35029d9afaa", size = 219408, upload-time = "2026-08-03T21:19:39.809Z" }, + { url = "https://files.pythonhosted.org/packages/db/e2/7e8109f65445bdc673a7b54f02c677de462db75674220fd1335efc8eb598/cffi-2.1.1-cp311-cp311-win32.whl", hash = "sha256:f8ec5e643a9a937f64e1999eb9f75d072263751912dc5cd06d3c85f8f44be7c3", size = 174470, upload-time = "2026-08-03T21:19:41.246Z" }, + { url = "https://files.pythonhosted.org/packages/73/c0/77ba02423c2f7d7091143c45cd49e0e6575c4c1967394bb542bd923a9b74/cffi-2.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:42f6930c31dc7f50732c9ae793c2786c7b6b044195967bbdde40bb9be81c4cc0", size = 185096, upload-time = "2026-08-03T21:19:42.615Z" }, + { url = "https://files.pythonhosted.org/packages/7c/47/9f1f85f9672ceda4984dc6c4f8824e8558992a2972c3d3c81fb8eb28d4ba/cffi-2.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:c7659f22557c5a0bc4855cd635f55edec690cc008a40768527762cb9fb263455", size = 179941, upload-time = "2026-08-03T21:19:43.747Z" }, + { url = "https://files.pythonhosted.org/packages/10/69/43965eccfdead3b9220015fd1320e117be8c6ed01a62ffab76eeb752f5d5/cffi-2.1.1-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:c8c69575568085ba0b1b10c0249d779a214aea6f6522e949a0fc9fb0fcb449d0", size = 184821, upload-time = "2026-08-03T21:19:44.887Z" }, + { url = "https://files.pythonhosted.org/packages/54/7d/16e5a096677b5e313ca80cd5e5170efa3ea44624a82bb111925522da64b1/cffi-2.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f81b3b8f3d4e343550fa4baa0e479bba9f2d29ce9c2e9b51d1ce1718d7442fcf", size = 184719, upload-time = "2026-08-03T21:19:46.129Z" }, + { url = "https://files.pythonhosted.org/packages/56/e6/8941622732edec876dd17d0453dce07317ae96db34f2ec1436c9d3785986/cffi-2.1.1-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:811bd1e21d32de12efca32393a0ab3f5133b54fce9bd44b8bd77ab07da14bf6a", size = 214799, upload-time = "2026-08-03T21:19:47.218Z" }, + { url = "https://files.pythonhosted.org/packages/44/de/f98430906df1545ffde0d543dd124a7a439bc2cd32b36b9c53f805df7333/cffi-2.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:68e62fe11f30d5ca8289242866f0a5291402d8529ca2178ab8afc5c9694ae890", size = 222389, upload-time = "2026-08-03T21:19:48.331Z" }, + { url = "https://files.pythonhosted.org/packages/6a/5b/717f1526b9957b34456313c31645c5b82b8fb5c3fe9e4752999be7128bfc/cffi-2.1.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:4a7c934f7360e8cd64fe9efadcbd10c7c6364f531e432b9a4bf5ccbc9e0e8b50", size = 210249, upload-time = "2026-08-03T21:19:49.543Z" }, + { url = "https://files.pythonhosted.org/packages/64/b3/f8aa4f3e34986c7e4ec45072d1b1b9dd295b6b18007b45518d79726dd725/cffi-2.1.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:3143d81e29e1e20a9ce10901ec369012947876596f75a222235965f2b7ae832e", size = 208775, upload-time = "2026-08-03T21:19:50.918Z" }, + { url = "https://files.pythonhosted.org/packages/b1/db/dceb9dd5b231e1da801793f8acc9f3c52a7e1afe40bb1aae37e02b0faad5/cffi-2.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c1453022f490d2459a11819d83ad1d586e9ff65a12ac3e705ffebd46d3685dcf", size = 221822, upload-time = "2026-08-03T21:19:52.054Z" }, + { url = "https://files.pythonhosted.org/packages/a0/d2/6cd24ae3be000a634109c247d1475d62e5616d0dc78c82770942ec384248/cffi-2.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:208f941bb9d18e768138677f0a6d2ce01f590df56043dda1df1535ac57c88517", size = 225232, upload-time = "2026-08-03T21:19:53.109Z" }, + { url = "https://files.pythonhosted.org/packages/cb/52/3fa190537004dd7f0ab860a6dc7c0175b8667f68d1e618a46f5498d30250/cffi-2.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:210019b6c7cf07f081b4c54635c8cf744377001350e29cc0f81c4377b4797735", size = 223597, upload-time = "2026-08-03T21:19:54.515Z" }, + { url = "https://files.pythonhosted.org/packages/80/fb/0bb75b7039588c074b37ae99f40d9bfddf990ecb2fbc346ebccd2e56b9be/cffi-2.1.1-cp312-cp312-win32.whl", hash = "sha256:046bfc24911b37851ee1b51aab8bffe713d89c68c6a057b09484ce9fd5f69b4e", size = 175292, upload-time = "2026-08-03T21:19:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/d9/79/615cc094e2fb508cade7de88d3b4f6c4ec2bab695c97bce9153dc65aadf5/cffi-2.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f53e442b08449d42821fa4a4fba000095af9f62742a500f978a9f557ec44339a", size = 185919, upload-time = "2026-08-03T21:19:56.89Z" }, + { url = "https://files.pythonhosted.org/packages/70/c6/d0ea84713fe46b243a436a18fcd47d639732747e21635c8a27191b06dc30/cffi-2.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:7bde5e4cc5c10140859842b9d383af292b22639a4dffb725314baf45968cef80", size = 180093, upload-time = "2026-08-03T21:19:58.155Z" }, + { url = "https://files.pythonhosted.org/packages/9d/f4/035513d4117049066b4779dc3b7c0c0fdad175fa13731c9f4003f1cd1478/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:b5bdfd1c873d4e093aabc0ca84c4ca6dbc4f752afb5c86f146d9742580c9da2e", size = 194248, upload-time = "2026-08-03T21:19:59.399Z" }, + { url = "https://files.pythonhosted.org/packages/76/af/2aeb4dbb5fc41a04161ae9ff1518de7cec08e164f44a8ce6a4cf7fd2cd1d/cffi-2.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:31348097ff5bbe827ccc41795d4dd099d9f0625e7def00ee653c137a490c2a6c", size = 196908, upload-time = "2026-08-03T21:20:00.746Z" }, + { url = "https://files.pythonhosted.org/packages/a7/46/2e5fdde8555706dd98139a910ca11be02809f3f605ce956f655d0214e100/cffi-2.1.1-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:9d2055050ea716bd38b7f7f1579c275386646b4894c155a3e2f3cd62ed41b7c6", size = 184805, upload-time = "2026-08-03T21:20:02.02Z" }, + { url = "https://files.pythonhosted.org/packages/55/41/4c7042f317b9217502988f0873af87e16ad606dc20f84e546e3e6ce9764c/cffi-2.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:19ee6127ee34de7d83ce3d371ebc5ed91addbdcc39f9ab15ce4eb35a4e534971", size = 184764, upload-time = "2026-08-03T21:20:03.141Z" }, + { url = "https://files.pythonhosted.org/packages/43/1f/1c3d90d91811c8f86ced9ed637956c54bfe5b79ca98fe976d7f8c8979f6b/cffi-2.1.1-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6a8dddef476fab96d066d578fc88526767b836ab5ab21754e1d5bf3879c31c7c", size = 214722, upload-time = "2026-08-03T21:20:04.377Z" }, + { url = "https://files.pythonhosted.org/packages/37/6f/3b5ce4c3b2192d250f04908f2bfd91ef34552ec8f7716a5d4abdb8d67bb2/cffi-2.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f16c709686a78c727bbbf059f92b0bf41c6fc60deec706d2dc19f529175a6125", size = 222369, upload-time = "2026-08-03T21:20:05.544Z" }, + { url = "https://files.pythonhosted.org/packages/02/10/4b3c75dde3d9663c9e02ba05c2668b954f671d4bbe346413ca8c696b295a/cffi-2.1.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:fcd22650c908d7b7da162bbfaab594a1227a15d1643a98c68b122ac642fa2264", size = 210175, upload-time = "2026-08-03T21:20:06.75Z" }, + { url = "https://files.pythonhosted.org/packages/df/62/14f74b9543e605d17701dc797b815958b8bb70b7624ce1b832ddad48ed6c/cffi-2.1.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aa9511c62d14da7aacc9b4bf51f3f697a621e83b2d6919008243c3aad168eea3", size = 208670, upload-time = "2026-08-03T21:20:08.04Z" }, + { url = "https://files.pythonhosted.org/packages/95/95/86342356ff5953b3fb06f7ef7c5bee212d45e770abc7218d451b9148313c/cffi-2.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a931079504ecc49efed7744c476a5c343a92fabf66dec2db95edb1b2fdc770e2", size = 221824, upload-time = "2026-08-03T21:20:09.274Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ff/7b3429ff53aafe931ed8a5fc69f481bbef7ba6de87ddcbb63d08f483f613/cffi-2.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a2d7755bef5a12ed488f4ef1f1b69ee9191d7396083b755a5d2295f6edb4768b", size = 225148, upload-time = "2026-08-03T21:20:10.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/34/a95870b9221e09cf4f2ce3178b1a210abdfe63a1bd357da940418d7b8d15/cffi-2.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e0bcb7e0f677f543555d2adff3bf19c05f66cdb4796e5ff602442ab2fe3c4ef7", size = 223564, upload-time = "2026-08-03T21:20:12.165Z" }, + { url = "https://files.pythonhosted.org/packages/70/ea/839b50531021a647fb5e929f72cf97bc1ff702b5472166164b5b6e76b851/cffi-2.1.1-cp313-cp313-win32.whl", hash = "sha256:334644fbac4eff73d985a17a91226df55d0f394160c4cfb880e084c8f7161cac", size = 175263, upload-time = "2026-08-03T21:20:13.559Z" }, + { url = "https://files.pythonhosted.org/packages/60/a6/8b149b2c3f2e11aaa1618ef64500b45f50f22c57a977a4dff1aff1f91042/cffi-2.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:1aa5645c30469b09530c4ebca77ebf8f17618293c58f8549cb1a543a50236e7d", size = 185688, upload-time = "2026-08-03T21:20:14.69Z" }, + { url = "https://files.pythonhosted.org/packages/01/9a/11f687cb39d6a3504060d5242f04f48c735afb4d3d533958a20594890cb2/cffi-2.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:63bbfd5ded17c4840ac07cd8f1c21ba9d9708141f840b324f422f41b207e3973", size = 180078, upload-time = "2026-08-03T21:20:15.917Z" }, + { url = "https://files.pythonhosted.org/packages/d3/7b/d6bbf82b8b96e7391438898c42f5bd96dd02030fd5b64937d248220003e2/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:7dbb61fe3a7699468030f71bbe5f8a0e326a151daa91beb11a6fc1f980c55e1c", size = 194064, upload-time = "2026-08-03T21:20:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/94/e6/bcc91b283be94735e268487a054004f0aa19947b6348fa367db53230abc8/cffi-2.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:f24fb43132a4c6b4cb4eb029492919b2db645be6808d738f244fd146c03c32cb", size = 196720, upload-time = "2026-08-03T21:20:18.268Z" }, + { url = "https://files.pythonhosted.org/packages/d9/99/c4b0c17cacdc9c3b8f280026286a9826d6a208c0f047591a3c3ce99b91fd/cffi-2.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d28630f5854ab07ab1fd4aba756de52326c82e6be15d414b12793f1975048b54", size = 184964, upload-time = "2026-08-03T21:20:19.708Z" }, + { url = "https://files.pythonhosted.org/packages/b3/a9/9db617d05d7367c1ad0ab00b3aa6e6f9281edd689b4ee9ea0e5a84e89c97/cffi-2.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:661c298b4821edebead0c91edd2b00374d67ad7c5a1f7a91d4442633b79d6a72", size = 184962, upload-time = "2026-08-03T21:20:20.833Z" }, + { url = "https://files.pythonhosted.org/packages/67/b8/b42132ca113dc567d37684437b46ca1dafc885902b02a110a02d5b511857/cffi-2.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58acb8ab8e295e6c5ea12f888cbb13cf21511ef2a3303a23f4325c29d17fe5c1", size = 222328, upload-time = "2026-08-03T21:20:22.118Z" }, + { url = "https://files.pythonhosted.org/packages/80/10/c5c0cbf0a657aecf59ef511409734230bf556f05a0d6c9eed7aa5c0a0166/cffi-2.1.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:456a61fa52d579ebf9df2e9552ead5129855dbaff6c1e5a9b1bc408809bdc062", size = 209985, upload-time = "2026-08-03T21:20:23.401Z" }, + { url = "https://files.pythonhosted.org/packages/d5/6c/bfa0b87b03b9238148beca990292843c9396ba069b54496596594173de7b/cffi-2.1.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a4f00aa42f75d6e4595e8866e748cc1705adc0cddfeb2ca86d0d03993d63ba03", size = 208530, upload-time = "2026-08-03T21:20:24.628Z" }, + { url = "https://files.pythonhosted.org/packages/e9/02/4e7d553a7ac4b4238b38b3c1b80d486e9d4436f8d2acbf87a0997fe3f402/cffi-2.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b0431303acaea1089ad4b3e9ce4e6518193def1118d4073ca848635ee4ea2e96", size = 221525, upload-time = "2026-08-03T21:20:25.758Z" }, + { url = "https://files.pythonhosted.org/packages/82/1d/a4aaf9babd75acb4d5f223bff71533bee748dd770a382619a798960ee9ba/cffi-2.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:64faea20f4e2613363a1a9b9c7dd73058f3ecd00133a511e72ad7c511658f527", size = 225053, upload-time = "2026-08-03T21:20:26.985Z" }, + { url = "https://files.pythonhosted.org/packages/81/10/5dc0e7bdd18e22107054288283380fc97a06ae3f1656a106908d666a3c88/cffi-2.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5c58fe613dc5e5336357eff555824a314d8e43282600435c8d1cb6a7a2fedd13", size = 223213, upload-time = "2026-08-03T21:20:28.277Z" }, + { url = "https://files.pythonhosted.org/packages/0b/e9/d0061c364cde06ee43168a0d076ac1da512cbc380d44767b844ba34fe2b6/cffi-2.1.1-cp314-cp314-win32.whl", hash = "sha256:1a18a57b58cfb21fc28d72e876acf10eaed67a1ed96226f92af4df681d571c4c", size = 177682, upload-time = "2026-08-03T21:20:44.288Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1c3e01e3ba14c39f6d10bfbac52753b7e22259e38088e5cfe1d704918690/cffi-2.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:3222ba5d678f80a030e6afbcc33dc1ae5cb45facabb61cee2c7016b8432fde48", size = 187949, upload-time = "2026-08-03T21:20:45.623Z" }, + { url = "https://files.pythonhosted.org/packages/87/5b/da4e39efe18eeb89cf580ea9cfc66b6a7c3eadb808fc0cc1d3a295cb5a5d/cffi-2.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:ab36d55f9ed2d067327667c2fea18dda018eb628dd6347aa01dda6cf1f5d3836", size = 182947, upload-time = "2026-08-03T21:20:46.955Z" }, + { url = "https://files.pythonhosted.org/packages/23/59/40338bf421c5accea1d45158170c87006ef1cd371b05c077e76476949728/cffi-2.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7750c6449dff7864bb9bb27ddfb0267756189201a3afc911d82b3caacd70dfc3", size = 188504, upload-time = "2026-08-03T21:20:29.495Z" }, + { url = "https://files.pythonhosted.org/packages/7d/47/5ecf1023850036e674c77ec4de86182d309ae344e39e7cba984b7df5d647/cffi-2.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0beceaabe56af686895136a2de78db54ecd8e4046b236b8fd6d6cb61389e9bf2", size = 188259, upload-time = "2026-08-03T21:20:31.291Z" }, + { url = "https://files.pythonhosted.org/packages/2a/9c/92934c3bea9f785b23eba304538c0b4d37a2a96d2431eb3a1bc87a11aa19/cffi-2.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:49cbc70e6542d4ccccb936558d1064a8012541e78f821f955cff24e357776c94", size = 223864, upload-time = "2026-08-03T21:20:32.571Z" }, + { url = "https://files.pythonhosted.org/packages/4d/45/ba4c93527bc38616a8bd36488acb69a2212d60486794f0c1f318949bbb76/cffi-2.1.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e2d65b31f36619cda3999b78b2aa9632e76b78448e7a56fc4240824200e7c4fc", size = 211538, upload-time = "2026-08-03T21:20:33.808Z" }, + { url = "https://files.pythonhosted.org/packages/80/e9/b6ef565e452acb932fb0cb5443f44a78efbd1233e566f02b5a83855e9115/cffi-2.1.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:28907ab9bfb6aa13184cfc17c6b8e1023c5ab6fd7076d8c20a35e59fe04f8f29", size = 210688, upload-time = "2026-08-03T21:20:34.974Z" }, + { url = "https://files.pythonhosted.org/packages/9a/95/eff5f0cee78d2eabc7eebffec40d3fc1876b5f3c95582e018bb4b99601f2/cffi-2.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:51b31d1c98274844cfd7838ce00bfc27c7423a4dc00fc0772fc3331c2cc90676", size = 223803, upload-time = "2026-08-03T21:20:36.564Z" }, + { url = "https://files.pythonhosted.org/packages/fa/01/579d39fb8bef00a335a23d83757b44feb24cd6345a2c451b64cb67b9c362/cffi-2.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e7cecbaadb83884793e05828cee59b210b24583b9c7425d0ba6a754fe22eb4e", size = 226763, upload-time = "2026-08-03T21:20:37.816Z" }, + { url = "https://files.pythonhosted.org/packages/8d/b0/0b44f47c60b01b57b6e2bbd92343f13a85a1d93bc46ccf6e47e244acd99c/cffi-2.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:25792eac27877609e7bb06d42ff88278a6624fff2ba9bbb523c09616b117e80f", size = 225688, upload-time = "2026-08-03T21:20:38.959Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d2/3b7176cb570a1d3e27faf67b72f591af508036e0d8b2be2ef9af9e8c84bb/cffi-2.1.1-cp314-cp314t-win32.whl", hash = "sha256:8ef53b2de9bcb9197d31854256575d59dbac0cba72ac627bb291ef5eceb74be4", size = 182868, upload-time = "2026-08-03T21:20:40.388Z" }, + { url = "https://files.pythonhosted.org/packages/56/78/31f00c1bcd97c9bbf55f1bfdf5bc809a5de8887473e90bb9960dca825e80/cffi-2.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:616f097f2fe415bc92a247f02e11f634e1f9e9a83d327e3c915c15089c87869e", size = 194104, upload-time = "2026-08-03T21:20:41.725Z" }, + { url = "https://files.pythonhosted.org/packages/7b/1b/58496f2ed0a35de575250c02a43ab3cc2c04d494a88fed31c1cabc0fd176/cffi-2.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ad2c86c495b899d862ea0f4b42891b8713a3bd45dd4105c7fd51c2a72f39f3a5", size = 186402, upload-time = "2026-08-03T21:20:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/c1/8f/9ebe220eab48a093d1a5a5e339ab0dc7316eef3bb04d63c42f0251b61f50/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphoneos.whl", hash = "sha256:dddad92b554513a31f272570678ba307fb9f618f05e3d4a5eacafff9eae03e1d", size = 194043, upload-time = "2026-08-03T21:20:48.179Z" }, + { url = "https://files.pythonhosted.org/packages/ff/69/844bad3ece306c4782c2ecb93597035b6690d48704b803914c199da1e8b3/cffi-2.1.1-cp315-cp315-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:da0e573f9f97159390c89d9f1a9e41908b66d408cc5b58d08cf3847d844c531b", size = 196737, upload-time = "2026-08-03T21:20:49.457Z" }, + { url = "https://files.pythonhosted.org/packages/1b/8a/af668013284634733f02d683458a0728739c7d6ddb5e14cb0c20832266fe/cffi-2.1.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:fb92203a88b3d3053034db775110081c49d28be6551923805e039924093761e4", size = 184933, upload-time = "2026-08-03T21:20:50.639Z" }, + { url = "https://files.pythonhosted.org/packages/0c/75/2f5207ff6d1a613133b23a5203cc0c2a628313b5eb3974d7956ae3c57950/cffi-2.1.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:2ae64be792b8966f2c69538199728b290e34726562896df1e5dc8ffd8d8188e8", size = 185002, upload-time = "2026-08-03T21:20:52.173Z" }, + { url = "https://files.pythonhosted.org/packages/e2/31/9e1313b0a6e30e91b3b3d3fff51ae99c857c07738e3afcce1f7334e1b7ab/cffi-2.1.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:507a24c282e0f42f8ed737cf048572cbf580468da5555764a8331735e9c736b6", size = 222271, upload-time = "2026-08-03T21:20:53.462Z" }, + { url = "https://files.pythonhosted.org/packages/50/e3/f6234a833e6e08c7007003074723c406559eecf9b48dfc97471e5a8eb7a0/cffi-2.1.1-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:246fa40ce8645a614ff682e0b70f37134e460eaf93a775e0cbe3cca585a67a80", size = 209919, upload-time = "2026-08-03T21:20:54.783Z" }, + { url = "https://files.pythonhosted.org/packages/0d/fc/5f74e293fced6edb51af3a46c4ccf6c23c9943774ecb375ddbd522c76add/cffi-2.1.1-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:471cee653ae88de62096552e6d24ccb4a5adb8c8c9f10b5054d0122c15bf2779", size = 208529, upload-time = "2026-08-03T21:20:56.066Z" }, + { url = "https://files.pythonhosted.org/packages/44/16/29e6d01b388bef055ecd6ca8244b3f4d336bd09e92d5d892187b9601084e/cffi-2.1.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aeae0e330c9f6acd681f647d46cefd30c29f93e3392882e792e82080c9691399", size = 221630, upload-time = "2026-08-03T21:20:57.336Z" }, + { url = "https://files.pythonhosted.org/packages/a4/18/fa7f1f6857d5eb88a4ca99ffcbfb7c387a287ccc154c64a73e86314745d7/cffi-2.1.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:42a494cee34437f05546455144f2b5d9ac09b1face62bcfce597d2e521066688", size = 225134, upload-time = "2026-08-03T21:20:58.675Z" }, + { url = "https://files.pythonhosted.org/packages/e0/9f/e8e3dfa04a1b4c241f8c91faacad872b4d4efd051d49764ad4e2fd4b9fea/cffi-2.1.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:cc572dace3f60ef98d7b12ff411d20f5362feb31a0439eab0085bbfd349982d7", size = 223197, upload-time = "2026-08-03T21:20:59.968Z" }, + { url = "https://files.pythonhosted.org/packages/f8/7e/8debeb04f1ab9fe2a6963964cd6f1aaf7192627b83926586a6a4e089c9fa/cffi-2.1.1-cp315-cp315-win32.whl", hash = "sha256:4f42141fc14250de6dde5ee7ea4432be017252d91f19c5ad043c084cea629cac", size = 177683, upload-time = "2026-08-03T21:21:14.901Z" }, + { url = "https://files.pythonhosted.org/packages/e0/31/5158704cc474ab65c1647932e88be78dc0873f47130e253be38bcaf13d01/cffi-2.1.1-cp315-cp315-win_amd64.whl", hash = "sha256:e6e8cff14d6fb0be70a09c0bdc58096f501952d04624ebf867e0e56da2df8960", size = 187897, upload-time = "2026-08-03T21:21:16.108Z" }, + { url = "https://files.pythonhosted.org/packages/cc/4b/b3a2da8570c704ffc0f9762cdc3ec0f02c8573798e0b5cf7f11c82bbb70f/cffi-2.1.1-cp315-cp315-win_arm64.whl", hash = "sha256:27350daa11d4f10c540e6e89dada4c54feb7256ad03e9a4dc075ebad7ba360d1", size = 182935, upload-time = "2026-08-03T21:21:17.271Z" }, + { url = "https://files.pythonhosted.org/packages/d0/ef/5443574510a1207e6f6bc38ba6e1f1de36cb48fef07b2728bb896a21f430/cffi-2.1.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:c26608d2222fb1e94487e4a387d85f13eb55d5ed725cb25a0c589ac4ee60e7bc", size = 188464, upload-time = "2026-08-03T21:21:01.163Z" }, + { url = "https://files.pythonhosted.org/packages/7e/ae/a56fa8c4686ad50e148fcbc8d3ae0d03915ff5c30d795058988c24118cef/cffi-2.1.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:4be96343e422f2dfcd12ab5c9f5aebe03f82f737c6bffeca6830b3875cb44aab", size = 188262, upload-time = "2026-08-03T21:21:02.382Z" }, + { url = "https://files.pythonhosted.org/packages/53/b2/6187f46f2912276a3ae284076109cc5c8680482f11f766ccf26db4a86427/cffi-2.1.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:937c0052c05a31ca1daf18de3158eed4dbfcb9cc107adbea227728d647be701e", size = 223779, upload-time = "2026-08-03T21:21:03.553Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f6/c3ad28bd19f77047a03084424fbd4cbe997303267c14423737324be0385d/cffi-2.1.1-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:df423d40ee8654634421812bc3b196da3f9bd7d32929da813f8394c4348a5358", size = 211520, upload-time = "2026-08-03T21:21:04.863Z" }, + { url = "https://files.pythonhosted.org/packages/a0/cd/ccac9013a5bd9fd764de118674ab9c805b5ca10c19270d90ee273f8b2240/cffi-2.1.1-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a730a083190634c65cca36ba5f489531576ebd79bcd5c8e172130f6453127231", size = 210673, upload-time = "2026-08-03T21:21:06.223Z" }, + { url = "https://files.pythonhosted.org/packages/52/86/2976131c639aead931c5bee5aba67e4b09fbeb8018b6f282f70803f923a7/cffi-2.1.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:363e05fa78e15116c3c32c210ee36884fd6b9afa6d440e47112c3bd511d64cb6", size = 223835, upload-time = "2026-08-03T21:21:07.539Z" }, + { url = "https://files.pythonhosted.org/packages/ac/0c/33a7aeab2f9c76918c52e084beb39c570db3588133412929e8ec06fab90b/cffi-2.1.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:770de9db11e84213beec501cfcaa013b019820ca881e03344dea5844f7876d94", size = 226705, upload-time = "2026-08-03T21:21:08.774Z" }, + { url = "https://files.pythonhosted.org/packages/e3/26/2cde30fdde421130bfc18f70395731a6e6b2053c6a1978a5258ff04e72fa/cffi-2.1.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:7da0c5eff80f0197f3b3d1232ec5a682a9325f4ae9016a78f5f5ca35f9ced1f5", size = 225539, upload-time = "2026-08-03T21:21:09.911Z" }, + { url = "https://files.pythonhosted.org/packages/6d/cd/a361394c94b2129d604bb846f624a8e88255a3ee33129c434a00d715e64f/cffi-2.1.1-cp315-cp315t-win32.whl", hash = "sha256:06c72bb76605a4b0cd0aad6930b69d4baf7dd5d806cfc409b824191099700e66", size = 182707, upload-time = "2026-08-03T21:21:11.226Z" }, + { url = "https://files.pythonhosted.org/packages/9b/b5/ba2b299993c26577d529b6ae29841f9e15b9fcf004d65f423f4fcf94ade9/cffi-2.1.1-cp315-cp315t-win_amd64.whl", hash = "sha256:d9c275eaacd24aa73f94ffd6de08fc3f932424d8b6c376f4bed7cde376fe7bc3", size = 193772, upload-time = "2026-08-03T21:21:12.39Z" }, + { url = "https://files.pythonhosted.org/packages/aa/29/35e016098c814cd93de9cd320c66b5bfba14dc6ecedd3cb518fa7c408c69/cffi-2.1.1-cp315-cp315t-win_arm64.whl", hash = "sha256:d18e5ac0f2f03f4f518d3e23db0f0cad7faa1da8620e9c09461d443bbf6e6692", size = 186360, upload-time = "2026-08-03T21:21:13.636Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/31/4971872b3ed8715346231fb6eb4da8fcba65a4143c189db151ee28a2812b/charset_normalizer-3.5.0.tar.gz", hash = "sha256:49bd5feb59b0bf3cbf6ebcf4352e371c95b9da9bacd4449f8b64d0ad2c10a26e", size = 169295, upload-time = "2026-08-12T14:35:31.624Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ca/8e/1e9a03657c2f12960c67b05e0550059787d4c8fc3fae822672f4fe7f40c5/charset_normalizer-3.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2478bd3b2ead3962a484fb802891be40d10049fb74f83e09cb4463fad023fea", size = 349795, upload-time = "2026-08-12T14:31:47.546Z" }, + { url = "https://files.pythonhosted.org/packages/76/e5/a42c8884ec0321c56124a5b43c135efe75260dad4f70ea0ffba4dcac85ec/charset_normalizer-3.5.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7cdded069549b5eae3d5d9bb6c2e5bb4fe83f9b81863e2a193cd747bf197aebb", size = 250677, upload-time = "2026-08-12T14:31:48.918Z" }, + { url = "https://files.pythonhosted.org/packages/5d/21/d94021b005d2e660fd13634eecfe4ad378b0d0bbb45134809aed42b94a99/charset_normalizer-3.5.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aff38231e3171c578b2c449a01afa44e9ff40844597a32873da102394f63d28e", size = 239868, upload-time = "2026-08-12T14:31:50.107Z" }, + { url = "https://files.pythonhosted.org/packages/47/d0/8b9b07e41501bfcdfe380b826481033ca2eb5fa4b5507eb64dbc4cba9885/charset_normalizer-3.5.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4346a693c08b1d0cfc0e3325bfb0ecd4322fb1a6904d68cf416f8da5e981b234", size = 280688, upload-time = "2026-08-12T14:31:51.182Z" }, + { url = "https://files.pythonhosted.org/packages/a8/29/b83b3469e596adff778bf3c8ca8f705822c1c3c0ada8477a0d00113bfa59/charset_normalizer-3.5.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b787efadba00f5da6fe89513bfbe3852d52ca3a448fdec165765cb3b44a80248", size = 277133, upload-time = "2026-08-12T14:31:52.362Z" }, + { url = "https://files.pythonhosted.org/packages/0c/aa/a080c4facaab4ece837558649eb6e2c760867d1c0ea52220cbbc4e89dcaa/charset_normalizer-3.5.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:143792a43e06dc3b27fc891948406e251502dc19ff9216cd80182b79131be5c5", size = 261492, upload-time = "2026-08-12T14:31:53.628Z" }, + { url = "https://files.pythonhosted.org/packages/f9/24/e23bcc9d4bc897bd620bc39c6b6052574f5ef93b85b50b175dd427a7dc07/charset_normalizer-3.5.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d74bcf1cdd8ac8267fb216473ce6b112efa07b163536288094541415084d131c", size = 259739, upload-time = "2026-08-12T14:31:54.86Z" }, + { url = "https://files.pythonhosted.org/packages/ef/85/f4c391c807f67c6483d02c02d28683e2fe229403728369c0cd33f61e4292/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3bfbe543d957213fc9a3db4979a8e171b7aa7504c1d737029defdb03a6095a38", size = 252120, upload-time = "2026-08-12T14:31:56.227Z" }, + { url = "https://files.pythonhosted.org/packages/4e/cb/dcf5aa14e24270613fafa2f02b3979820c244610410e5508696ca96bd0d2/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:3587d94b5c9f05c2dc4c3f3d47aba6375ff141a21adae3051d8d4d53e8a937c0", size = 241622, upload-time = "2026-08-12T14:31:57.54Z" }, + { url = "https://files.pythonhosted.org/packages/83/a9/decd1c1cc01ccbfce66845096e3945397e4ede354f31cc5cb28fc6ca601b/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:a7cb4cd266bd85613367fb85a30cfbf6fe6349919e87e18ca8dba584951bfb8a", size = 280745, upload-time = "2026-08-12T14:31:58.91Z" }, + { url = "https://files.pythonhosted.org/packages/8b/47/420ddefc04f089e4497b3bcbadea37586477713300c13204fb4a7c736071/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:ffdd7ac514301d0a67f7c23b9f2b431ef909a3c3dd6c3766668d0a6f5900c94e", size = 258669, upload-time = "2026-08-12T14:32:00.069Z" }, + { url = "https://files.pythonhosted.org/packages/58/73/01d5173097a5f0a13417e77db9ba3aea24de54421344c8e0c4973bf8cf21/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:3684ebbdffd51329ac44245d1d227d90b965797aa1a8abd026568a1f6ae88811", size = 277344, upload-time = "2026-08-12T14:32:01.211Z" }, + { url = "https://files.pythonhosted.org/packages/41/4a/300ef2598da520744aca7c49370e48732cab1f04de5f784f0e4f0e9e1d11/charset_normalizer-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8b8788f114845c01f2b520e0b91ea58d143276cfc0483aa943e815f7b9555c15", size = 263440, upload-time = "2026-08-12T14:32:02.421Z" }, + { url = "https://files.pythonhosted.org/packages/be/5e/78dd366df9ae4810039c0f799ec47d325a5cb76e92bb707ca9039b7c2306/charset_normalizer-3.5.0-cp310-cp310-win32.whl", hash = "sha256:9a1d9b13e5e394e13e3c316f0d910d100b17681ff59797f30da1dba032061296", size = 181680, upload-time = "2026-08-12T14:32:03.932Z" }, + { url = "https://files.pythonhosted.org/packages/67/d6/455982837da317ce47cafe35260f446f218f5843090b77a59ee69aa0ad94/charset_normalizer-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a54587f93f2e289f8faf25b35c997d4cc75cf677485ac6f50c985715989f99c", size = 205665, upload-time = "2026-08-12T14:32:05.045Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e5/5b285d50d4fc82111c18ea4fff8b5e1916a257b98dcd5cd0abf1c165473b/charset_normalizer-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:38a395079f229a631dece74e24c69c1f612536dd51f345a7d6a98abe2d3e047a", size = 184409, upload-time = "2026-08-12T14:32:06.503Z" }, + { url = "https://files.pythonhosted.org/packages/50/42/71e4e3bfe59202feef062c68487f54c6adf501cfbe087ecd93e3cd597fea/charset_normalizer-3.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e46a37ea7fcf9ae01d71b2e5ece19f1565987f3e308394b829197cbefc061f92", size = 349110, upload-time = "2026-08-12T14:32:07.832Z" }, + { url = "https://files.pythonhosted.org/packages/0f/dd/fd3386d0fbd358d3b5c7a2fa5bf312afe6159b04fafeb67d39fa971d7448/charset_normalizer-3.5.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1cdfed4d7a59333c8220c67dd3be4e7a6c887b67453a64394022dcc919570add", size = 250773, upload-time = "2026-08-12T14:32:09.113Z" }, + { url = "https://files.pythonhosted.org/packages/17/ad/4901a66d6d3b17f1096725d7e50266132c16555aa6a70047fe1cf262b4b2/charset_normalizer-3.5.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9491f594859b68052edebd69e05fb045055a713b57a67974e6c1553b4e503c39", size = 240229, upload-time = "2026-08-12T14:32:10.43Z" }, + { url = "https://files.pythonhosted.org/packages/43/b7/1d790e0425e0f4c99e9b89a3956a94a6d2d0c6f01b2a5eca93d8f082d5ac/charset_normalizer-3.5.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:420b19411959eec115063229536788e6b32d0a7fa907d6b940317919120d702d", size = 280757, upload-time = "2026-08-12T14:32:11.628Z" }, + { url = "https://files.pythonhosted.org/packages/44/bb/4b8c8086c67636e52d6354ad17697f54a00b40041ca53dc765737e21709b/charset_normalizer-3.5.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a565303d118ea3b94a4b6c076bf568069726be414e43b06d58f7070b076ce11d", size = 276174, upload-time = "2026-08-12T14:32:12.808Z" }, + { url = "https://files.pythonhosted.org/packages/25/fa/690a11924c40c766d258d2b74d817cc5efe7bbcfceeedc5c0f35256d7524/charset_normalizer-3.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:815f143a91983ba3041bba066e492ae3c42de523fb1c699685a1abf3313b7d1b", size = 261808, upload-time = "2026-08-12T14:32:14.138Z" }, + { url = "https://files.pythonhosted.org/packages/53/2d/4a6945eff0c8f684e3f5b7b978644ab18ba9198da060dbaa1d9206bc6cc9/charset_normalizer-3.5.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c5e981a5ac8641381efe6f0029467500661616a530d27bc6eedfe45f840599f8", size = 259922, upload-time = "2026-08-12T14:32:15.485Z" }, + { url = "https://files.pythonhosted.org/packages/60/4d/10ac7e07bbf7ea569effeb9524e32f345f7e643800b20d538fb4706eab4e/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1a573e1e428f93908e79e04b349717f400e720f2f82285f0aaaf3ee0ff7f4c79", size = 252315, upload-time = "2026-08-12T14:32:16.764Z" }, + { url = "https://files.pythonhosted.org/packages/13/bf/1ddacf7aa7da12097f229a6a4e71a70200937a621b3617f6dc819fb99a66/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:32e6d56dd825205f81e5c45bcebb4df6a11fb2bbf4969a01ef156d6ced90c224", size = 240600, upload-time = "2026-08-12T14:32:17.956Z" }, + { url = "https://files.pythonhosted.org/packages/c2/88/293444d48c8d86fe51552262117134a9fc666d68cbbbc9b8c1b2b35a29be/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:30ae26a1adcd943690dcbbc47f28be762bae9e08ad7442b78c86b1c0dd5a626c", size = 280788, upload-time = "2026-08-12T14:32:19.1Z" }, + { url = "https://files.pythonhosted.org/packages/12/ed/0e34e40584f51eda38d4a5daf25fd8586366347efd4b2470dbf64710e778/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:5f51a19dc52197a20218b05ec5336d0c6b3b09935f838724722032c8d45dc91a", size = 258425, upload-time = "2026-08-12T14:32:20.207Z" }, + { url = "https://files.pythonhosted.org/packages/72/b2/c1b1c27f6f0ef35b21a8bdb592854bbf7f219629db3477f74c0b1380e0ed/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6e44bc2780516b3df986d6fe33103c7080cd9dcd5576fe3cb4b0f64309c8f22b", size = 277437, upload-time = "2026-08-12T14:32:21.345Z" }, + { url = "https://files.pythonhosted.org/packages/b2/bd/a1b7d959a37847675adb2f5978f81703306dd78df4fefb82ee5a1cc5e37f/charset_normalizer-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7faa47b56070b3dd6f4898ed28528843ab130d53266cb9948d9b1f3bb1a5c5e8", size = 262947, upload-time = "2026-08-12T14:32:22.613Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fd/a76a2d7e639bfc6a9c869371e34f28231eaca7d7126ec19895aa38eedb51/charset_normalizer-3.5.0-cp311-cp311-win32.whl", hash = "sha256:830c04a49998b5ed58c8b642c65b7b26419397f52392a64121ba9fd0e95e7f9f", size = 181313, upload-time = "2026-08-12T14:32:23.736Z" }, + { url = "https://files.pythonhosted.org/packages/97/84/6fc03e802578df41a2ab9b6a1f26657fb92e32285a603ad5852a4a4f68c1/charset_normalizer-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:8cb9b6892b53bd6d11fa4cde3dbee020b1f0b6656be1fbaa1ec0d4324a7839db", size = 206197, upload-time = "2026-08-12T14:32:24.938Z" }, + { url = "https://files.pythonhosted.org/packages/0a/27/7208360ff1901607359869fcc45ca0989d597f3e30777ba30c7254170587/charset_normalizer-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:2403b489c103e9a18c835863fc6dd54361355c8291d4cafdb37492b683440b9b", size = 184925, upload-time = "2026-08-12T14:32:26.123Z" }, + { url = "https://files.pythonhosted.org/packages/6d/3c/045ea64ea5a550870dd8ab60b2242870328d53f17d2be593b4f9f3121474/charset_normalizer-3.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:98820e1ceb25c6df7a80c4fd8efa59cb121f99bc7c4c1693ad94a2caff5b311d", size = 343861, upload-time = "2026-08-12T14:32:27.254Z" }, + { url = "https://files.pythonhosted.org/packages/fc/1b/7502be709db899d5b4801509829188b3a5a10969411da9c846115a5f1b70/charset_normalizer-3.5.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:608553f476fca509537e804c4a71f5eb166ce63b75141f89c2c686ce1aa36956", size = 237550, upload-time = "2026-08-12T14:32:28.385Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ce/66392661375148d9455c17bee25509a54e28c39969e34befa48ec8777936/charset_normalizer-3.5.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6753de11eef42f1c321b26d682957d92c7f7bbce6530f34bbe0f9291dd37cc6f", size = 229673, upload-time = "2026-08-12T14:32:29.668Z" }, + { url = "https://files.pythonhosted.org/packages/6a/64/f58c32a8d4ecf55b82ee61ee9aa6a664d4afcd36c72feb4c926fd6fe9af8/charset_normalizer-3.5.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f76dc0a47f94cb9b69d86f01e477f4b0371ca70208b9ccea7e063c41eed9046", size = 260768, upload-time = "2026-08-12T14:32:30.891Z" }, + { url = "https://files.pythonhosted.org/packages/b1/ea/36c90e59a96386174377e855479ec154221ef001e96637e0b23be92489c4/charset_normalizer-3.5.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c387c6bf91b4774e359a48a179e2872b8e8bf741e4fde06ba8d1665eb9a4760a", size = 257880, upload-time = "2026-08-12T14:32:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/23/35/5b85772eb82528ef22ba29487ad544a7049dfd27f35b1a5a55dbc0843048/charset_normalizer-3.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:14f6904a3cf870abf044df3a8c4924ac6c8ef77e9896586fd37e73ae96cff2af", size = 247547, upload-time = "2026-08-12T14:32:33.495Z" }, + { url = "https://files.pythonhosted.org/packages/b6/14/ba11a99c2a22ab04c2d5383a700b378cb463a78ab15f36444cabc10cd671/charset_normalizer-3.5.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0cce46dd29d73e135e8087b96eb62a4aca6d69391b7f97808c6588ebed3178f3", size = 243326, upload-time = "2026-08-12T14:32:34.794Z" }, + { url = "https://files.pythonhosted.org/packages/4c/4a/cadba3f2400b45aa1d62a4ae0298bf58a3b30b1158baf15c338c7ce5b601/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b476cdb63df22da2b91837593380be3ddbe406f36c506c1c91d80e7196b66288", size = 238820, upload-time = "2026-08-12T14:32:35.984Z" }, + { url = "https://files.pythonhosted.org/packages/47/41/d5188b9342d75b72c2b05d3ee373f01a691397e770f001ee05e3b37925f5/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1f56ce84b317ef2a59d7d3461891c7597c79247d2192bb8114c68a1a1debfcc0", size = 231661, upload-time = "2026-08-12T14:32:37.191Z" }, + { url = "https://files.pythonhosted.org/packages/13/5f/df38fa972c4e945c3d8cee2bc4e610613af522fd359c7dc7a74c419f0278/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:9ce0f885239357379d92fd9a5fddbe20f0e30e0527c29ba69f8e99eeb1304a76", size = 261459, upload-time = "2026-08-12T14:32:38.369Z" }, + { url = "https://files.pythonhosted.org/packages/a5/d7/043ff7720067a3beee05523465ac9c1c846c68b7884930dd483f72ee5ab6/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:96ae7ab5d8155fde927aa0864fbc8ba3cc4fde6d41ab0c7cea9d6012b4978603", size = 242300, upload-time = "2026-08-12T14:32:39.505Z" }, + { url = "https://files.pythonhosted.org/packages/19/f6/33980b7b802a048e546a6d9ad2ea783a6cf6b10a86aaccd10db462d8b913/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bf91921009025e96ce57a03ced6d14604fc3baf0530351638e9504a55da6fa3b", size = 259101, upload-time = "2026-08-12T14:32:41.02Z" }, + { url = "https://files.pythonhosted.org/packages/a6/b7/0d19bde844bff9165377c1da9ef3c4792a4c24bd49b5b7094d9e6f6ab58b/charset_normalizer-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0b2e44e6d42d1a4ff78ccc219a93c5449105d10b16198d1aea581080df8073f9", size = 249246, upload-time = "2026-08-12T14:32:42.47Z" }, + { url = "https://files.pythonhosted.org/packages/1c/cc/2c34fdfaacdf0e96e880ef562cbf80a9b5f8ea97e0dd9e57ba348a9c65cf/charset_normalizer-3.5.0-cp312-cp312-win32.whl", hash = "sha256:deb99535e9bf0bea8e274c6413eb939a21be35a3f492678dba4d5b1f4d70f142", size = 178025, upload-time = "2026-08-12T14:32:43.909Z" }, + { url = "https://files.pythonhosted.org/packages/76/d0/c34dbd1df23bcbdc1b5d2f48256340d72fa747f1eb03924a9d2fa35ed85b/charset_normalizer-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:e54dd1a66fa4bce0ccaf0db9dde336e49b3eec646dc4c1c0991279369d373a14", size = 200143, upload-time = "2026-08-12T14:32:45.254Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ea/4e6bf1465d60c3d8f488d5bde140d0cb91cd23ccab0dc2895cc6c6982047/charset_normalizer-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:b8ea208b304587d47931b36481342d20336e0d338ab052f8b4305926482598d6", size = 180046, upload-time = "2026-08-12T14:32:46.545Z" }, + { url = "https://files.pythonhosted.org/packages/1d/be/cc7b7b6fc41984902c0d31b06f5d9297e67705c1dae9352608e5540fad09/charset_normalizer-3.5.0-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:5c23fa4f6eccdd601949cb00f3988c01d64e671d8faba356397971077022e144", size = 211050, upload-time = "2026-08-12T14:32:47.81Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ae/e3ec8f17313609f43f7b323012fdb1ee37b83432277ca4eceba83e00366c/charset_normalizer-3.5.0-cp313-cp313-android_24_x86_64.whl", hash = "sha256:07f6f42b5a6325df35b458004fb5f9f29bf502d89287a33c7cdef3590e31de0f", size = 222768, upload-time = "2026-08-12T14:32:49.027Z" }, + { url = "https://files.pythonhosted.org/packages/c5/50/9f9c0d7ccc1512d49e27a0e7c12c58ec71dfe91698fa4326f058c33e1f1b/charset_normalizer-3.5.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:8efc3f1563ed431882dd0dc0411b5f8ace1b1b89074981deaf6bd8af77dbe1bc", size = 193907, upload-time = "2026-08-12T14:32:50.414Z" }, + { url = "https://files.pythonhosted.org/packages/fd/1d/cfe7b745ef7f4c3b7214581955b5a0869ba2ac551a58fc11036281ae167c/charset_normalizer-3.5.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:368eb2fc9482158b3a3386e8f01fa61f479c968e9a19ceab8f0188b86b312991", size = 197135, upload-time = "2026-08-12T14:32:51.605Z" }, + { url = "https://files.pythonhosted.org/packages/28/55/30fafdcfca9ba616bc394240545e4cd52f4f66dea43ded81b7d2d5274fde/charset_normalizer-3.5.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:826a295a039178479a325be1ae60eded1f0b10f7dda749df59e2440de8f61d64", size = 339892, upload-time = "2026-08-12T14:32:52.82Z" }, + { url = "https://files.pythonhosted.org/packages/f4/08/bdca5fc2bdc36ee443673dc7d12b23885a5a7b282bef85a1a4c3b325b40e/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70ff1c16eb0eb5ee6bb12739292347f981a5ba764cc4df1bc2e69b0405d4ac3b", size = 239439, upload-time = "2026-08-12T14:32:54.058Z" }, + { url = "https://files.pythonhosted.org/packages/d1/9e/506c8d7a7722bba7c8cdd78c1b5ef23bda92bfbe0b3e28ea84673d519a0f/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3cfdab178a4add5483e26a9bb1c16d8018ccf39b4be7a3aea6c3979e6828f2ee", size = 227896, upload-time = "2026-08-12T14:32:55.326Z" }, + { url = "https://files.pythonhosted.org/packages/0d/1a/dd828f2b1d6f4bf10821b9a74d866be05ffcdbfcddfc501d6fe6428762a7/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6083d10a846218502d664375b9448508d9fa580bd834567423156c6abfbe899d", size = 262548, upload-time = "2026-08-12T14:32:56.484Z" }, + { url = "https://files.pythonhosted.org/packages/be/81/196d26f6bd78b93e0d451b69082a71027ceeddd4b0be9170b81bb038f824/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0f211c21aa316cb6e2662e54a1194633a79d98a50a876addacfce7ba5b34b09f", size = 259986, upload-time = "2026-08-12T14:32:57.661Z" }, + { url = "https://files.pythonhosted.org/packages/3a/6a/5b964a1eb0f9075ecd45083eeb21aaec215334f98bac3d400302ea73875d/charset_normalizer-3.5.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b08ebf9488c7ff5eff038e48e6ea938178dfd9dcc8598b5ca941e4ae27b20be", size = 249853, upload-time = "2026-08-12T14:32:59.123Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b4/aee3a9d82edd0e931091ef3e9f03e46491ae3590e96e998d0975dadbe17c/charset_normalizer-3.5.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:6c95450fce59f00c6d08eff6572ec2e736e5054c9450253afd5748f8416f2eb9", size = 244217, upload-time = "2026-08-12T14:33:00.341Z" }, + { url = "https://files.pythonhosted.org/packages/22/3e/33f72ca11c1b619b220fd9f35905ebd171cbd0e0470f2357e467b9e861ee/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5780a29823e1d2bec69b7a104ead4195a43f3e97782efaedbf1f79a0157af715", size = 241307, upload-time = "2026-08-12T14:33:01.589Z" }, + { url = "https://files.pythonhosted.org/packages/78/27/6029dccba958621c7f3a65136f87c5512d712aef9e890f09512cc171bd03/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:054420b5db984971d886e5e4e2c37c760ae6682aedbd066687ff0949d9ed5f08", size = 233305, upload-time = "2026-08-12T14:33:02.866Z" }, + { url = "https://files.pythonhosted.org/packages/18/d7/691c967be459153fe9faf49bf78bc95639ef8bf6dd008f38cc6389a349eb/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d016dc857136c726958102c3b8a3986acdc65ace6fbf12cfdc09cc4bfa2935b2", size = 263465, upload-time = "2026-08-12T14:33:04.166Z" }, + { url = "https://files.pythonhosted.org/packages/7c/2d/9202221be5c90b2a835924191e362690ed8dc8c7d6606100c2bd03fe0f8c/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f2ce3d39fb4a9d674e6639dd5d3146b2e273475d2260f10163228d66fc04433d", size = 245060, upload-time = "2026-08-12T14:33:05.325Z" }, + { url = "https://files.pythonhosted.org/packages/b3/9a/298772fd0a0cbccadf36451a1cd7eef4b66a11e99b4a7f6fafc47cc62c75/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:a5613a3a82c974227bde18f03409e30c467f8065cb56d822e3eb83708a5f223d", size = 261091, upload-time = "2026-08-12T14:33:06.475Z" }, + { url = "https://files.pythonhosted.org/packages/82/3b/1a11fe66e555dbe2f5714ade6ba74fa29edc9155d9cf1001d4d6ed096aa7/charset_normalizer-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fded2e82ff082e5d8e017e2ddcc1411bd8cb83b8585097fc401ef574f756b888", size = 251857, upload-time = "2026-08-12T14:33:07.784Z" }, + { url = "https://files.pythonhosted.org/packages/17/fc/73b817e8af3f1d25ec5cf458d405abba5a144cf9812238a61530f5eac186/charset_normalizer-3.5.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:8f006866047c6ec4b627ec144b1e0bbc7427cb31fd7c08d19897d0ac9032af3d", size = 139745, upload-time = "2026-08-12T14:33:09.123Z" }, + { url = "https://files.pythonhosted.org/packages/b3/fb/ddb66303c86f7dc5043a457dad9fa82b4d6d0cb97094f9bcdde21693fd58/charset_normalizer-3.5.0-cp313-cp313-win32.whl", hash = "sha256:196e270c4e80827b5072eed7d6aa661d133afada94fe366669f9609e718d305e", size = 177217, upload-time = "2026-08-12T14:33:10.305Z" }, + { url = "https://files.pythonhosted.org/packages/fb/88/6018cc8d76ea2b7cb02918f37e23e86c261d1a102713d7e88d2cfb8b211c/charset_normalizer-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:72982d9958a42f8132bf2d6b90214ed66477295ef1188731f98ae3511c6eeb5a", size = 198896, upload-time = "2026-08-12T14:33:11.51Z" }, + { url = "https://files.pythonhosted.org/packages/20/2e/04c0bbfc8d9abf91959f7a3d207d45cbf63a8116984caae2381890019bb5/charset_normalizer-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:0b373bab0b867b68b8eb249da9478cab9181a42993437cd2f5dba5fb0b4fbd1b", size = 179193, upload-time = "2026-08-12T14:33:12.726Z" }, + { url = "https://files.pythonhosted.org/packages/43/14/d098868dac5ff27e0258f548b1c74c6484be528384965d8fcf8fc6a4011d/charset_normalizer-3.5.0-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:d95244906ed69d0f79f190893c65e336c15959003e21449256dc05c001b52ea2", size = 211664, upload-time = "2026-08-12T14:33:14.153Z" }, + { url = "https://files.pythonhosted.org/packages/e7/da/a944b32a46601ae5a4c3499e8d64ecd14fe82313f00da74dcdf00273a0b4/charset_normalizer-3.5.0-cp314-cp314-android_24_x86_64.whl", hash = "sha256:d788e2ded0c4c47efa4d73cfe59eaf975ee32f425219873d2cb3e3fbaa00f636", size = 224375, upload-time = "2026-08-12T14:33:15.472Z" }, + { url = "https://files.pythonhosted.org/packages/f7/db/eabb5996be2f529744755e7b2fc9396eff4a64961f034e7fd49d54b9afb2/charset_normalizer-3.5.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:f9f91d3e8382900f3a68fa0ce94294479de9cd2de6bc0c70acd0f0dfd511836b", size = 194364, upload-time = "2026-08-12T14:33:16.607Z" }, + { url = "https://files.pythonhosted.org/packages/78/65/4ad3c5be108930310d8003f5602861d5b89f728293b9f09c3a4837f7ba10/charset_normalizer-3.5.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d54625cbf4e6b60bf0639728cb8b4cb541e340f6d7cafae5806051a40ddf4c45", size = 197643, upload-time = "2026-08-12T14:33:17.88Z" }, + { url = "https://files.pythonhosted.org/packages/3d/39/8fee3201b98d52289be60a775797d69be05a04fb6cfb48c1587dad33e649/charset_normalizer-3.5.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:1f99a8c3a1da5d955edbad18208b3d627bdd54c48a6e739fa877bdca98c686d6", size = 341384, upload-time = "2026-08-12T14:33:19.239Z" }, + { url = "https://files.pythonhosted.org/packages/a7/dd/9e757101d1f76c35c0643684ba499ac3a181fb2b264c68174bf727d627e8/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bf1e75dc07a3850b53d1e5f75e04d3ae12afe56284be7821771eaa2466350c73", size = 241637, upload-time = "2026-08-12T14:33:20.619Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e4/7857023015400bc4aa0a82fbcca29fa2dc7ec25f971a130764cb2dc7a589/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ac5a9cc079c67d75f4ddf343276031879eadbb333d1bb231cce297b8d7b9aae8", size = 226170, upload-time = "2026-08-12T14:33:21.773Z" }, + { url = "https://files.pythonhosted.org/packages/7d/ae/8b52935b304f7b6bbf33151ed2b75266b09aa4b6f8f04230d948885b2577/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0dfe83c1b4d00abbf433998117a14f56a5c2bc68226c0d331709eed0d1ce539b", size = 265093, upload-time = "2026-08-12T14:33:22.999Z" }, + { url = "https://files.pythonhosted.org/packages/dc/78/6e838f6bb059f2c0afc60a4e7f294252f043c254656ad4114c50302cae4d/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e4e8fa586df2208ef040684751345f10f503834a757c9a74ecd19c1a2f9b1ccd", size = 262789, upload-time = "2026-08-12T14:33:24.214Z" }, + { url = "https://files.pythonhosted.org/packages/c2/08/189b27e51fddc9d6b3695331da0e31792c1d88b953ad854e57f06e9b2cc8/charset_normalizer-3.5.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:82cc5835997ec78afe293a192e385099355770a7db94b2fb1239d36b32796f1c", size = 250580, upload-time = "2026-08-12T14:33:25.707Z" }, + { url = "https://files.pythonhosted.org/packages/ac/55/64854e99b25841f83e8e37d9df2f3d1f96f693439f80e5fabd542a7e47ab/charset_normalizer-3.5.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:19e52bda45086df8a4be4bb5910af6f5d9d3b538c78712c8ae09ef10b85bf458", size = 245008, upload-time = "2026-08-12T14:33:26.971Z" }, + { url = "https://files.pythonhosted.org/packages/4f/01/7720c904fa635d4260b4dced6029cf3d298c57b26741365d5a8d28c54043/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:3418edd0ecb72a0a3861cf72f31be0ad9b7fe338ce2b58fb5cc80b9aeb792700", size = 243892, upload-time = "2026-08-12T14:33:28.237Z" }, + { url = "https://files.pythonhosted.org/packages/70/50/7bfcb327631d4870c720872b548745f6ec8baa044d51c21b5d1d32ac4e3a/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:125ee619611019471b177c70bc3e9d4cda9fad7e01d93523501d3b188df0193a", size = 230996, upload-time = "2026-08-12T14:33:29.511Z" }, + { url = "https://files.pythonhosted.org/packages/24/51/40c45d6d940c04005ed721aa54bdebf1ebb2930f8a2ae537e8d60484fb27/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:a3ad0e3da22852533858663848608f3f24c0d35e5cde415a4903476f2b4c88ec", size = 265834, upload-time = "2026-08-12T14:33:30.689Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d4/ef7a227ef89d215b47f9df79c3966610b17faa13bb2f236989207a631622/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:4ebebb410bc517e1d284c52a123e82704b21e4e7e26a21ebecf7439d0647b8a3", size = 245544, upload-time = "2026-08-12T14:33:31.859Z" }, + { url = "https://files.pythonhosted.org/packages/37/a9/a4ca9156964ded61c7718eba410ce11be2fd2b263fda4bcf08367b6578cd/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:91f9f7c151e772acebe489eaec96e96a2877202d7dd144e3f96b8676881715a0", size = 264110, upload-time = "2026-08-12T14:33:33.13Z" }, + { url = "https://files.pythonhosted.org/packages/38/6a/838364bb8702229c6e5f8b23f80ff0f052a12dfaf3113a12fd6acbe92a44/charset_normalizer-3.5.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:401ea6e7af9e7852ed818f64714b579c1935482049670847ca3bd7ba45dc63fb", size = 252303, upload-time = "2026-08-12T14:33:34.98Z" }, + { url = "https://files.pythonhosted.org/packages/9c/5f/d88032edce951f499a2321cf7ae0d35a043c74be12bc22d81084cc7afbcc/charset_normalizer-3.5.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:f7496aed56b06325a1ad419c5bf23c6dd042558e874f71dd1b958f3e255f3053", size = 139964, upload-time = "2026-08-12T14:33:36.195Z" }, + { url = "https://files.pythonhosted.org/packages/37/ae/1c4a46b6b00d1c34d2ee355ef99ad6173674166800d1af0f05f85028d513/charset_normalizer-3.5.0-cp314-cp314-win32.whl", hash = "sha256:606a86c1c3196f3738de39a67a7490bbd61cb31c0e0436070bd0c6a48170b38e", size = 179790, upload-time = "2026-08-12T14:33:37.356Z" }, + { url = "https://files.pythonhosted.org/packages/01/51/f94dcf34fa8eba48c1fb89b6490a5f1426e19488fe5f38aac6c648c99057/charset_normalizer-3.5.0-cp314-cp314-win_amd64.whl", hash = "sha256:ec6c464cf45867f66a2273e2214d9199a8fbad5cb95ca0fd45f6a2fe1d9d2cf4", size = 203723, upload-time = "2026-08-12T14:33:38.639Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ba/91d386870b5d9e4b0d8c4034f63877cc2e47b99c81ef05f3e6d42bf9a53f/charset_normalizer-3.5.0-cp314-cp314-win_arm64.whl", hash = "sha256:dc28949de1bb5f7f30a46f15d74ce7ac5aaa63e03c5de04d68f571c7423af834", size = 183423, upload-time = "2026-08-12T14:33:39.899Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c9/534ecb17b7fb95f9052c4a44cf316316a27d4a8f73e8475ff55e778dcdd7/charset_normalizer-3.5.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:68b7e84ae8239a94f8d2c8f3f3a3a81bcde54805ec8f42a34de927d155688ec6", size = 368967, upload-time = "2026-08-12T14:33:41.093Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b2/ad7c3242d7fe55cd55126c22c65cb1b49779782cdf8932fd01d12232d86a/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:58ca5dc0a0ef99f2801ec0574214c978e9574055bc783830bbb6e7433218609f", size = 239478, upload-time = "2026-08-12T14:33:42.428Z" }, + { url = "https://files.pythonhosted.org/packages/7e/62/77f0b850048e430fc350ec58876b0c020f5c8d0d3956fd1a4d6ae2fa292f/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6c57af4084c10cb3286688d65e4c654190ff5edcbc2411d08cdca0a8a44c59a1", size = 227036, upload-time = "2026-08-12T14:33:43.635Z" }, + { url = "https://files.pythonhosted.org/packages/f0/ba/47d951e1a51dddbaad0a1410baf49fb1d897ceb00281568f1183b79bce9a/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1619a3cc174a7e3963dd34348e6fceb6e50db0ddeb0031bd7c73a58286454fa", size = 260772, upload-time = "2026-08-12T14:33:44.96Z" }, + { url = "https://files.pythonhosted.org/packages/b0/61/8c7ff4c81b2a88271126acf4b83ab3e31f6d63868b0f01d331eaa0f9cb67/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1328cc57dd4372be1265f68232cee890e087416e3e6e93e6ffb32c2bad4d36a4", size = 259273, upload-time = "2026-08-12T14:33:46.185Z" }, + { url = "https://files.pythonhosted.org/packages/e1/fd/36129689be08dc287b951306946657ff70d76e287dd57018861f86d0e474/charset_normalizer-3.5.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:06f4fb62a9139bef056b8b2da6773c94c2f259f90e4b8e53b166f3d0372d7cf6", size = 248086, upload-time = "2026-08-12T14:33:47.54Z" }, + { url = "https://files.pythonhosted.org/packages/cf/f8/bcae67f994c8fd31dda445e5ebf84045823c31443fe46f0e9ee6aca99aa0/charset_normalizer-3.5.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:478650a70a750d75d5add401606c77f77069c32e4ba2c9131dc6cee566962ca0", size = 242671, upload-time = "2026-08-12T14:33:48.746Z" }, + { url = "https://files.pythonhosted.org/packages/61/92/0472cdad1061c2f0e4d3aee29973eb6e81bb8fe256ff2860cf115b15f1c9/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:48920bf6fe83eb2226756ac623fa54940487154eb18f80889d5735cf234965c0", size = 241311, upload-time = "2026-08-12T14:33:50.152Z" }, + { url = "https://files.pythonhosted.org/packages/f2/89/04a03de5d27c77c624d9fcf6287073754bd438df1b58cb7d030c57c2824d/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:168a0cb536b5123a77bc42ecf5e0bf6f923d0d9ae43c42a14eb0677c19ac6c19", size = 229898, upload-time = "2026-08-12T14:33:51.523Z" }, + { url = "https://files.pythonhosted.org/packages/42/a2/639c4278adcb7ed1f4db608dd9ac19b6774fa2285a96b1c0bdb9c124ccbd/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f278e131afa96a3622cef9211c406ea2ad1b68eb06f8837cd443684a40e0ae50", size = 262852, upload-time = "2026-08-12T14:33:52.924Z" }, + { url = "https://files.pythonhosted.org/packages/9d/95/02e34c97bedfd0c5574efb9179c850591acc7f967ba039ed8dd29d332b73/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:d6100f877d2ed95f0856a3fde25334153add94bf2224c43f45f88e7039262aaa", size = 242913, upload-time = "2026-08-12T14:33:54.18Z" }, + { url = "https://files.pythonhosted.org/packages/a3/64/0946aeab6462dad9f160a50dfb4704d3f58a5ee708f085abc2105fbbff0c/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:4c440122e1ea68b1f8b44a631ebf49c39180f6869b1da22d76e8a724208ec6e9", size = 257938, upload-time = "2026-08-12T14:33:55.802Z" }, + { url = "https://files.pythonhosted.org/packages/79/77/36787d41ead124746506a4425c729f4f17c68280af8a6a5baa0a598cae86/charset_normalizer-3.5.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e5f834965c2fe589837bac1002e07e25734ff70381903ccd95b3d649e22bfa40", size = 249467, upload-time = "2026-08-12T14:33:57.081Z" }, + { url = "https://files.pythonhosted.org/packages/65/10/d9f6c5589cd24198d4ce6cd2948191c18e657272f433e5a00d258d9f5c22/charset_normalizer-3.5.0-cp314-cp314t-win32.whl", hash = "sha256:076cf9d3f3c7e410295c09d96355cf3b1bcae74990034d80e4371e20fe1ba4c6", size = 190624, upload-time = "2026-08-12T14:33:58.449Z" }, + { url = "https://files.pythonhosted.org/packages/6c/81/43e0584a802051a22c725795ebe1df78263abc7de858eef6cdc9b36637e9/charset_normalizer-3.5.0-cp314-cp314t-win_amd64.whl", hash = "sha256:3288a560dc3114d5d2ebe309b1ef43f8af355eafe25856832415c2a8196c9db3", size = 215902, upload-time = "2026-08-12T14:33:59.753Z" }, + { url = "https://files.pythonhosted.org/packages/30/f3/af6a1160fef0eac4510d035241e11eccf78e5350e4cd4de79e79fe02a5e5/charset_normalizer-3.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:a284c36b9c6616bf0a8aa4aabba668a0c75ba65ccf40a79868aeaa69ad996897", size = 193452, upload-time = "2026-08-12T14:34:01.017Z" }, + { url = "https://files.pythonhosted.org/packages/42/a4/dee470afb7a55c4f78b6fef37306c51fed17ebf94dbe530798c91d394350/charset_normalizer-3.5.0-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:c38d1e9bc2073b0984d2099ea647fd7f6c0d8f83a1e14e0cd32926f16e4c44ce", size = 341595, upload-time = "2026-08-12T14:34:02.4Z" }, + { url = "https://files.pythonhosted.org/packages/6a/32/9c3126dc429c6d9d7f79c52681a7c4453ed20a26267c9a8275d7ab620aba/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9f45186390aee4d1f26f723c615b67df346766c3b16df000d84d6e374f06757", size = 242177, upload-time = "2026-08-12T14:34:03.741Z" }, + { url = "https://files.pythonhosted.org/packages/4b/9d/5b616a887301ff4cc0916b39ba44257390d3da80deeed6e8b6f2f26b14a8/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f0fde5e5100c735b2274ab898f0742a5dcde492796296cfbe7e0ad6a4cd1a396", size = 236730, upload-time = "2026-08-12T14:34:04.991Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/d6d3e70be93ebe5fabef65e4c7ac113e1d1705cbaeb5fb72467e713aca17/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:3d00e18e7bbf47e332ab63903d18bae31efc701b1d8cca0382b97784a621fc44", size = 265158, upload-time = "2026-08-12T14:34:06.235Z" }, + { url = "https://files.pythonhosted.org/packages/30/e7/3f1fafa87e2643257474f9c4eec609f2193a61d907dce7dd4f3f2390ebd5/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5b81980668800dd1c69faad8aea6e85a8cee0e13bcd3bba7671695ff16260293", size = 262931, upload-time = "2026-08-12T14:34:07.511Z" }, + { url = "https://files.pythonhosted.org/packages/0a/df/ebeb224a949d91829e5e114c6b64372a3c792b00762a9e951ce416f3a32d/charset_normalizer-3.5.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9bb3e0d1345b9c0fe73673ea656375f38a78ec679c2edeae0c24800f04798a85", size = 251388, upload-time = "2026-08-12T14:34:08.949Z" }, + { url = "https://files.pythonhosted.org/packages/a0/64/9a6ce2e7acc5cf1b4636f78f82e89ff581e06a0216a40678b28bd4d832c4/charset_normalizer-3.5.0-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2401f7671242e921e604f609d429f6b282ea4ca787a6ffd22ed7372011ddb9d1", size = 251821, upload-time = "2026-08-12T14:34:10.138Z" }, + { url = "https://files.pythonhosted.org/packages/f1/b1/6e69b8056f615e5ccff6b91ca16db2d47922251f016821a300c115267fef/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:96720f2aeed3434bc48f4d52fbad64ecc820cfed88915d664780ed9ba09ede78", size = 244507, upload-time = "2026-08-12T14:34:11.488Z" }, + { url = "https://files.pythonhosted.org/packages/0f/34/02c15d6a0aa6b934dcdc136b111da63ae857b9fd51cf5505b0736337c2eb/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:c455829625df983f716cbaecbba77f2d1dc2e0e0ed1638c059cece15a279344b", size = 240951, upload-time = "2026-08-12T14:34:12.991Z" }, + { url = "https://files.pythonhosted.org/packages/ae/15/0fe893d3e1c7d111280bd6c4bd4c1e431487a1124a1bcbce78dfeda3a3a8/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_ppc64le.whl", hash = "sha256:c41b067eddcfa5ee6b1169c287605be7fb6b0ea22bba6474c5bb978a668def4f", size = 266162, upload-time = "2026-08-12T14:34:14.232Z" }, + { url = "https://files.pythonhosted.org/packages/55/ea/eca03527307670f5d102c295671a800c404ca958cf94fefd10fc963a72f0/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:e31786a947b136329bfdc458c82c06d4ec539b4a4436b7da4df4aafc9902ee80", size = 251835, upload-time = "2026-08-12T14:34:15.48Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/fee5633081e595fe9e191df6f215106c791ad596eddf5e41e39b8ea0f2e2/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_s390x.whl", hash = "sha256:c5c6d47a865147e0ae3322ce92e7fb52ba3169d94b447deda56897ea2aa6fac9", size = 264314, upload-time = "2026-08-12T14:34:16.679Z" }, + { url = "https://files.pythonhosted.org/packages/cf/fb/17f47ae6ca35b562fb6e6f4b05f7aec6034217353eb4a23aaa3566dc7340/charset_normalizer-3.5.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:c75191e3c8052045179646cb40e280800a4e0bdfda34d9c949c2f268d44e80e4", size = 253194, upload-time = "2026-08-12T14:34:17.975Z" }, + { url = "https://files.pythonhosted.org/packages/59/88/f2b0f7ebb92493e925889ff29239b3b0073ffafd91230dbfc69e5cf9389c/charset_normalizer-3.5.0-cp315-cp315-win32.whl", hash = "sha256:83b62410bd36bb1178a7d563e2ee0cf21eb1c980c912ab99c2c78f06227f1731", size = 179800, upload-time = "2026-08-12T14:34:19.409Z" }, + { url = "https://files.pythonhosted.org/packages/e2/f0/afb5bfdea52fd943b1960403847a276b8e900c6e4cd6a38752321b4eda64/charset_normalizer-3.5.0-cp315-cp315-win_amd64.whl", hash = "sha256:e3b9eaa99a6d8c9ace4cd303915947ef55088d4cd87c6676874f98c5c03aa040", size = 203726, upload-time = "2026-08-12T14:34:20.656Z" }, + { url = "https://files.pythonhosted.org/packages/fc/71/219783eb691aa2ec879c0e521afdfe2b826f9678eed51b9c039d03e0db2b/charset_normalizer-3.5.0-cp315-cp315-win_arm64.whl", hash = "sha256:fec352b793cdc183cc9e7e0b6c10fd7bff38ec54ba44cc43599b9b56f7f3db2e", size = 183428, upload-time = "2026-08-12T14:34:21.975Z" }, + { url = "https://files.pythonhosted.org/packages/d1/d0/14aef3b9f80f2593c039d897e89034635b9eb0eb44b6ce5173bbd79ff338/charset_normalizer-3.5.0-cp315-cp315t-macosx_10_15_universal2.whl", hash = "sha256:c9bde7a960720c8b8e1b5ef7afaa0c9a2f3b55c44abd635b2b29dd066b298e3a", size = 368728, upload-time = "2026-08-12T14:34:23.221Z" }, + { url = "https://files.pythonhosted.org/packages/10/fc/b249466ddbbeffa448b6597631e9091d1f01b5132ff8e7a0e21a6eb72b63/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8cd1283a9fe6c2065c807e9d5da81afe5e1e004caef39adc0d8ae86dd883698", size = 240925, upload-time = "2026-08-12T14:34:24.504Z" }, + { url = "https://files.pythonhosted.org/packages/2b/b9/c17e72aaa1b3e1ca6c184e8025cf138ed492d01a54f85286ff7d31253a4b/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:1c010dd86d3f4c4433c9634d33ce8147393b270dfa54f217f965540b8ae8e075", size = 234932, upload-time = "2026-08-12T14:34:25.822Z" }, + { url = "https://files.pythonhosted.org/packages/18/d7/f84ef0966bbe216f71029e34e7fa425a16b1682e2a40265e679dedf2b655/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5e68229977b2dea28e7061c0c0630a23f2f9f6e9c6fb38d77d3d6dbfe3768b74", size = 261733, upload-time = "2026-08-12T14:34:27.112Z" }, + { url = "https://files.pythonhosted.org/packages/3b/73/3e887fa0781a395339355ed934ab6561ceb5bb52574160f070224039c630/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a60773eb5fda796e6e6f76b9c152d270fe59f9788a51a6ff8ba44082d8548ae4", size = 258460, upload-time = "2026-08-12T14:34:28.431Z" }, + { url = "https://files.pythonhosted.org/packages/b3/81/52ebd9849bf9e35d0b21fff115cb6543162a8e1f2f564e8f87121a336b8c/charset_normalizer-3.5.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d9419f44e568f7fafcdc0b3b5c766a2364e705a9b34fb8a56b431e0d1f3f4258", size = 249894, upload-time = "2026-08-12T14:34:29.698Z" }, + { url = "https://files.pythonhosted.org/packages/85/f3/9366492b8a5fe0187de282e001d61345740cf79eb4a5f20181d769be02b5/charset_normalizer-3.5.0-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:75e243abbb528c1a774390ed71e3f868a9f37b1373442e4bbadd401cfc505ff4", size = 249540, upload-time = "2026-08-12T14:34:30.96Z" }, + { url = "https://files.pythonhosted.org/packages/0b/af/28bb5e5dbd3e67cb9196a62781ac2b6d79492f4fc7a069b6ca7d6d6c8d58/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:54c963ce6404e52255b737e8a06d356fc762d59096ae566203a67cf2b7d050f2", size = 242734, upload-time = "2026-08-12T14:34:32.481Z" }, + { url = "https://files.pythonhosted.org/packages/26/d6/7ccfa62b53b40fc06b2d3504825aa400764740bd10cf248fdc4272441b93/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:63ea0cc840c66670183578c2630d138c0e944aeadfc33f25173ee240f5db780d", size = 239580, upload-time = "2026-08-12T14:34:33.916Z" }, + { url = "https://files.pythonhosted.org/packages/0b/82/71c0c9b046697b8da66b3acefa8d5f92d00a9ef433ad7c3522b971d0369a/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_ppc64le.whl", hash = "sha256:6c06875a1d4a7537bef70f659b55c6b55b9a47ec3ba8f2db610350c2d9915e6e", size = 263281, upload-time = "2026-08-12T14:34:35.333Z" }, + { url = "https://files.pythonhosted.org/packages/d6/01/d027583c869f40ba980c1c76994adbd522c360a6327e72beb44d7c267385/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:4253da1b4456b633651a8d59eb1dc7a8a8fa38241014dd7c217b353e547ae394", size = 250027, upload-time = "2026-08-12T14:34:36.991Z" }, + { url = "https://files.pythonhosted.org/packages/2a/e9/6475d739e0ec8bb1236e06263dc3affaffdf947d8114ad27024932f325da/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_s390x.whl", hash = "sha256:f044cb1cf44012184715f46584658993b5fee9344d71c4b0c455a17a299730c0", size = 257547, upload-time = "2026-08-12T14:34:38.277Z" }, + { url = "https://files.pythonhosted.org/packages/a5/60/d1f502fcaa048a2aca3ab80bfef8407659c131e4f1792fa805fec14b4960/charset_normalizer-3.5.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:a17864853f7c518ae7d4b368af98f427f9396805476af40af8698560f09d7d97", size = 251718, upload-time = "2026-08-12T14:34:39.562Z" }, + { url = "https://files.pythonhosted.org/packages/c3/69/76343dcf4381a698807ff8a20d89f66bbdd9f6222b0b17740f77ab764335/charset_normalizer-3.5.0-cp315-cp315t-win32.whl", hash = "sha256:9e726478d7a213847860219d74665a6892a643ac93b8f76580f6cf9ed39996b7", size = 190757, upload-time = "2026-08-12T14:34:41.053Z" }, + { url = "https://files.pythonhosted.org/packages/e1/ea/d18147626a1667cc773c42104ab155a4ca5d6d4d174b7a35e01062213ea5/charset_normalizer-3.5.0-cp315-cp315t-win_amd64.whl", hash = "sha256:d7229a99120c6c2792d96f4857c2648ce5530e93667a2c2388c5ef69a6b84775", size = 215431, upload-time = "2026-08-12T14:34:42.501Z" }, + { url = "https://files.pythonhosted.org/packages/47/21/4869598aae0872d94faa5933918a4fe37ab2c5af9d095786e241f9506fed/charset_normalizer-3.5.0-cp315-cp315t-win_arm64.whl", hash = "sha256:527e28a5e751d9e11369b9c5f9ab35c748eb9c109101920c7deb40d6eadf8d03", size = 193205, upload-time = "2026-08-12T14:34:43.781Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f3/7b523d807cb5e73562ef8acf21d39cdb9d704955327362c781bc3478a73d/charset_normalizer-3.5.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:5a4ee37248dfac25107c758bda99d545ce73e60b44d2dd39e4a2bb9f2831e9f5", size = 330840, upload-time = "2026-08-12T14:34:45.06Z" }, + { url = "https://files.pythonhosted.org/packages/f0/de/fc68978fe78ca97063c96d764e41ff92ca639948f319271e0ff450e577a2/charset_normalizer-3.5.0-cp37-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a864bdcacd8bff58bb4845304e031f821a3ec64b2b7259f2d409cd49c9e59ca3", size = 251862, upload-time = "2026-08-12T14:34:46.58Z" }, + { url = "https://files.pythonhosted.org/packages/a9/cb/82b41a0ab7fb1a88065f1d78ad32696ad88ea3fe8e25b8189d08833938de/charset_normalizer-3.5.0-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:84b736e3b391601bc47b86da381c749c0f894e9191aaca9f31f30c2632206df3", size = 239484, upload-time = "2026-08-12T14:34:47.869Z" }, + { url = "https://files.pythonhosted.org/packages/e8/0c/19608b631f4538f908098d4a2d56a8f79a665e27cc58e9d90479761a9227/charset_normalizer-3.5.0-cp37-abi3-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6abb1f356fb865baeb6ebc3fadd843e9a96fbf49b9adcca55037f3cceccb7438", size = 230602, upload-time = "2026-08-12T14:34:49.265Z" }, + { url = "https://files.pythonhosted.org/packages/29/db/f648eb30e14eba301aed61e11672156f137905c1bdbb530151abe8065943/charset_normalizer-3.5.0-cp37-abi3-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d366548d2ee28a8cfdcc4296363978cc644a728333be9824d2de4652e83df0a", size = 259208, upload-time = "2026-08-12T14:34:50.632Z" }, + { url = "https://files.pythonhosted.org/packages/d3/e0/ed2c8bdbac484d69614d6993143aeb6cb0f4dd1561c883402517b623c8ef/charset_normalizer-3.5.0-cp37-abi3-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d672f329ae504ee240eb39b6effb3318aa8e7e8924c0ce8eee5760b3fad98539", size = 253659, upload-time = "2026-08-12T14:34:52.11Z" }, + { url = "https://files.pythonhosted.org/packages/32/08/b4907cb9ec5b521d9d024ced13611240b86ef065c2eb15b3ad2334dc9940/charset_normalizer-3.5.0-cp37-abi3-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c54036a518748b6c02e666f6d46c3817561998fb904c3be25b56fb4fe3dc5706", size = 248821, upload-time = "2026-08-12T14:34:53.399Z" }, + { url = "https://files.pythonhosted.org/packages/12/b2/e2d1abcfbc05822f0030869efb4e9f8a3658e13b4821796d4b62da917327/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:22a1889f1c9b752c63c36758a0c2145458e3cadb20fced7a0790002e9dd12b26", size = 240271, upload-time = "2026-08-12T14:34:55.09Z" }, + { url = "https://files.pythonhosted.org/packages/dc/f9/4ba127ad610542fa3eabfa41c45bf12d357860a815b3566374ec0188e213/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:b7eb3eab5c646d3de7dcb14a7c9caebace5249c5767da39e1761cb1576e521a3", size = 232155, upload-time = "2026-08-12T14:34:56.543Z" }, + { url = "https://files.pythonhosted.org/packages/01/68/40613182366d00bd6dbd5f6c84a926cbd120960e038a8269e9ae7d782762/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_ppc64le.whl", hash = "sha256:2080aa129a28267984cdc902898993d788c995c384e285d0d19199f56760d52e", size = 259674, upload-time = "2026-08-12T14:34:57.815Z" }, + { url = "https://files.pythonhosted.org/packages/0a/53/4574a14fa4c9de4a6c9f31725354bfa40b67f653e6d594ce1654f9a41b32/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_riscv64.whl", hash = "sha256:fd68c825548a611158230e2f9222e210ceb2e3391995c0aa5865cbdf3ab4bd49", size = 246122, upload-time = "2026-08-12T14:34:59.337Z" }, + { url = "https://files.pythonhosted.org/packages/5a/02/bd8030d13d92c058ca7b2b9615bbb3169569e144db64d65c149cd45abf5e/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_s390x.whl", hash = "sha256:d8a9316f4da85e937242642b537c6d55d7e9287dd38e5634732f8233932aff45", size = 255221, upload-time = "2026-08-12T14:35:00.71Z" }, + { url = "https://files.pythonhosted.org/packages/77/9d/10ecd3bcbe2666b3d4d4026c97b48f73990682815db516052a1e8f4a31c5/charset_normalizer-3.5.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d90254c8f609338c53ec180fcd4c4f9c16502e238e3fc88ca7fd4c2f38d445b8", size = 253450, upload-time = "2026-08-12T14:35:02.217Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0f/d044c4872c0938a84f87b5027a698c0e61bacfc5c3551a4e749ca9b7bc5c/charset_normalizer-3.5.0-cp37-abi3-win32.whl", hash = "sha256:8b3e9e29b8b07cc461b9ce7768db7693a93979d0dadf22046f6f3555ded2f516", size = 173594, upload-time = "2026-08-12T14:35:03.845Z" }, + { url = "https://files.pythonhosted.org/packages/10/6b/6046773901f1944b9a89436351529811ee958afc7b774563be9d74a6f0c3/charset_normalizer-3.5.0-cp37-abi3-win_amd64.whl", hash = "sha256:0c8953d9d1617794cfc40d81179571c9ba3805dd029623a15c93f1fb70e60a74", size = 198959, upload-time = "2026-08-12T14:35:05.187Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a6/b57708ac92aefc8e8389d51d5178129b81f03196da61ee2c23e687b8178a/charset_normalizer-3.5.0-cp37-abi3-win_arm64.whl", hash = "sha256:562d24ca7797c1af8852994950c2e623a907b201fc4b0ed29e92af173d3828ca", size = 267055, upload-time = "2026-08-12T14:35:06.533Z" }, + { url = "https://files.pythonhosted.org/packages/22/c7/754d09943a616937df61e4ba367c409ded2a987e872972098d51a6fcf73b/charset_normalizer-3.5.0-py3-none-any.whl", hash = "sha256:993dfcbe75a85a3784abb5084f2c41b915767c90546fcc92803cffa28611baea", size = 67943, upload-time = "2026-08-12T14:35:30.363Z" }, +] + +[[package]] +name = "cryptography" +version = "50.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/41/6cbdcf9142d00fe82836fbb51e503e58088575cf7a0fe1dbff6695bf0840/cryptography-50.0.0.tar.gz", hash = "sha256:eeac2acb5a20ed25e0ad6d1df9891a520b78b404266b6d11778f25d5d691a6c9", size = 880201, upload-time = "2026-07-31T14:25:10.11Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/5c/59086b4aac5e879d38ddbcf74e4be7ade89cebc3eb199a55da998c3bb46a/cryptography-50.0.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:031e2d5dd4bb9caa3ca9c82e5a197fd8ae680232cee62603d1a813f3f07e3d03", size = 4001252, upload-time = "2026-07-31T14:23:33.331Z" }, + { url = "https://files.pythonhosted.org/packages/57/ef/8f2df13c7216bcad3e1c74e07f6e193d93e998e114f524a53877c9af27ad/cryptography-50.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fd9192b7b70c573d7f214eb1ae35e00d359f6f5e4b27c7e21e30de1fc6204645", size = 4719554, upload-time = "2026-07-31T14:23:35.611Z" }, + { url = "https://files.pythonhosted.org/packages/d9/41/029086c34d91052fc3b88bcc8056f709a7c915c7a23b235a54eb800b1c97/cryptography-50.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:06a32a980526a6ab9a4b9bf8f7385800791e2bb960903cb6b530e4817509a3b7", size = 4702130, upload-time = "2026-07-31T14:23:37.635Z" }, + { url = "https://files.pythonhosted.org/packages/7d/ff/b6ce0954962e7f7b969f850a883744197bb3910bdfd7b6da162eab7d9f68/cryptography-50.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a1b30560f2acc95aa8b2e06e716a13dbfc97314747b80d9707e307f77b40d6b3", size = 4725244, upload-time = "2026-07-31T14:23:39.471Z" }, + { url = "https://files.pythonhosted.org/packages/06/1e/63a1027cb7fec360a182208e1b7767d5aa1fe57be3d6aa856e69a321edc0/cryptography-50.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:8d89f3976b10b4ce31118de72329025f70d2c6ead14a8217c5514dd2c6d5a78f", size = 5342265, upload-time = "2026-07-31T14:23:41.286Z" }, + { url = "https://files.pythonhosted.org/packages/6b/72/a1116d683a6d7ece94590013882515de087edf9ef0e6292aae615a44df73/cryptography-50.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b42a28c1844fd9de8f3f7d540e36b66f3a9c83fceac7170ebc7a6a19edd9dcae", size = 4734609, upload-time = "2026-07-31T14:23:43.139Z" }, + { url = "https://files.pythonhosted.org/packages/15/37/36a9c479bbe49acea2636c7fd3360d20f7b7e079c300352011c44850b181/cryptography-50.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:900131fafd8aead39ac7dd3a7e833be754c17a95cfd91221636949fe4eb0aa8a", size = 4356517, upload-time = "2026-07-31T14:23:44.939Z" }, + { url = "https://files.pythonhosted.org/packages/32/98/8a151d64367204cbc63ec65d37502f1d9c53cf4bfc6ec3c532614dbec60d/cryptography-50.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:07949c449a1abcf60d1ee6e88956d89404c7df3c8258f46589e912988e551987", size = 4724529, upload-time = "2026-07-31T14:23:46.93Z" }, + { url = "https://files.pythonhosted.org/packages/22/f6/ec13b470172126464a86bf54d2294a46d29837fc51ba3e45d4047946fb5e/cryptography-50.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:f89831ef99dd7dd169ab06d63a831adb9e20a87aac6d380266bbda5823349169", size = 5299852, upload-time = "2026-07-31T14:23:48.851Z" }, + { url = "https://files.pythonhosted.org/packages/da/3a/f05e32c99d440c9bb891ea0e36c9091891e36be5a9a87ab2ee6ea20729f6/cryptography-50.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:82148ec5bddac30b51a5b3c1945075f896fa022cb93f8e4a01e9f6ee95292c5f", size = 4734462, upload-time = "2026-07-31T14:23:50.861Z" }, + { url = "https://files.pythonhosted.org/packages/ca/dc/bd72b26be8953f80625f63151efd38eee71c76ca6cf591c08ff34615a79e/cryptography-50.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1489e263a8048bb8b6a8bac662eb2d402ea5d2b7b4699b72f385f1e2772db105", size = 4852708, upload-time = "2026-07-31T14:23:52.715Z" }, + { url = "https://files.pythonhosted.org/packages/27/20/c930314a2ab476d15dec966ec87e2e9637bb02b06106b12c0396c57bb603/cryptography-50.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7cec5b856506da6defb290f30c9ee687d5f5e8cb0bd3f6459dde43b0b4fa40ef", size = 5004179, upload-time = "2026-07-31T14:23:54.887Z" }, + { url = "https://files.pythonhosted.org/packages/32/2e/c9db68a0c4bfa28e310707527c0ee3a2bd254104d2e02e68f368e197aa4c/cryptography-50.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:bd1c592e4d5974f0d08d4888e432157adba757c66da0246918e43677fafa2d30", size = 3840395, upload-time = "2026-07-31T14:23:56.677Z" }, + { url = "https://files.pythonhosted.org/packages/c3/fb/951032a3bf22a5697c83183fb6294a4843772947a70e616c57b3ff5f522e/cryptography-50.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:49e7d93abdbd2990caced757e5fade25302f719c3c8fb6e6fff2dde98999fc41", size = 3989258, upload-time = "2026-07-31T14:23:58.881Z" }, + { url = "https://files.pythonhosted.org/packages/d4/67/91eb047e69c5e845f2f14b8a2e4a1aab0f283cb885531e9e22c8adb176bc/cryptography-50.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:19736989797678c6af1e55cd49055cdbcb55d8f6b5583ac5335f933aba9101dc", size = 4700648, upload-time = "2026-07-31T14:24:00.702Z" }, + { url = "https://files.pythonhosted.org/packages/30/82/85f0f7425c856b9f96459411eb12e74ef72df9caf6f8f15bf23a33ff131f/cryptography-50.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:80b63928fa35083b33966ce1efb70e5b9607181e49dcd1c22c8c005e319f667f", size = 4682442, upload-time = "2026-07-31T14:24:02.538Z" }, + { url = "https://files.pythonhosted.org/packages/1a/28/b555a365adff1cca2fbe7b9e487d68a40de6bc67ff2cb587473eb43de0e7/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:d58c3db7cd6eed54e6c06744db55456b65ebd7492ddeae9c1e93cfca7aa857d3", size = 4707596, upload-time = "2026-07-31T14:24:04.394Z" }, + { url = "https://files.pythonhosted.org/packages/72/d8/f52538140cc719df62a01cf87d1c7142318d235817109d6f4054d7c352d6/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:df2a58a472f332225671c35b0a830208b86d004f82baa8530fa3782c85646533", size = 5314552, upload-time = "2026-07-31T14:24:06.31Z" }, + { url = "https://files.pythonhosted.org/packages/38/14/6120e5bd7c5aa022ad15424ba4d5c5269d0d9448ed4d55e492ea91e3c1c4/cryptography-50.0.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:11b74db56cdbe3cdee6e3f6982ecb70334fa10dce99ed58bf7894aaaa3b2a037", size = 4717113, upload-time = "2026-07-31T14:24:08.349Z" }, + { url = "https://files.pythonhosted.org/packages/fa/71/190bf38c3ee2e0f8efc9860ae100c9df4169742eef274b91e7aa1cb133b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:f59e38625469987d7ef6d495323c55e7db6c212eaf6112267e0d3b565a2e9c9f", size = 4338580, upload-time = "2026-07-31T14:24:10.227Z" }, + { url = "https://files.pythonhosted.org/packages/3a/63/504ccfbbe61fd8aa983f7f146399cdf034c72c2fc55f5b2dfdcdcdb20c99/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:ecfed7367f965a0328cfbdd70da860f15441f002f613185668c6e6ebf5a0ac11", size = 4707038, upload-time = "2026-07-31T14:24:12.169Z" }, + { url = "https://files.pythonhosted.org/packages/01/77/2cf79bbfc4d12ca106437a6e170d6aaa01a373e93093118aaaef0e801bd4/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:9aa87839c383bdbab6ef865787a1fb877af8dd03464c4400322726feaaadfc6d", size = 5273110, upload-time = "2026-07-31T14:24:14.38Z" }, + { url = "https://files.pythonhosted.org/packages/e5/45/8aae2972c520145377ea3559a605a899bebe227bf070b33cdb445929a9b9/cryptography-50.0.0-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:6ba6a53445bd3cfa809ef3ef5f1589aa6ba08784a1d962bf47d0940e871dab1c", size = 4716439, upload-time = "2026-07-31T14:24:16.415Z" }, + { url = "https://files.pythonhosted.org/packages/7b/20/4fe50b619a48c2525cc46e2dbc1ac490708d704be5d467bdaac6dc955682/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3f5735ffe4996d28b809371756219f5354864902a3b9e7c0b9ee87041209fc9c", size = 4837383, upload-time = "2026-07-31T14:24:18.553Z" }, + { url = "https://files.pythonhosted.org/packages/92/91/3a31366e183343d3703f8995c095f5734676bd6938118047e50fcf279eb4/cryptography-50.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1b4a266766514614f8aa60416e71f2fc6e575d36e7bdc90f644fadb2f4b75b95", size = 4985772, upload-time = "2026-07-31T14:24:20.385Z" }, + { url = "https://files.pythonhosted.org/packages/74/9a/02ffe35b2853d121689871eb5dce862092562b3a1ed5cc98f1aaed441506/cryptography-50.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:12b9c6996425c76ea6c457ace4f3073e715b8c545add07cd1a8f3a4f90691269", size = 3816291, upload-time = "2026-07-31T14:24:22.125Z" }, + { url = "https://files.pythonhosted.org/packages/03/37/73d005be173aff344af30e9fd2a576575cb2391a7101d9cd3842e1fa8cce/cryptography-50.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ccdc4a71a4dabae05de219404f9f4abc38e3b58422177ff93d0da05967dafa07", size = 4036009, upload-time = "2026-07-31T14:24:24.122Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c6/7a6202a534e32103a285b7834a120869557fe198d51d7cfe59754c8bda9c/cryptography-50.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:910e1d2668e7de9648f2bcee30e180db2a6b15c30f887d7c4c93ddf96e3992e3", size = 4745252, upload-time = "2026-07-31T14:24:26.118Z" }, + { url = "https://files.pythonhosted.org/packages/85/4f/0fa8c2f4428198f15d9ff8d63400e27afbf94ce833f6108da1eb3753f945/cryptography-50.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a91296cb61e8df6f86d0c19cc4068228da256bf59bf86049fbd821084565327f", size = 4728939, upload-time = "2026-07-31T14:24:27.994Z" }, + { url = "https://files.pythonhosted.org/packages/d1/63/54dd723490ba2dc09b299682c10b38db38f159728bcaae8c591b8af2f22d/cryptography-50.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e722f16708d854fe924790e051061f6704a472c3bac347b6fd88033ea8dd0dc5", size = 4748483, upload-time = "2026-07-31T14:24:30.254Z" }, + { url = "https://files.pythonhosted.org/packages/1d/dd/7c77d26285cc7f6991efce64a0f5b4f9383bfa5dd8c5033003eaf7db4cdb/cryptography-50.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d764dcf130c428ef66786f866dd750f53182bc608813489915e9fc106bb0c82f", size = 5367599, upload-time = "2026-07-31T14:24:32.457Z" }, + { url = "https://files.pythonhosted.org/packages/46/c9/f60aed34c013f317f92817b6c171c2d22a78270fa41109bd4b08af26b194/cryptography-50.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:105110f43a471dbd0060b9c9516cb8a6a79233631a04cc2ba16f28323ac6e025", size = 4762647, upload-time = "2026-07-31T14:24:34.599Z" }, + { url = "https://files.pythonhosted.org/packages/be/f3/f9a0173b139372c3a48ed98154b45cc6b9de17c789d5ab552e621c293609/cryptography-50.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:828743d939e9629bc267b8e2d08d8bb67cd4319c771a33d4b18b22dd8fb7440a", size = 4385197, upload-time = "2026-07-31T14:24:36.647Z" }, + { url = "https://files.pythonhosted.org/packages/d8/36/83bb81f6e569bc38e1e4a7bc80f29b46bb9601920bc455fc8e888f5d5742/cryptography-50.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:2a8183b489dc1f7f80f135780fadc1108f14b31b8a40411c7a5b17425f65f28b", size = 4748095, upload-time = "2026-07-31T14:24:39.493Z" }, + { url = "https://files.pythonhosted.org/packages/6b/16/d3008eff98c764979865834c3d386d4fd041b5f52e7f34fc29ac1a5eb515/cryptography-50.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6e7d61120573a7f2cd94cc095f9e81f6967c61ccdf194285aa143ecec8e0b708", size = 5325948, upload-time = "2026-07-31T14:24:41.556Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f8/d97f9603efda3888187bfdb893f26c41be4735c10631d05d284ee6b047c4/cryptography-50.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:37fdb0d0111f1e2ff07139dfb79f1b49531f8e213c46f1163dd7642979b58c47", size = 4762400, upload-time = "2026-07-31T14:24:43.636Z" }, + { url = "https://files.pythonhosted.org/packages/64/a2/4615c8f7d81a00b1d6e6afe19f694e1543582349fb5f4076f6cb5dc36485/cryptography-50.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87f62a3d3b9888ed0fdde100ec06aa61ca9cd44bad9057d1dff9a516b5f5bb9", size = 4878208, upload-time = "2026-07-31T14:24:45.522Z" }, + { url = "https://files.pythonhosted.org/packages/d2/1a/efcfb02f91407149a0dacffffab791f7e19bf6385f63b3666dc8b5e5c9c8/cryptography-50.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:65c2c3add92b45fd0709db8594536aea39c2a67af0e27ffcf049c498501140b7", size = 5037050, upload-time = "2026-07-31T14:24:47.697Z" }, + { url = "https://files.pythonhosted.org/packages/57/30/4a22984d4f1bdfb8c054f07a92bc176b97a3134cc1d6c4b3bffb1f3688b4/cryptography-50.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:d24fead1d4d076e1bfb006dcec392074a3cd8d7b4fc8a595aa64073b2b7a96ba", size = 3874135, upload-time = "2026-07-31T14:24:50.085Z" }, + { url = "https://files.pythonhosted.org/packages/9d/3e/e54cde8c01631a5a8226ccd617eab9e57fd5cfdad90f1a9e6bb570794631/cryptography-50.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5e34edd123674534acd70147f0ca331eaa2c74e6325fb2028c886aa26ba0b68c", size = 3963170, upload-time = "2026-07-31T14:24:51.968Z" }, + { url = "https://files.pythonhosted.org/packages/01/b6/0b9e125e90f3d2dcf599a218a899cda7326a3158cfa258723f0b398b08f6/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8eb5e1172eb569ea8a872796576e6a67c276351728b6455d5beb01242b027c6a", size = 4692441, upload-time = "2026-07-31T14:24:53.743Z" }, + { url = "https://files.pythonhosted.org/packages/53/c9/a5151588710785a96d7bc4de27d4cd62f263bbbcb203cfe29df537eb6505/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:910d11e1a385c654bf738bf3e6b8e6ed5de0f5610fcae2be9e5b398d8081d20e", size = 4699810, upload-time = "2026-07-31T14:24:55.746Z" }, + { url = "https://files.pythonhosted.org/packages/c7/1a/15b92b25eb6ce3089cd49377ae990a0f3ad485a510f968aed1f19dbdcdf2/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:62598a8a57f815db4c6259a4e97d857dab56697e7de8e8ab02352ab74da1995d", size = 4691924, upload-time = "2026-07-31T14:24:58.082Z" }, + { url = "https://files.pythonhosted.org/packages/62/15/219075012ab13e8905f3cd572204f4acb4b111df787104346b9bc0cea789/cryptography-50.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:07479a1cb08219ab719147e742e76090c9c773321959bb94946fffdd397a6437", size = 4699593, upload-time = "2026-07-31T14:24:59.951Z" }, + { url = "https://files.pythonhosted.org/packages/8e/b5/c2c5fce26f0ee40d21bafe7f191d29a34b35a65ac4fe8a1191d1983612e9/cryptography-50.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c99c003e088647b8a5b7c145d6f78c335f6348332b62e142d411c4b63d1460b9", size = 3813796, upload-time = "2026-07-31T14:25:02.298Z" }, +] + +[[package]] +name = "google-api-core" +version = "2.34.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-auth" }, + { name = "googleapis-common-protos" }, + { name = "proto-plus" }, + { name = "protobuf" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7b/7c/9be3903e3d45415e8ca493c75f8990a0f6f579d168015d44c379350d0ab0/google_api_core-2.34.0.tar.gz", hash = "sha256:98a779fe72de956eb1c9c2f47ff4c4432a668ece1a002ec38bed07ec2698ae59", size = 187953, upload-time = "2026-08-06T06:23:58.128Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/c1/a8a92ae1bc4b1a8f804c776d7d3f0c771b78a62c3ad4df1be41b3fd8c767/google_api_core-2.34.0-py3-none-any.whl", hash = "sha256:cdf9c67e7ca2402d86ccbfde5f2503fc83e3cc3f58cc78456ae96cad24a6d2de", size = 180545, upload-time = "2026-08-06T06:22:47.502Z" }, +] + +[[package]] +name = "google-auth" +version = "2.56.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "pyasn1-modules" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/4c/fa42116a48bab3f7a143cf5042ecff7df9c8b73f8a376203cd534d1dc966/google_auth-2.56.3.tar.gz", hash = "sha256:40e229fc901f0a305b553050e5fce562d509bee0435be053abfa91582b51b90c", size = 367110, upload-time = "2026-08-06T06:24:01.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/b3/6117b2f24065cd7e2c4f140e9a193e215f089ca8ba314cf91eb9d0b7fe0a/google_auth-2.56.3-py3-none-any.whl", hash = "sha256:8ec438808f813ad034535000261eed1067475d229d05bbf4216e78c3f2362e53", size = 259116, upload-time = "2026-08-06T06:22:51.788Z" }, +] + +[[package]] +name = "google-cloud-core" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core" }, + { name = "google-auth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4a/c6/9d7d9ed6703eb35306ca7bf381fb66ba8d978c61a1b550a2e1730a4c4ce8/google_cloud_core-2.6.1.tar.gz", hash = "sha256:1e044b131f2ae097b92312fa195164b0aeb6dc6a88e00231e1210516314c420c", size = 36017, upload-time = "2026-08-06T06:24:18.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/4f/37960d5255988b218c40c9b5a2b731013684294a50735d1dd1ab4894e531/google_cloud_core-2.6.1-py3-none-any.whl", hash = "sha256:2682a8a4474a32f56292fb4bca7fa7e4fb0b4af958f6abfe4bca8d195747fd45", size = 29393, upload-time = "2026-08-06T06:23:11.405Z" }, +] + +[[package]] +name = "google-cloud-storage" +version = "3.13.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-api-core" }, + { name = "google-auth" }, + { name = "google-cloud-core" }, + { name = "google-crc32c" }, + { name = "google-resumable-media" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/7e/73bb7512df1d1aad6ce3f9aed847cd40e0cd400ba4a85d86ab8eb412e9cc/google_cloud_storage-3.13.1.tar.gz", hash = "sha256:a80bf8cac2794808aa61c50c5f769ecbbe2d10331bacd0d69d30e59b14b346b2", size = 17341051, upload-time = "2026-08-06T06:24:42.229Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/6f/d69f0e185e08ddb58c323a0a935af2b492907b5de362bc08933b0a3b5644/google_cloud_storage-3.13.1-py3-none-any.whl", hash = "sha256:98208de6c21e85cecd3eb44551894efff33d98365500e178867d4305854a770a", size = 341486, upload-time = "2026-08-06T06:23:36.548Z" }, +] + +[[package]] +name = "google-crc32c" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/03/41/4b9c02f99e4c5fb477122cd5437403b552873f014616ac1d19ac8221a58d/google_crc32c-1.8.0.tar.gz", hash = "sha256:a428e25fb7691024de47fecfbff7ff957214da51eddded0da0ae0e0f03a2cf79", size = 14192, upload-time = "2025-12-16T00:35:25.142Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/ac/6f7bc93886a823ab545948c2dd48143027b2355ad1944c7cf852b338dc91/google_crc32c-1.8.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:0470b8c3d73b5f4e3300165498e4cf25221c7eb37f1159e221d1825b6df8a7ff", size = 31296, upload-time = "2025-12-16T00:19:07.261Z" }, + { url = "https://files.pythonhosted.org/packages/f7/97/a5accde175dee985311d949cfcb1249dcbb290f5ec83c994ea733311948f/google_crc32c-1.8.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:119fcd90c57c89f30040b47c211acee231b25a45d225e3225294386f5d258288", size = 30870, upload-time = "2025-12-16T00:29:17.669Z" }, + { url = "https://files.pythonhosted.org/packages/3d/63/bec827e70b7a0d4094e7476f863c0dbd6b5f0f1f91d9c9b32b76dcdfeb4e/google_crc32c-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6f35aaffc8ccd81ba3162443fabb920e65b1f20ab1952a31b13173a67811467d", size = 33214, upload-time = "2025-12-16T00:40:19.618Z" }, + { url = "https://files.pythonhosted.org/packages/63/bc/11b70614df04c289128d782efc084b9035ef8466b3d0a8757c1b6f5cf7ac/google_crc32c-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:864abafe7d6e2c4c66395c1eb0fe12dc891879769b52a3d56499612ca93b6092", size = 33589, upload-time = "2025-12-16T00:40:20.7Z" }, + { url = "https://files.pythonhosted.org/packages/3e/00/a08a4bc24f1261cc5b0f47312d8aebfbe4b53c2e6307f1b595605eed246b/google_crc32c-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:db3fe8eaf0612fc8b20fa21a5f25bd785bc3cd5be69f8f3412b0ac2ffd49e733", size = 34437, upload-time = "2025-12-16T00:35:19.437Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ef/21ccfaab3d5078d41efe8612e0ed0bfc9ce22475de074162a91a25f7980d/google_crc32c-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:014a7e68d623e9a4222d663931febc3033c5c7c9730785727de2a81f87d5bab8", size = 31298, upload-time = "2025-12-16T00:20:32.241Z" }, + { url = "https://files.pythonhosted.org/packages/c5/b8/f8413d3f4b676136e965e764ceedec904fe38ae8de0cdc52a12d8eb1096e/google_crc32c-1.8.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:86cfc00fe45a0ac7359e5214a1704e51a99e757d0272554874f419f79838c5f7", size = 30872, upload-time = "2025-12-16T00:33:58.785Z" }, + { url = "https://files.pythonhosted.org/packages/f6/fd/33aa4ec62b290477181c55bb1c9302c9698c58c0ce9a6ab4874abc8b0d60/google_crc32c-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:19b40d637a54cb71e0829179f6cb41835f0fbd9e8eb60552152a8b52c36cbe15", size = 33243, upload-time = "2025-12-16T00:40:21.46Z" }, + { url = "https://files.pythonhosted.org/packages/71/03/4820b3bd99c9653d1a5210cb32f9ba4da9681619b4d35b6a052432df4773/google_crc32c-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:17446feb05abddc187e5441a45971b8394ea4c1b6efd88ab0af393fd9e0a156a", size = 33608, upload-time = "2025-12-16T00:40:22.204Z" }, + { url = "https://files.pythonhosted.org/packages/7c/43/acf61476a11437bf9733fb2f70599b1ced11ec7ed9ea760fdd9a77d0c619/google_crc32c-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:71734788a88f551fbd6a97be9668a0020698e07b2bf5b3aa26a36c10cdfb27b2", size = 34439, upload-time = "2025-12-16T00:35:20.458Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5f/7307325b1198b59324c0fa9807cafb551afb65e831699f2ce211ad5c8240/google_crc32c-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:4b8286b659c1335172e39563ab0a768b8015e88e08329fa5321f774275fc3113", size = 31300, upload-time = "2025-12-16T00:21:56.723Z" }, + { url = "https://files.pythonhosted.org/packages/21/8e/58c0d5d86e2220e6a37befe7e6a94dd2f6006044b1a33edf1ff6d9f7e319/google_crc32c-1.8.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:2a3dc3318507de089c5384cc74d54318401410f82aa65b2d9cdde9d297aca7cb", size = 30867, upload-time = "2025-12-16T00:38:31.302Z" }, + { url = "https://files.pythonhosted.org/packages/ce/a9/a780cc66f86335a6019f557a8aaca8fbb970728f0efd2430d15ff1beae0e/google_crc32c-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:14f87e04d613dfa218d6135e81b78272c3b904e2a7053b841481b38a7d901411", size = 33364, upload-time = "2025-12-16T00:40:22.96Z" }, + { url = "https://files.pythonhosted.org/packages/21/3f/3457ea803db0198c9aaca2dd373750972ce28a26f00544b6b85088811939/google_crc32c-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cb5c869c2923d56cb0c8e6bcdd73c009c36ae39b652dbe46a05eb4ef0ad01454", size = 33740, upload-time = "2025-12-16T00:40:23.96Z" }, + { url = "https://files.pythonhosted.org/packages/df/c0/87c2073e0c72515bb8733d4eef7b21548e8d189f094b5dad20b0ecaf64f6/google_crc32c-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:3cc0c8912038065eafa603b238abf252e204accab2a704c63b9e14837a854962", size = 34437, upload-time = "2025-12-16T00:35:21.395Z" }, + { url = "https://files.pythonhosted.org/packages/d1/db/000f15b41724589b0e7bc24bc7a8967898d8d3bc8caf64c513d91ef1f6c0/google_crc32c-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:3ebb04528e83b2634857f43f9bb8ef5b2bbe7f10f140daeb01b58f972d04736b", size = 31297, upload-time = "2025-12-16T00:23:20.709Z" }, + { url = "https://files.pythonhosted.org/packages/d7/0d/8ebed0c39c53a7e838e2a486da8abb0e52de135f1b376ae2f0b160eb4c1a/google_crc32c-1.8.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:450dc98429d3e33ed2926fc99ee81001928d63460f8538f21a5d6060912a8e27", size = 30867, upload-time = "2025-12-16T00:43:14.628Z" }, + { url = "https://files.pythonhosted.org/packages/ce/42/b468aec74a0354b34c8cbf748db20d6e350a68a2b0912e128cabee49806c/google_crc32c-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3b9776774b24ba76831609ffbabce8cdf6fa2bd5e9df37b594221c7e333a81fa", size = 33344, upload-time = "2025-12-16T00:40:24.742Z" }, + { url = "https://files.pythonhosted.org/packages/1c/e8/b33784d6fc77fb5062a8a7854e43e1e618b87d5ddf610a88025e4de6226e/google_crc32c-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:89c17d53d75562edfff86679244830599ee0a48efc216200691de8b02ab6b2b8", size = 33694, upload-time = "2025-12-16T00:40:25.505Z" }, + { url = "https://files.pythonhosted.org/packages/92/b1/d3cbd4d988afb3d8e4db94ca953df429ed6db7282ed0e700d25e6c7bfc8d/google_crc32c-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:57a50a9035b75643996fbf224d6661e386c7162d1dfdab9bc4ca790947d1007f", size = 34435, upload-time = "2025-12-16T00:35:22.107Z" }, + { url = "https://files.pythonhosted.org/packages/21/88/8ecf3c2b864a490b9e7010c84fd203ec8cf3b280651106a3a74dd1b0ca72/google_crc32c-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:e6584b12cb06796d285d09e33f63309a09368b9d806a551d8036a4207ea43697", size = 31301, upload-time = "2025-12-16T00:24:48.527Z" }, + { url = "https://files.pythonhosted.org/packages/36/c6/f7ff6c11f5ca215d9f43d3629163727a272eabc356e5c9b2853df2bfe965/google_crc32c-1.8.0-cp314-cp314-macosx_12_0_x86_64.whl", hash = "sha256:f4b51844ef67d6cf2e9425983274da75f18b1597bb2c998e1c0a0e8d46f8f651", size = 30868, upload-time = "2025-12-16T00:48:12.163Z" }, + { url = "https://files.pythonhosted.org/packages/56/15/c25671c7aad70f8179d858c55a6ae8404902abe0cdcf32a29d581792b491/google_crc32c-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b0d1a7afc6e8e4635564ba8aa5c0548e3173e41b6384d7711a9123165f582de2", size = 33381, upload-time = "2025-12-16T00:40:26.268Z" }, + { url = "https://files.pythonhosted.org/packages/42/fa/f50f51260d7b0ef5d4898af122d8a7ec5a84e2984f676f746445f783705f/google_crc32c-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8b3f68782f3cbd1bce027e48768293072813469af6a61a86f6bb4977a4380f21", size = 33734, upload-time = "2025-12-16T00:40:27.028Z" }, + { url = "https://files.pythonhosted.org/packages/08/a5/7b059810934a09fb3ccb657e0843813c1fee1183d3bc2c8041800374aa2c/google_crc32c-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:d511b3153e7011a27ab6ee6bb3a5404a55b994dc1a7322c0b87b29606d9790e2", size = 34878, upload-time = "2025-12-16T00:35:23.142Z" }, + { url = "https://files.pythonhosted.org/packages/52/c5/c171e4d8c44fec1422d801a6d2e5d7ddabd733eeda505c79730ee9607f07/google_crc32c-1.8.0-pp311-pypy311_pp73-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:87fa445064e7db928226b2e6f0d5304ab4cd0339e664a4e9a25029f384d9bb93", size = 28615, upload-time = "2025-12-16T00:40:29.298Z" }, + { url = "https://files.pythonhosted.org/packages/9c/97/7d75fe37a7a6ed171a2cf17117177e7aab7e6e0d115858741b41e9dd4254/google_crc32c-1.8.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f639065ea2042d5c034bf258a9f085eaa7af0cd250667c0635a3118e8f92c69c", size = 28800, upload-time = "2025-12-16T00:40:30.322Z" }, +] + +[[package]] +name = "google-resumable-media" +version = "2.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "google-crc32c" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/76/f5/f35505e6091614e285056a495488cb0a9c1a9dcc88a4a3c91bbc5fd4835b/google_resumable_media-2.10.1.tar.gz", hash = "sha256:224975032ddb73f7ed9e2f0f4cc08ed1b06874c52d48cc8533e3eb72980b21a0", size = 2164548, upload-time = "2026-08-06T06:24:50.489Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ac/ba/77ef49baf338c03a11deadac984e3161b3f2b4fa4bb5aab160e7ca0fd522/google_resumable_media-2.10.1-py3-none-any.whl", hash = "sha256:4e2cbc704207ddc09f23b1f18e8ef4a4ccbfe0f1768b370e5c969704adbd0a1c", size = 81533, upload-time = "2026-08-06T06:23:45.464Z" }, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.75.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/73/74bcab964c9a7a61f2bb71e8179b0f13e6fa98f7ce00fd168aab291e4a2e/googleapis_common_protos-1.75.1.tar.gz", hash = "sha256:d3042c6c5a2d4e67113104d6b6818b59b6bd92a197f2a91508e801fe815cf071", size = 150967, upload-time = "2026-08-06T06:24:51.972Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/51/186c02b8549b69ccda44429cf6ff5081e4b61a602ddfe6a8020d1be31d1b/googleapis_common_protos-1.75.1-py3-none-any.whl", hash = "sha256:28a1934bcd33b9c9da66ac301a0a4227e3367f095a17d0375cb98f0a09d93b79", size = 300626, upload-time = "2026-08-06T06:23:46.696Z" }, +] + +[[package]] +name = "idna" +version = "3.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" }, +] + +[[package]] +name = "impi-rt" +version = "2021.18.1" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/35/6ef32a71394233a996ca56c15c1e4218040367a0758bf2fd0868fe34347f/impi_rt-2021.18.1-py2.py3-none-win_amd64.whl", hash = "sha256:a4ec3c4b6bda016db573b2505a4d7c59ddfb0c94553eead301da917891c7b6ab", size = 21855822, upload-time = "2026-07-14T12:41:35.392Z" }, +] + +[[package]] +name = "isodate" +version = "0.7.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705, upload-time = "2024-10-08T23:04:11.5Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320, upload-time = "2024-10-08T23:04:09.501Z" }, +] + +[[package]] +name = "jmespath" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d3/59/322338183ecda247fb5d1763a6cbe46eff7222eaeebafd9fa65d4bf5cb11/jmespath-1.1.0.tar.gz", hash = "sha256:472c87d80f36026ae83c6ddd0f1d05d4e510134ed462851fd5f754c8c3cbb88d", size = 27377, upload-time = "2026-01-22T16:35:26.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/2f/967ba146e6d58cf6a652da73885f52fc68001525b4197effc174321d70b4/jmespath-1.1.0-py3-none-any.whl", hash = "sha256:a5663118de4908c91729bea0acadca56526eb2698e83de10cd116ae0f4e97c64", size = 20419, upload-time = "2026-01-22T16:35:24.919Z" }, +] + +[[package]] +name = "joblib" +version = "1.5.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/f2/d34e8b3a08a9cc79a50b2208a93dce981fe615b64d5a4d4abee421d898df/joblib-1.5.3.tar.gz", hash = "sha256:8561a3269e6801106863fd0d6d84bb737be9e7631e33aaed3fb9ce5953688da3", size = 331603, upload-time = "2025-12-15T08:41:46.427Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/91/984aca2ec129e2757d1e4e3c81c3fcda9d0f85b74670a094cc443d9ee949/joblib-1.5.3-py3-none-any.whl", hash = "sha256:5fc3c5039fc5ca8c0276333a188bbd59d6b7ab37fe6632daa76bc7f9ec18e713", size = 309071, upload-time = "2025-12-15T08:41:44.973Z" }, +] + +[[package]] +name = "khiops" +source = { editable = "." } +dependencies = [ + { name = "khiops-core" }, + { name = "pandas", version = "2.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "pandas", version = "3.0.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "scikit-learn", version = "1.7.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "scikit-learn", version = "1.8.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] + +[package.optional-dependencies] +azure = [ + { name = "azure-core" }, + { name = "azure-storage-blob" }, + { name = "azure-storage-file-share" }, + { name = "khiops-driver-azure" }, +] +gcs = [ + { name = "google-cloud-storage" }, + { name = "khiops-driver-gcs" }, +] +s3 = [ + { name = "boto3" }, + { name = "khiops-driver-s3" }, +] + +[package.metadata] +requires-dist = [ + { name = "azure-core", marker = "extra == 'azure'", specifier = ">=1.39.0,<2.0.0" }, + { name = "azure-storage-blob", marker = "extra == 'azure'", specifier = ">=12.28.0,<13.0.0" }, + { name = "azure-storage-file-share", marker = "extra == 'azure'", specifier = ">=12.24.0,<13.0.0" }, + { name = "boto3", marker = "extra == 's3'", specifier = ">=1.17.39,<=1.35.69" }, + { name = "google-cloud-storage", marker = "extra == 'gcs'", specifier = ">=1.37.0" }, + { name = "khiops-core", specifier = "==11.0.1" }, + { name = "khiops-driver-azure", marker = "extra == 'azure'" }, + { name = "khiops-driver-gcs", marker = "extra == 'gcs'" }, + { name = "khiops-driver-s3", marker = "extra == 's3'" }, + { name = "pandas", specifier = ">=2.3.3,<4.0.0" }, + { name = "scikit-learn", specifier = ">=1.7.2,<1.9.0" }, +] +provides-extras = ["s3", "gcs", "azure"] + +[[package]] +name = "khiops-core" +version = "11.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "impi-rt", marker = "sys_platform == 'win32'" }, + { name = "openmpi", marker = "platform_system in '[Linux, Darwin]'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/4e/0fe3e79359f061b7b3baf9936a85fb555a6e96f273a88f62b79111a2bf16/khiops_core-11.0.1-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:b095c6b2f63462702e7e2148bef83dbc6bc7df3f0e08bba1738aa03509f74043", size = 4281296, upload-time = "2026-07-01T13:20:52.966Z" }, + { url = "https://files.pythonhosted.org/packages/75/50/8b1f4f3aa83ddd2181a54478d244682f10f657586a032f2d1cfb81ce4625/khiops_core-11.0.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:19fa6c2c05c5719ad0e8196c733e82abeb4d2f8893821631a1e6aa6421021d3d", size = 3859039, upload-time = "2026-07-01T13:20:55.033Z" }, + { url = "https://files.pythonhosted.org/packages/0c/84/8861ecf4b666ebcbf59ae2babe01449d4a936cc7fc8863878921c52ddfdc/khiops_core-11.0.1-py3-none-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e8496f7fed7aa7483b178dacbe1550a01b7f1ec729497d8e0ffe207d03628373", size = 5777644, upload-time = "2026-07-01T13:20:56.841Z" }, + { url = "https://files.pythonhosted.org/packages/dd/f9/4828f05af9899701a01047eb72d204e027ea5a802acb22a4fa7cc99784b2/khiops_core-11.0.1-py3-none-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74e2f3a34f6e3de6a2e0c68ca171779afa073efb8491aedc5b961a8a361ca827", size = 5996423, upload-time = "2026-07-01T13:20:58.589Z" }, + { url = "https://files.pythonhosted.org/packages/69/d5/8b79a5ea24c14095de98c2cd210706ac84e03835c6b96611eeed9b06a24b/khiops_core-11.0.1-py3-none-win_amd64.whl", hash = "sha256:43d8885ba37c67132b2decf72561877cf05e505da8878f7389400937e4fef262", size = 3983561, upload-time = "2026-07-01T13:21:00.431Z" }, +] + +[[package]] +name = "khiops-driver-azure" +version = "0.0.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/16/b757c76414f5a9149d41b8cb08875adc199acc96fb1723e62c660f3311cc/khiops_driver_azure-0.0.18.tar.gz", hash = "sha256:c2bbe282fba224b59cc5d6771ed8b19dca7655891c852a3a0470cb1f6633d9a5", size = 8118942, upload-time = "2026-07-03T10:09:18.562Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/7a/14bf58693b732b265a35e2f9183c476fea31d6bd4e8b9d5d5c7ad47b95e4/khiops_driver_azure-0.0.18-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7527015444aad49d11b425e9662b0c90136ca554acffc9280f0786db3ccd3789", size = 4579194, upload-time = "2026-07-03T10:09:11.257Z" }, + { url = "https://files.pythonhosted.org/packages/4d/f5/8ab81bde71128b40959b86298b85ff90c9e925b5cc77140f3ecc1d6dcfb8/khiops_driver_azure-0.0.18-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:701f379a9bf142f26a59c7c60e45f6eac578645f07440740625b3036628bd1f9", size = 5564762, upload-time = "2026-07-03T10:09:13.343Z" }, + { url = "https://files.pythonhosted.org/packages/1d/8e/e3817dde95bfce6ff8a382b7b5ef498f23fa9edd03540c1aa56e306c9be4/khiops_driver_azure-0.0.18-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:a0ba040d264201560ad481b58c7cbc07c072b67f49e3eea41158d03a509f3191", size = 5565935, upload-time = "2026-07-03T10:09:15.062Z" }, + { url = "https://files.pythonhosted.org/packages/cd/3f/77c2b63c2befa31d78459e2bccdef4e1b9c125388fad77c526813a637861/khiops_driver_azure-0.0.18-py3-none-win_amd64.whl", hash = "sha256:88d0beb3a96e2990e82eab134c521b4143d0b39e64a48c0f2c54ca7460123738", size = 831160, upload-time = "2026-07-03T10:09:16.878Z" }, +] + +[[package]] +name = "khiops-driver-gcs" +version = "0.0.23" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/cb70be9348bde59b1f5bbca852aad39618567489575c6be5fb0d2ac227eb/khiops_driver_gcs-0.0.23.tar.gz", hash = "sha256:ab969d44450664ef51728f29c04db798f079589ab486b788e12b1a8b06e83b2a", size = 4602334, upload-time = "2026-07-03T10:18:34.284Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/8a/35fcea47c39df3c20f9282be63a15a26cd39d70469fcd21c6a51a3670e8c/khiops_driver_gcs-0.0.23-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6850a115e8a6ccf91f6b48952395ed32a31e0a0b0b1c96561bb7898f2b58c4d4", size = 3944358, upload-time = "2026-07-03T10:18:27.576Z" }, + { url = "https://files.pythonhosted.org/packages/a5/80/6d05c9d4f6773da1399fbbd2b69cb236d44e76900abbd5768d7b8617b4b4/khiops_driver_gcs-0.0.23-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:c5908192ac39ef9d3f60b1ed8415df7ab489052a384d50946374295c877d7a30", size = 5017144, upload-time = "2026-07-03T10:18:29.348Z" }, + { url = "https://files.pythonhosted.org/packages/e1/ee/5f444660e648ee77d1ebc69283956c7d224ff4912777e6a498c4a69b07a6/khiops_driver_gcs-0.0.23-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:90dddd21f8e9edf6344a9dd8f06e8fb7624c81a763e65c59a919d16469bf677d", size = 5012734, upload-time = "2026-07-03T10:18:31.143Z" }, + { url = "https://files.pythonhosted.org/packages/8f/bd/4fcdb33774a391a94f84cab29ad9bd57d1cc5fc3f21636931b6879d3025a/khiops_driver_gcs-0.0.23-py3-none-win_amd64.whl", hash = "sha256:4fbbab54c2374cf144d633db438878a4c4a9c1e4521ccae263a72cc2323d6d5c", size = 1357791, upload-time = "2026-07-03T10:18:32.701Z" }, +] + +[[package]] +name = "khiops-driver-s3" +version = "0.0.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/91/213942b0c570dc30908730b06d9fe655f66ab9982e38e292b8c0d2b4d22f/khiops_driver_s3-0.0.25.tar.gz", hash = "sha256:cfe345ae0ff53fcd1783f130470dad8ef9d9855dbc4f2781d495d5ceb0795524", size = 4629195, upload-time = "2026-07-03T10:16:18.11Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/55/e7e79eca00e0ff7f018f33905e7b7b898767e3d836162c9b4bd9effbb5a8/khiops_driver_s3-0.0.25-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6c8b95cdfb8bda7fa10f92e529ec28f0365d9e2b2b2c6cef8ea9e46e090c5df6", size = 4509876, upload-time = "2026-07-03T10:16:11.036Z" }, + { url = "https://files.pythonhosted.org/packages/ed/80/4e94bf0e4b2011b9171587b5753b3ad4eea3c001bcaeb48263bfb6cb1e79/khiops_driver_s3-0.0.25-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:b34f74443ac71463b49978617d3ed982d038f30192418d1f60298cae2128fca7", size = 5507751, upload-time = "2026-07-03T10:16:12.86Z" }, + { url = "https://files.pythonhosted.org/packages/72/8f/5c683aa36bc173557b34c3a780958562837c62cdd544187aa0d6a0b1d448/khiops_driver_s3-0.0.25-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:6b772a93b176f051c915656e6d92e1a13510ca496585d9d8875be854d12920a1", size = 5491593, upload-time = "2026-07-03T10:16:15.071Z" }, + { url = "https://files.pythonhosted.org/packages/1c/df/fbf7a07d848cc07276c771be2805830a0c132394656128b0e1cd01d7d846/khiops_driver_s3-0.0.25-py3-none-win_amd64.whl", hash = "sha256:b9c7a5e36fe744df88e90599e3d5bed776c62215e30ed87195902edf0711c997", size = 2416988, upload-time = "2026-07-03T10:16:16.669Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d0/ad/fed0499ce6a338d2a03ebae59cd15093910c8875328855781952abf6c2fe/numpy-2.4.6.tar.gz", hash = "sha256:f3a3570c4a2a16746ac2c31a7c7c7b0c186b95ce902e33db6f28094ed7387dda", size = 20735807, upload-time = "2026-05-18T23:37:14.07Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/49/ec46835a70be8fa6446c495126ac84fdb28cb2558e1620ffb87a10c8b64c/numpy-2.4.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0280e0356c0829a18d9de1cb7eee50ec22ca639878d7240307ca0943d73cd2c4", size = 16969194, upload-time = "2026-05-18T23:33:13.503Z" }, + { url = "https://files.pythonhosted.org/packages/0e/0d/f5957185c0ee2f3e12f78715aa9e3b353fd83633316c8532b38faa37e3f6/numpy-2.4.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:110f8b71aacb688ec69062bb7f6938a0f8acb01b7c1c4beb453c65b6d234584d", size = 14964111, upload-time = "2026-05-18T23:33:17.795Z" }, + { url = "https://files.pythonhosted.org/packages/ad/40/40a40ee0ddf7ceb782c49af278894b686e586d65d8c1889c8b5da01a3d7d/numpy-2.4.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:4cfe66903cc32a9921a6733d96b19bb6abf310397581bbad89c228f5abaf0ee8", size = 5469159, upload-time = "2026-05-18T23:33:20.654Z" }, + { url = "https://files.pythonhosted.org/packages/63/13/f9a8046535cb21deae82f8d03de9617e08882d274fad2539630761888228/numpy-2.4.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8155154c7c691289fe18f510b5d4657c68c67989f293f0535a91360392ff6538", size = 6798936, upload-time = "2026-05-18T23:33:22.987Z" }, + { url = "https://files.pythonhosted.org/packages/33/a8/6fa8c1a345a8c85dbb21932c447bee07c30a2c2a3f31e369c0a84b300147/numpy-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab0a9c4ffb1a6d95ef519fe4247dba8eb6b18ad93999f76b7f657039acabd47", size = 15966692, upload-time = "2026-05-18T23:33:26.62Z" }, + { url = "https://files.pythonhosted.org/packages/02/03/74fe2a4cb3817d94d86402f2506554130a2f01414e299b5a843e5a8a957f/numpy-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:89cd468399cfd2504718f0ba50e410dca55a170b61a02ad92bb18c8a65186e93", size = 16918164, upload-time = "2026-05-18T23:33:29.955Z" }, + { url = "https://files.pythonhosted.org/packages/c5/80/3615be3313f7e7696609bc194b9f0101da809df79e859bdb84e0cd043f46/numpy-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c2d37ab77531417474168eb79d6d80b14f821a966818505d03013d0833edb7a8", size = 17322877, upload-time = "2026-05-18T23:33:34.724Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ac/a691e0fe2675e370d0e08ff905adc49a1c8830e8cae03efe4477e92cd55d/numpy-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f407cb6b8e9d6d8c626bc73c945db1706035af8fd632295547bf1c9e46d092d6", size = 18651487, upload-time = "2026-05-18T23:33:38.217Z" }, + { url = "https://files.pythonhosted.org/packages/15/a7/9bc1cd626d7bf6869bfedf27b91b6ab5dd607758bf8e959d6fa80c6a59cb/numpy-2.4.6-cp311-cp311-win32.whl", hash = "sha256:ddea102b48f9e339f3948bf22040944184627a30fdf7f858667673b9c5f033c8", size = 6233945, upload-time = "2026-05-18T23:33:41.331Z" }, + { url = "https://files.pythonhosted.org/packages/c5/31/7fc6239c12bce7e931463251cca4426c465e1876ba3cc785402ef4dd8f4e/numpy-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:1e254a00cdf42b1e4d5b3d68d33af63268d41340d8885df2ab6470f2e1500147", size = 12608406, upload-time = "2026-05-18T23:33:44.131Z" }, + { url = "https://files.pythonhosted.org/packages/27/83/140f85a466595a16382996a1bf06b2b54bcd597488921b0c9daaeeda72af/numpy-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:ed9749eef4cbd126da3dc1d6bcb3a57f5eb7ac6a6484146bdbf743f552dfc577", size = 10479528, upload-time = "2026-05-18T23:33:50.725Z" }, + { url = "https://files.pythonhosted.org/packages/95/2a/3d7b5ac8aac24feaf9ad7ed58f45b0bbc06d37e4338ae84c9f2298b570f9/numpy-2.4.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:001fbb8e08d942dd57599e781f2472269ee7f2755fae407b4f67b2f0b17da3f1", size = 16689119, upload-time = "2026-05-18T23:33:54.065Z" }, + { url = "https://files.pythonhosted.org/packages/ea/12/92c4c131527599e8288d6918e888d88726f84d805d784b771f32408aeaef/numpy-2.4.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ebfb099f8dcf083deef3ac1ca4c1503f387cf76296fcb3816b66f5ecb5f54fdb", size = 14699246, upload-time = "2026-05-18T23:33:57.621Z" }, + { url = "https://files.pythonhosted.org/packages/ad/fe/c0a6b7b2ca128a8fb228575147073b660656734b8ebe4d76c8fd748dcc79/numpy-2.4.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3213d622a0283a39a93d188f3cf72b26862df52fbb4ca3697f51705016523d41", size = 5204410, upload-time = "2026-05-18T23:34:00.302Z" }, + { url = "https://files.pythonhosted.org/packages/f3/d4/9770d14ba719432bb90a421bfd443872ed0f70f7264b64bec12ea363d5fd/numpy-2.4.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:357cc07a6d7b0b182ff02249616a03742827ebb1277546b5c7cd7f7620a45698", size = 6551240, upload-time = "2026-05-18T23:34:02.852Z" }, + { url = "https://files.pythonhosted.org/packages/c9/c6/50a46a6205feba2343f1d6d17438107c5dc491ed1c736e6ea68689fd906b/numpy-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f9fb9157b4ce2971008323afe46053787b526ef624fea915b261468a8421a0f", size = 15671012, upload-time = "2026-05-18T23:34:05.485Z" }, + { url = "https://files.pythonhosted.org/packages/99/60/14115e6364fa676c5397c2ad3004e527e9aa487abf5d0706ec81bbd08529/numpy-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:90f9849678c75fe7afa2d348ac842c168b0a4d3d61919687216dfc547976d853", size = 16645538, upload-time = "2026-05-18T23:34:09.265Z" }, + { url = "https://files.pythonhosted.org/packages/ae/c5/693cbe59e57db94d2231fa519ca3978dc9e19da5a8f088588f5c6e947ff2/numpy-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c1a2af6c6ef86344a6b0db6b97834208bf598db514f2b155042439b62605601a", size = 17020706, upload-time = "2026-05-18T23:34:13.053Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fc/85b7c4eff9b4966ade25c2273cf7e7012e92366c032058653934b37de044/numpy-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e5805d5a22fd19c8ccff10a9561f9df94436b0545619ea579db2d3c35294bce2", size = 18368541, upload-time = "2026-05-18T23:34:17.024Z" }, + { url = "https://files.pythonhosted.org/packages/f6/81/e1b27545deedce7f4a0b348618c6b62d74e36a4dc9ccd42f3eb2f85eee32/numpy-2.4.6-cp312-cp312-win32.whl", hash = "sha256:e3eeb0aabd6bd5ce64faae67e9935203a6991b4bc2a485a767fbafb2c5125f45", size = 5962825, upload-time = "2026-05-18T23:34:20.3Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ca/feab00bd44aa5fe1ad2c18f08b4d3bb92e26484b0b1d1443897809ed528c/numpy-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:d8e8286dd7cea7895157318d1b91cdacac64c479f3cbc8dce548331728484751", size = 12321687, upload-time = "2026-05-18T23:34:23.095Z" }, + { url = "https://files.pythonhosted.org/packages/63/cf/5a6d34850a39d1093558564f77ee8e8e0bee5061151b8f05a55711001ec7/numpy-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:4081eb135ac24158bd51cdfbef16f1c64df7063b1143f24731387137c092bec8", size = 10221482, upload-time = "2026-05-18T23:34:25.876Z" }, + { url = "https://files.pythonhosted.org/packages/fb/82/bdab26d7438c6791ca31b7c024ca37c1eab8b726ba236129005cd4a06e45/numpy-2.4.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:511dbaf848decaaaf4b4ca48032619fb3138710c4bf7da7617765edad1ef96b0", size = 16684648, upload-time = "2026-05-18T23:34:29.41Z" }, + { url = "https://files.pythonhosted.org/packages/1b/30/a80189bcc7f5e4258b3fbc3968d909d1756f54d023299ecc39ad6fdb9ef8/numpy-2.4.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf162abab1c1a736333192707cef898e735a5ca00f38f27eeedf44b39d9e85eb", size = 14693902, upload-time = "2026-05-18T23:34:33.013Z" }, + { url = "https://files.pythonhosted.org/packages/97/12/70b5d0d7c15e1ebb8a6a84a8caa1d19e181d84fb58bb6d70aca29099dec1/numpy-2.4.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:043191bfa8eab18c776647b62723ac9dddece59743b13f49b2016094129c2b3f", size = 5198992, upload-time = "2026-05-18T23:34:36.132Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8c/ebd2a8f8a83541f8d38cc5667e8c2b69cecfd30da6e45693e8158857d44b/numpy-2.4.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6180d8b35af935aed8ece3a85e0a43f87393ae0ac87c8d2c8bd2c993f7270ef3", size = 6546944, upload-time = "2026-05-18T23:34:38.484Z" }, + { url = "https://files.pythonhosted.org/packages/bb/c5/7b863a97a91671a0338f4253bd3b5a3d3852f0692dae91711c9f4a10e787/numpy-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72fbe16c6fac95aedf5937fa873445cec2110be35d8a4e9433d7501fd98dae6b", size = 15669392, upload-time = "2026-05-18T23:34:41.257Z" }, + { url = "https://files.pythonhosted.org/packages/a5/9d/3584b9984ca4c047aea75214ce1a4c4c73d849bd71b604264b7f5653f8a8/numpy-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a7830bab239b79cda9c08c2da014761cafb48da6150e1da17ac06283f43b6089", size = 16633220, upload-time = "2026-05-18T23:34:45.075Z" }, + { url = "https://files.pythonhosted.org/packages/05/ae/7c67fba23bd98caec7c99261f3a16072ade14813486b0282cb29846de832/numpy-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ef4aea96ce4d3b074422cb4f2f64e216bf9e213004bb58ecfdf50ea02ea8eb9a", size = 17020800, upload-time = "2026-05-18T23:34:49.065Z" }, + { url = "https://files.pythonhosted.org/packages/d9/5d/3b6725cb31d983c5e66916f5d36f6d7e5521129e4c4404d64f918292a5b6/numpy-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dfa20cc6ca228e6b155b11da03825975ce66aea520985dbbddf0f2a5a495c605", size = 18357600, upload-time = "2026-05-18T23:34:52.709Z" }, + { url = "https://files.pythonhosted.org/packages/f7/da/2ccc6c2fe8898dee01d90c75c5f5f914a23daf99e3e0f59516a08760c8b5/numpy-2.4.6-cp313-cp313-win32.whl", hash = "sha256:56b39e5e0622a09a25bf5baf62f4bcf0cb8a41ae6e2819cf49bbc5a74c083f91", size = 5961134, upload-time = "2026-05-18T23:34:55.618Z" }, + { url = "https://files.pythonhosted.org/packages/b5/cd/9cc4dc876fb065d5c220aae4d5e14826b2715331bb7618ce1fb07a679d99/numpy-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:c4fc99836233ea196540b17ab0983aff60ed07941751930f5f4d05bc3b3b7359", size = 12318598, upload-time = "2026-05-18T23:34:58.928Z" }, + { url = "https://files.pythonhosted.org/packages/39/1e/c0bcba1f8694116485fe28fd1be698c278fcda4141c5b0e53a2aed8b12a8/numpy-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a7c711e21628b52034bb5ab8d1bce291f752fcc5e92accc615778acee1ff4778", size = 10222272, upload-time = "2026-05-18T23:35:02.167Z" }, + { url = "https://files.pythonhosted.org/packages/63/6d/cc5619247c8f4204e507f5883528372e4ac4bb189e579fb859a12e480b1f/numpy-2.4.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:112b06a867b235ef466ed3508ddf0238050df9c727cafb5301ac385b899189a1", size = 14821197, upload-time = "2026-05-18T23:35:05.468Z" }, + { url = "https://files.pythonhosted.org/packages/00/58/f1c39161c87d9e9bed660f1ed4bafc0e403d5ec9650b6dd77aead07d489b/numpy-2.4.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:eaf7fa2de5c0be8ae6ff8e9bea2ccd725e980541244521d8d4b5f3354a27babe", size = 5326287, upload-time = "2026-05-18T23:35:08.693Z" }, + { url = "https://files.pythonhosted.org/packages/af/57/3917ab0fd97f271a8694513581b8a36c655f111c446852c302f04ccdb6fc/numpy-2.4.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7265a2f3d436e54ef9f2b52b5c937e6be778781bd97a590319d7348f1c1ca997", size = 6646763, upload-time = "2026-05-18T23:35:11.459Z" }, + { url = "https://files.pythonhosted.org/packages/eb/0f/037e64c494b67581ae18193d770adef354c41f3f2c8ebf865602d949bf8f/numpy-2.4.6-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f74a575920ab21fe304421a3fc28793d82e299cae9eccb37084e9fc7f3617c20", size = 15728070, upload-time = "2026-05-18T23:35:14.79Z" }, + { url = "https://files.pythonhosted.org/packages/21/a6/5d2bae9c9542eb4df16dc9c46dc79c186e9bad53805dfa5399a6023c6db0/numpy-2.4.6-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ede83e07a75dd06bc501566c1eca2afc0d61677c1472ac9ad93fdee6e638a48d", size = 16681752, upload-time = "2026-05-18T23:35:18.836Z" }, + { url = "https://files.pythonhosted.org/packages/92/14/23d1dfb410ae362cd59ce53e936b1513d545eb40db3949ced632e19a459e/numpy-2.4.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:68bb27509ac1b9a3443094260f6326150663b06abe40b73a2f81160623da5b67", size = 17086024, upload-time = "2026-05-18T23:35:22.52Z" }, + { url = "https://files.pythonhosted.org/packages/4b/6e/23595a2c642cdf3bc567877064bdd7f91c8b0038a4453cf2daf7248eafe9/numpy-2.4.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a0df0043bdb289bde1f62da130d20df23d58b45429f752bc7a8fc5325a225ecd", size = 18403398, upload-time = "2026-05-18T23:35:26.398Z" }, + { url = "https://files.pythonhosted.org/packages/8a/90/0ac3bc947217e66dec77e7cbc6a1979d1af70b6461b82f620d3bccd5e4c8/numpy-2.4.6-cp313-cp313t-win32.whl", hash = "sha256:29a287e0cf63ff528da061de6b9f64a4618da591ca1046aafc54062e40ca7eab", size = 6084971, upload-time = "2026-05-18T23:35:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/77/71/5673e351671a1d2bd6063b91b44f70c0affea7d1516fa7a6572941ba4aa1/numpy-2.4.6-cp313-cp313t-win_amd64.whl", hash = "sha256:25c692919ac5a01f170a3bfcd62d745b24fd095c353d50812637d6fcab442e75", size = 12458532, upload-time = "2026-05-18T23:35:32.175Z" }, + { url = "https://files.pythonhosted.org/packages/3f/88/19d3503c5046e688f049274b27a3ef3d771152fa80d3ba3d01a3dff61abe/numpy-2.4.6-cp313-cp313t-win_arm64.whl", hash = "sha256:1e978ec1e8bd0e0e4de6bb75de9d30cbb74db6b6a2bb727618613703ca0167dd", size = 10291881, upload-time = "2026-05-18T23:35:35.465Z" }, + { url = "https://files.pythonhosted.org/packages/f8/91/3ab2044d05fd16d343c5ac2e69b127f1b2854040dd20b193257c78028bd3/numpy-2.4.6-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06ca2f61ec4385a07a6977c55ba998a4466c123642b4a32694d3128fce18c079", size = 16683458, upload-time = "2026-05-18T23:35:38.353Z" }, + { url = "https://files.pythonhosted.org/packages/8e/62/764ce66fa4147ae6d73071a3abf804ffe606f174618697c571acdf26a7c9/numpy-2.4.6-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:38efbc8de75c7a0fc1ac190162d892787f3f47b57cc291231aafee36b80982b7", size = 14704559, upload-time = "2026-05-18T23:35:42.14Z" }, + { url = "https://files.pythonhosted.org/packages/60/61/23f27c172f022e04025b7dc2367f4d63c1a398120607ec896228649a6f48/numpy-2.4.6-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:d581b735e177fdcdce6fed8e7e8880a3fb6ee4e3653a3ac6af01c6f4c03effc5", size = 5209716, upload-time = "2026-05-18T23:35:45.377Z" }, + { url = "https://files.pythonhosted.org/packages/03/71/21cf70dc6ea3e3acb95fc53a265b2fc248b981f0194ceb5b475271b8809d/numpy-2.4.6-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:0a041d3d761dc3c35cc56ce0351506a02bcbc25f7b169f652435141a17db9096", size = 6543947, upload-time = "2026-05-18T23:35:47.926Z" }, + { url = "https://files.pythonhosted.org/packages/d5/91/64288395ee1799bd2e0b04a305dce9666da90c961e1f3fe982a05ee1c036/numpy-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40fdc1ae7125e518ea98e53e69a4ebc27e1fd50510c47b7ea130cf21e5e1d42b", size = 15685197, upload-time = "2026-05-18T23:35:50.863Z" }, + { url = "https://files.pythonhosted.org/packages/f3/eb/ebffaa97dc55502df69584a8f0dcf07f69a3e0b3e2323670a2722db9aa39/numpy-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2c306dea656c12c68f51f4cea133cbe78ca7435eb28c735eac1d3ebe73be6e8", size = 16638245, upload-time = "2026-05-18T23:35:54.752Z" }, + { url = "https://files.pythonhosted.org/packages/b8/0b/54f9da33128d7e350fab89c7455902eeae70349ee52bddb448dc4a576f45/numpy-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:33111801a01c12a8a1e3721f0a9232f8cfc8ae2c6b7098167e6f623c6073f402", size = 17036587, upload-time = "2026-05-18T23:35:58.355Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f0/fdebc1052db1cc37c64beb22072d67cd6d1c71adca1299f53dec2b5e20d3/numpy-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ae506e6902902557576a26ff33eda8695e7ecb3cb36c3b573a0765dee114ebdb", size = 18363226, upload-time = "2026-05-18T23:36:02.845Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b4/298628d98c72b57e57f7165ae6a481a1deaf6f3c28262a6e4c739c275930/numpy-2.4.6-cp314-cp314-win32.whl", hash = "sha256:aaf159caa35993cb1f56fb9b8e4610d35758e7ca005412eb1daa856a78c9c4b1", size = 6010196, upload-time = "2026-05-18T23:36:05.92Z" }, + { url = "https://files.pythonhosted.org/packages/df/ac/46de6dda46478f7942f839e094970be2d4a861e005c4b3bf07c92e291a09/numpy-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:b507f5c4c1d508876d1819b6bf9a49d365b96320b5d4993426b33a23ca4b8261", size = 12450334, upload-time = "2026-05-18T23:36:09.107Z" }, + { url = "https://files.pythonhosted.org/packages/78/92/b8b798ac784102c0da830d2257d59358e3d3d90d1e2b3f2575dad976c5cf/numpy-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:6f41ae150c4e32db4f3310cdaf64b1593a03dbabe29eec77fc9b50fe64061df6", size = 10495678, upload-time = "2026-05-18T23:36:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/30/34/ec28d1aa8115971537c01469ab2011ee96827930f0a124de1000cc2a7ed7/numpy-2.4.6-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ece3d2cfe132e7d51f44a832b303895e6f2d499c5e74dfbdb06ee246147a304a", size = 14823672, upload-time = "2026-05-18T23:36:16.473Z" }, + { url = "https://files.pythonhosted.org/packages/16/bd/f6d1fede4e54e8042a7ff97bb495510f3c220f94bcd9e8b228e87c92cc0d/numpy-2.4.6-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:e3e5193ef5a3dc73bceee50f7fdc2c90dbb76c42df8d8fae3d1067a583df579e", size = 5328731, upload-time = "2026-05-18T23:36:19.767Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f0/e105b9e2fd728a9910103884decd6951d9dd73896b914a98d9a231de02ee/numpy-2.4.6-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:17f9ade344e7d9b464a084d69bcf18fc691cb1db67c62ed80820bf4926d78f0e", size = 6649805, upload-time = "2026-05-18T23:36:22.266Z" }, + { url = "https://files.pythonhosted.org/packages/82/dd/1206a7ca6ab15e3f02069707ca96222e202af681bb73756da7527f3cb837/numpy-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cd5ffd25db4e7ba6a375693b3fc0fc1791ec636c17db3720da19bde7180ec43", size = 15730496, upload-time = "2026-05-18T23:36:25.713Z" }, + { url = "https://files.pythonhosted.org/packages/51/e7/38d3ea825dcab85a591734decb2f6c67caa7c8367d374df1a1c3842f9b07/numpy-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7d92c3819208a60205a12a245c91ad70cb0a85336659b19b834205573ac8456e", size = 16679616, upload-time = "2026-05-18T23:36:29.652Z" }, + { url = "https://files.pythonhosted.org/packages/93/b7/caabfdf53edf663e0b4eb74d7d405d83baef09eb5e83bcd32d601d72b93e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e85b752a1e912b70eaad4fafbd4d1238007ab221de2009b9a2f5ae7461239895", size = 17085145, upload-time = "2026-05-18T23:36:33.449Z" }, + { url = "https://files.pythonhosted.org/packages/f9/45/68d7c33a6bcf3e5aa3bdbd57a367e6f615286dfd6482f97e8ffeb734306e/numpy-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:29cb7f67d10b479ff07c17d33e39f78c07f71c40ef30d63c153d340e96cd3fb4", size = 18403813, upload-time = "2026-05-18T23:36:37.369Z" }, + { url = "https://files.pythonhosted.org/packages/9c/50/0753655aa844c99cd9e018aacf76f130f1bd81d881bb74bc0aef5d73a8ba/numpy-2.4.6-cp314-cp314t-win32.whl", hash = "sha256:260a5d70215b61ab4fadf5c7baacd64821842975eea312125ed3c39a6391b063", size = 6156982, upload-time = "2026-05-18T23:36:40.817Z" }, + { url = "https://files.pythonhosted.org/packages/b2/d4/7c67becf668f973cb490cec3e98dfd799d866f9c989a54d355672cfa0db6/numpy-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:81a1cca95ed5bb92aa8b10dd2cdc9a0d3853a50fad926c28b5d7e8ea54389627", size = 12638908, upload-time = "2026-05-18T23:36:43.996Z" }, + { url = "https://files.pythonhosted.org/packages/43/bb/e1c71a4295b1b1d1393d50dbb4f2a36283c6859d9d3892e84f00ec5a91d5/numpy-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:0c9136e14ed34a9e343a31c533d78a9813a69a3148332bce5e9821cb2f996e66", size = 10565867, upload-time = "2026-05-18T23:36:47.114Z" }, + { url = "https://files.pythonhosted.org/packages/de/12/b422cc84439adc0d00de605bf4a308890ae5c26f2c71fbd73e5d08fbb0dd/numpy-2.4.6-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:55cced7c52e981362f708ad635198e97a752dfba412cc03c23bbf3bd8d5cd662", size = 16847511, upload-time = "2026-05-18T23:36:50.673Z" }, + { url = "https://files.pythonhosted.org/packages/44/53/f481bef68011740f8849418d82db07230e825013f31f4eef5ba5b805316a/numpy-2.4.6-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d6da64deb6b8ed903e7560180a92f2d804ee1ba5eeb849ac2748b8c1aba1f6d7", size = 14889064, upload-time = "2026-05-18T23:36:53.879Z" }, + { url = "https://files.pythonhosted.org/packages/7f/57/42ed575c10ced8af951d426bc4e1f8aff16fd851db33f067036215a7f860/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:68a5124b13fa6cc2086764a20005d30bc0548146f7f5322f02fce212ca14317f", size = 5394157, upload-time = "2026-05-18T23:36:57.194Z" }, + { url = "https://files.pythonhosted.org/packages/6a/ef/f66cc724fcc36c1e364c67f51ae9146090b8b584f27d58b97fdae3edd737/numpy-2.4.6-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:948424b06129ce883307e8cff868c31396d8dc7630a59c61d70d98dbe70f222c", size = 6708728, upload-time = "2026-05-18T23:36:59.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/9c/c531f2293b91265d8b48e9b329f54fdd7ffae73cb4134ea10cca4237e9cc/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbbdb29840ca3d91ee0fece42fc29278886d908280bfec0a5846c6f901a3eb0", size = 15798374, upload-time = "2026-05-18T23:37:02.674Z" }, + { url = "https://files.pythonhosted.org/packages/1a/b0/413077f6b1153ed3cba361401c6783bbad6114804a000cc22eb71c13e190/numpy-2.4.6-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ad03c0965fb3c692200e74d458ca28c1dbb4ce96f9a479a8aa041ad5fabca02", size = 16747286, upload-time = "2026-05-18T23:37:06.327Z" }, + { url = "https://files.pythonhosted.org/packages/15/ce/e5ec180bc41812edcd8daeb8639d205622c0e8c02259d8ab25a0201b3c2a/numpy-2.4.6-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2803abfebfc990042cd494d8ce2d5f82e9d847af6d35ec486923aa19dbad5e73", size = 12504263, upload-time = "2026-05-18T23:37:09.715Z" }, +] + +[[package]] +name = "numpy" +version = "2.5.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +sdist = { url = "https://files.pythonhosted.org/packages/9a/80/db0b4559e57ec36362bedbb05530a87fafbcb6067708c946967a41d449e7/numpy-2.5.2.tar.gz", hash = "sha256:d482d171c406ae88c5b19cad3b6a1c4c5209f886ab74bc44c2c865c23f52d860", size = 20773161, upload-time = "2026-08-09T13:48:27.962Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/72/dccb0aaf40972777283303919f613964227266d0c13adebb79ac124f1c3e/numpy-2.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:14e373cfc6387177e8409dac3c7159be8eb05cd77096cd7c950268b86f62831c", size = 16891693, upload-time = "2026-08-09T13:44:51.702Z" }, + { url = "https://files.pythonhosted.org/packages/60/2e/b5aee50a1f74ac815cf8331812cb8251e29024025de462e0c047641c614c/numpy-2.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4bbd96c833ecc8cc069ce518078fc8c60cb9cbfb0fea5b7a803ad65035596d03", size = 11903109, upload-time = "2026-08-09T13:44:55.501Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f4/29e78102a80601cf034d4e9767022cffeca2c3b4c926e1754572ca95593d/numpy-2.5.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:6e8172ddfcf5cf74b811d372b570b83c60bd2de87a6fbfbebdadb4a9bd9c6cbb", size = 5350202, upload-time = "2026-08-09T13:44:58.401Z" }, + { url = "https://files.pythonhosted.org/packages/11/4b/dcd3b7eadaf4035d2c7a4289d232523a6964f602598ef7674e4bd7291f93/numpy-2.5.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:65f188481f1669e26f62b701e8205d19e460fa4a9b52a1414ba382330e4a3414", size = 6687736, upload-time = "2026-08-09T13:45:00.813Z" }, + { url = "https://files.pythonhosted.org/packages/e5/21/4947e0e9d6c9fc2e2ff15b8949049ee44f63adb9cacc729ab8793f97e712/numpy-2.5.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8ee9c4eeb8454b3660a8b53493563c3e121c2fc94fbd72b848ef814ed7b676a9", size = 15612696, upload-time = "2026-08-09T13:45:04.151Z" }, + { url = "https://files.pythonhosted.org/packages/3a/5f/62d28cf019460c7f1394105b4d49d9911a9c444cb77ab0bd95a204c5a6de/numpy-2.5.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3cdec01fa790a186d430433fdd4d4ffb70eed6f0eeb4bf05c8dbe2dce0a9bcb8", size = 16722264, upload-time = "2026-08-09T13:45:07.714Z" }, + { url = "https://files.pythonhosted.org/packages/14/25/3f0be4c1b9fdf5dd5e708a6806978564d7c46a055c000496309ff2a2f8af/numpy-2.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7999d4ddb0c4025018373fd787510d46e04c769467af22869707b3c1cfd459ab", size = 16974396, upload-time = "2026-08-09T13:45:11.316Z" }, + { url = "https://files.pythonhosted.org/packages/22/72/6262cbdeeb45da9d971e40715f579d791603ba8ec0b5e2db1ac55454421d/numpy-2.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c1f017dc0875c9209d219f97feceb7d54c2661bb243deb4114478e1295808af7", size = 18476044, upload-time = "2026-08-09T13:45:14.869Z" }, + { url = "https://files.pythonhosted.org/packages/36/33/29208b8b075bde62d26a81d14b358c42b0f69b6cabd98d4ff97f37f22b05/numpy-2.5.2-cp312-cp312-win32.whl", hash = "sha256:d6a48072864e3324e194a8fbb3c657bcc5b5c869dbc64c9537b1d5c862572c0a", size = 6072817, upload-time = "2026-08-09T13:45:17.867Z" }, + { url = "https://files.pythonhosted.org/packages/7f/b9/87fea2769fe1c47c1b5b01d8310772c9d1a85d485de7cf386ef7a3332b02/numpy-2.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:28ac63476ec7651484215ee7fa15a1f78b57c14621f01e392afe17b9a1390ce4", size = 12464674, upload-time = "2026-08-09T13:45:20.734Z" }, + { url = "https://files.pythonhosted.org/packages/14/52/032b97e00461ab0809bbe4c588b035620e5a14b8cdee47ecddefc7b17d33/numpy-2.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:27650bb0e7140fa3d37b9923b4803645e0b125d190f326eecfd3f4dad8e8ade1", size = 10397131, upload-time = "2026-08-09T13:45:23.73Z" }, + { url = "https://files.pythonhosted.org/packages/f5/d2/6b24738a0ef4557d189b150046cd07823c50e4273e8aebd651222e24306f/numpy-2.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8e4cb9a754c8a0c62eaa88273a5fba3391f4a610d1dee893c0755da31c083f15", size = 16886595, upload-time = "2026-08-09T13:45:27.323Z" }, + { url = "https://files.pythonhosted.org/packages/65/60/f2d208d366f263f39c6e69ed309290717aab41078b6d04c9be2a84fa2a07/numpy-2.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:52c808f96484f5571a5cc863775ce50247c17dfb3b0361f8ed6b4b0456f80080", size = 11896845, upload-time = "2026-08-09T13:45:31.638Z" }, + { url = "https://files.pythonhosted.org/packages/3c/79/81e0bf24f4d020a2b1d5cd297a9f60c3f24eeb116f9bba5870443f7b6a4a/numpy-2.5.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:29d81e97f668489cba8ebfd796b9bdd453525d35dd9e162e2daec94bf3fc7740", size = 5343880, upload-time = "2026-08-09T13:45:34.373Z" }, + { url = "https://files.pythonhosted.org/packages/ba/cc/e3141cf06d1a8a2c7e107543fe1269c1d1af760d4d683c0794a4ee1127c2/numpy-2.5.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:afb3f0632d6b2e3ba04dbce8d1e48d321b369138b73830b5ca371a0e8d479d56", size = 6682264, upload-time = "2026-08-09T13:45:36.7Z" }, + { url = "https://files.pythonhosted.org/packages/29/f1/2a64a307d92c5d98f5255a4014eb43bb6103ee477087b61ecae44a3aa9b9/numpy-2.5.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0aadf13b60048d501e05fa699efaf7734e2494f3498a4c2a5521d822640324f3", size = 15609566, upload-time = "2026-08-09T13:45:39.518Z" }, + { url = "https://files.pythonhosted.org/packages/7b/44/59a1eb68e773c4098d107ef34a0dbdeca501d72ffcfbff9a7707343921ce/numpy-2.5.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29b86ff8a6cc556b47ec6b64b194815cc80e6bf5eedcc6cddfd65318cb0b4eee", size = 16709995, upload-time = "2026-08-09T13:45:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/8a/4c/3e54d4ddbc359a1295f8b633e8106bcd4d7d4a206e82df051bdfb3058755/numpy-2.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6950c4b7dd562453090548ba7f5da7e59f57f85663f15d5dcc60e249192f7e59", size = 16972511, upload-time = "2026-08-09T13:45:47.094Z" }, + { url = "https://files.pythonhosted.org/packages/f2/9f/02e371638ebf19b66d46231e4be52999e87f32d1961b113bc45656608b22/numpy-2.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9727f472d2f3888053b8a75ab0cb94745a9de224bb5846dbadc0092101bc71d", size = 18465609, upload-time = "2026-08-09T13:45:50.808Z" }, + { url = "https://files.pythonhosted.org/packages/eb/ae/ad6645abc7a3510fe48e8ea1ab4598166f500057ef4ebf38bfad4f1577de/numpy-2.5.2-cp313-cp313-win32.whl", hash = "sha256:4f9744f9fbdcea0bc552e8f19e1f141f811a3f9bc2be2cc6e86d982cab23e3f4", size = 6070204, upload-time = "2026-08-09T13:45:54.111Z" }, + { url = "https://files.pythonhosted.org/packages/15/20/f3489f86d81ea460b2bcdceaed094142ca6579f6be0ec527b781d39afe68/numpy-2.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:85aaccb24182c25df891ad0ec333585967e115269d5f1b17f2c9ae005bc96657", size = 12460532, upload-time = "2026-08-09T13:45:57.167Z" }, + { url = "https://files.pythonhosted.org/packages/d5/21/35b31dde1b283b79de828b80f876afd8c94e28fe1e9c375f89e261cc4c0d/numpy-2.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:bd68ece1553d2023c09a4226d9e41c586ad2d20594d1a456186c33513d2cb3f2", size = 10396725, upload-time = "2026-08-09T13:46:00.478Z" }, + { url = "https://files.pythonhosted.org/packages/ac/f8/c3b222bf075b50afd8e949a07a15c4b312a4a84bd8102a332bcd953cbbb4/numpy-2.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d787cf769c3baeb5f6235e778edb52c08dfa923789b5958f28e6450f96107cb1", size = 16885180, upload-time = "2026-08-09T13:46:03.939Z" }, + { url = "https://files.pythonhosted.org/packages/17/e1/2c1d4b1987795a92b5bbf7c24fe249ab96aa2573ab0d7604802c189d7b86/numpy-2.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:24b9dc2e3d84aa58523798805194e23e736f3f6ce2d1a5b92583ae734e6dbda8", size = 11907878, upload-time = "2026-08-09T13:46:07.045Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ee/d08226fc858044355983a6e5b94f08ff6f3969e0a2b160a4a89f0ddb3445/numpy-2.5.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:9e9413326d726c2545bfa65d2c0876871e8d8386e77f992c1d426e180bbd4323", size = 5354922, upload-time = "2026-08-09T13:46:10.04Z" }, + { url = "https://files.pythonhosted.org/packages/94/f0/6d3d933056440ebbc5e6bad92065fc6c26a48a84a36b1208580e94eea76c/numpy-2.5.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:60e902ac295855348a5ca2ea4c89108989a9f5fddfad3dfc0a8f36b10358567e", size = 6679168, upload-time = "2026-08-09T13:46:12.275Z" }, + { url = "https://files.pythonhosted.org/packages/c4/3b/ecd49dd90033cceb2704d88ca905d4d7d89b0e8c739608754ffd325fa820/numpy-2.5.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:50e500dc868e9313530ce12ba470fe50ff3afe3d62993ed6eff652dacd555b65", size = 15624501, upload-time = "2026-08-09T13:46:15.322Z" }, + { url = "https://files.pythonhosted.org/packages/c7/99/461bd36dbdfac6c1c53efa370bd55a83227542d0d118f1677dbf1a3dacd5/numpy-2.5.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318b9a4c845dbea06708a29c84ee429cc3065048db34cdb799047643492050ee", size = 16713701, upload-time = "2026-08-09T13:46:18.949Z" }, + { url = "https://files.pythonhosted.org/packages/f9/9c/2b251df9e8a5d647b62b0cbc1b90a91850c1cf4859ecb532fd0b4eacff6c/numpy-2.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:34c319e2963be042673fb46570501b2f06c41924e17e3563d58646b4380dfb68", size = 16986065, upload-time = "2026-08-09T13:46:23.006Z" }, + { url = "https://files.pythonhosted.org/packages/8f/25/20de43f53ff1390534a124475055a19f01fe10c920a0fd11b8e18d6d6052/numpy-2.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f06571a052127dc1b4e8b83029b4d1b20daa2b64a31cdd181fc6bc774e9000eb", size = 18470031, upload-time = "2026-08-09T13:46:27.102Z" }, + { url = "https://files.pythonhosted.org/packages/56/5e/0c577ca308d6da5eb79b546ba10bbe5b60148192194e2da060913b1de4f1/numpy-2.5.2-cp314-cp314-win32.whl", hash = "sha256:2cc779226e476d1e1f08c74068c419e60f41a9e0e069c92f6671d31d5c985e98", size = 6121028, upload-time = "2026-08-09T13:46:30.046Z" }, + { url = "https://files.pythonhosted.org/packages/15/5c/7bcbd5b11f94199073320410cddcbb80cee62415bfeb540874b265c2d922/numpy-2.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:7587f53dfbd5edc0f7b87c6217b4c6d2d1f2ef9c3da70bc1315e7db5f8d7ec9d", size = 12597627, upload-time = "2026-08-09T13:46:32.886Z" }, + { url = "https://files.pythonhosted.org/packages/87/bc/4d0b06fba0da90ccc75af62823cb9dcedb6c9ea0cffa058cb2c9ee773a77/numpy-2.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:3e4c367352d3747784248a227fbec218e193b56f7e6692e3b64fc805478ecfdf", size = 10680414, upload-time = "2026-08-09T13:46:36.036Z" }, + { url = "https://files.pythonhosted.org/packages/cd/17/f429aac9dc08833a0d0f188eba38c532a751b1a1f2ca6018a37b455cb321/numpy-2.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b879fb674276e331513fb136b78dbc6bd3c848309e0d841cfd63be3896c4cfc1", size = 12026967, upload-time = "2026-08-09T13:46:39.084Z" }, + { url = "https://files.pythonhosted.org/packages/ca/9f/d0849de96a2a4ceaa16662f18ee13eaa9c0aa418269fdc8c4857c56b11da/numpy-2.5.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:fd0d703772bba096843785bd38371e31bb4a0c1151497ad5739d182114a73f7f", size = 5473874, upload-time = "2026-08-09T13:46:42.075Z" }, + { url = "https://files.pythonhosted.org/packages/89/3c/8df216d4a4a5422a3de045301cf7df8ea47286d76f5cb7160b0128ac26b7/numpy-2.5.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:3a2f061cebd9e3d23bdcfaaded5e2293a4c6a5b60fa42df85d410a725ce621bf", size = 6789276, upload-time = "2026-08-09T13:46:44.387Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3a/20d7e9891c4ddfadd6ff8d95bf4b29f353d8e1770553de2099880551dfb9/numpy-2.5.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6df895598c0edcb41030126c89e0f353b07d93238116143b7405e937359736c4", size = 15659154, upload-time = "2026-08-09T13:46:47.538Z" }, + { url = "https://files.pythonhosted.org/packages/aa/d6/f3aa3d2688bf501b858835c6bd087ae9b51a56ae6fca8e2b0990abd177af/numpy-2.5.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1ab3d4a901f844ea836c3e80bf463c6a27d7f3c14e8e292fcf28d348b25b9bce", size = 16748909, upload-time = "2026-08-09T13:46:51.442Z" }, + { url = "https://files.pythonhosted.org/packages/7d/8f/1c5cae8d2baf86ab802ae97a00be55bc7e21ebc11b12bbc33376c5f05342/numpy-2.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:cebc2d6dbb605a7703d59751dea4bd6b0ab127a5a4338a6f432df1936fef8b26", size = 17027685, upload-time = "2026-08-09T13:46:55.095Z" }, + { url = "https://files.pythonhosted.org/packages/5c/27/71d3467404aedc1c24ce79610f91b52b0b0f466c43a701aa56fc75c145ab/numpy-2.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:eaca7ff36f0f52e2111ec71f169d8fd3e889e7ddc0d2592e0d703fd8d3ce8fac", size = 18501181, upload-time = "2026-08-09T13:46:59.09Z" }, + { url = "https://files.pythonhosted.org/packages/14/2f/42921d27c40aea7e077f4a423ae509fd9220b028cd787bafefd8ab2b3a5f/numpy-2.5.2-cp314-cp314t-win32.whl", hash = "sha256:ddf47472af2e4280d79bac82304f5e80150211f1b9e614b760061d5fdfbb6eba", size = 6271085, upload-time = "2026-08-09T13:47:01.903Z" }, + { url = "https://files.pythonhosted.org/packages/75/e6/bad5f5d56de9b1971bac959963dda276d35c40f1854475005434bbe08692/numpy-2.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:44ef9675d908e65f9953063837c3277730f3f4437615a4cdab67b366cabaf884", size = 12787971, upload-time = "2026-08-09T13:47:04.963Z" }, + { url = "https://files.pythonhosted.org/packages/df/05/f608795cb34391acd67e38d94a3c36abd8d8576293a3a80727d7595c372c/numpy-2.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:eaa088384c46f519dacb93b7ec483a6d6b19a4a2085ae4f25ab9b1c43d387d1e", size = 10750306, upload-time = "2026-08-09T13:47:07.976Z" }, + { url = "https://files.pythonhosted.org/packages/33/c6/28de0191c5f82b7d42a0a51390ba98587048aa93a39fafb05bdbe6e8d00c/numpy-2.5.2-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:078f9b027b478c9379b9677babbf0f8b8f1ecfada27636d7b9a93990c638739f", size = 16885274, upload-time = "2026-08-09T13:47:11.439Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d1/973ca116000d244897e468ea1aff30b589e5022e3c8744b71706fe33bd57/numpy-2.5.2-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:50a68f4bacd8a2b33d8da3d2269d0d78500f86ea582e4786dc10f5ef2c2c6842", size = 11907846, upload-time = "2026-08-09T13:47:15.128Z" }, + { url = "https://files.pythonhosted.org/packages/78/d9/8c4b3937ef204cb2fd88d389ccd0f265a2ffb11f35a01d2064cf46714bd6/numpy-2.5.2-cp315-cp315-macosx_14_0_arm64.whl", hash = "sha256:e79aba74ffaf5f78a050d777c184cddf8fdffabab38acf5f3ef1fecbc17895d6", size = 5354892, upload-time = "2026-08-09T13:47:18.07Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/b6ee65ea2999fdb7023935e108e6fb776ee4082aa15f159acfa857e578c8/numpy-2.5.2-cp315-cp315-macosx_14_0_x86_64.whl", hash = "sha256:9a0731745a72a184490a582fb4af2533512bd071ace67785b5fdffc0ae58dce8", size = 6679309, upload-time = "2026-08-09T13:47:20.456Z" }, + { url = "https://files.pythonhosted.org/packages/43/f3/acb18d8b137a393c8e7803a8c994c9e64bde3930692a69d826993113a159/numpy-2.5.2-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4ec954036759bcee3aa484f8603bd9c14f3e776293b85578b8734c2d72777c69", size = 15625850, upload-time = "2026-08-09T13:47:24.365Z" }, + { url = "https://files.pythonhosted.org/packages/a9/bf/a8e9bb0db815a0e265b5744ebedd3af0bd5faad8604e5b50a1cd012f3c91/numpy-2.5.2-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc649493697006bc90614a5f0bbc8cb3cb1866715c474e473694968d7e6b99ab", size = 16713664, upload-time = "2026-08-09T13:47:27.965Z" }, + { url = "https://files.pythonhosted.org/packages/0c/c3/6e913736b3dd6582344af32418b5fb9dab34282e8a8174ae1d54ceb0fc13/numpy-2.5.2-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:cf7de32f486e4ac9e2d93b810f9e9ac72a728dd46a32a0bb403222f27f653514", size = 16986749, upload-time = "2026-08-09T13:47:31.541Z" }, + { url = "https://files.pythonhosted.org/packages/80/09/7d3b23eff5c7428ef6c01e6f7052bb60d504c4d33e317b36b8959c24ad97/numpy-2.5.2-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:2ffa7bacab3e2ee1b19ed31766bb60bb380b68c23f051e199c5cc598afd68710", size = 18470495, upload-time = "2026-08-09T13:47:35.364Z" }, + { url = "https://files.pythonhosted.org/packages/a5/a4/68a321d825374f6eb677ffe8ef8c6b9a328304e6fd2e39d9530822776607/numpy-2.5.2-cp315-cp315-win32.whl", hash = "sha256:6b588cc8f902d6bff201c19fd00c43ab8545671e3554d014e12e14139e5e8617", size = 6120696, upload-time = "2026-08-09T13:47:38.561Z" }, + { url = "https://files.pythonhosted.org/packages/c8/23/deafbb1700f79fae9cd1e91220f133d124cc267de1b584da3fbf6db2f6cd/numpy-2.5.2-cp315-cp315-win_amd64.whl", hash = "sha256:07d4e89f3a9ab0a9ba24264ccdb642b3dd951b2281e8883a5481a4aa79cc31a7", size = 12597324, upload-time = "2026-08-09T13:47:41.401Z" }, + { url = "https://files.pythonhosted.org/packages/33/cd/3272ba105e3bbbdaeb11357eda31e7a6825ffe159e8171665660299a948f/numpy-2.5.2-cp315-cp315-win_arm64.whl", hash = "sha256:a610dc7e3c52edd39c2bc2375ff9c3fd59cb3ad00e4472d36f83bc1457145788", size = 10680466, upload-time = "2026-08-09T13:47:44.873Z" }, + { url = "https://files.pythonhosted.org/packages/0e/0e/58370637b1bb70a5c9ce2b43f4b521ccb224e36ccb76a6596b17ae4b447c/numpy-2.5.2-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:40f4d451aed46a8046a1aae41c4e55fb3612273df9c502480135e1501576a34b", size = 16993947, upload-time = "2026-08-09T13:47:48.97Z" }, + { url = "https://files.pythonhosted.org/packages/10/93/2abcb807712b289d6d60fe4cf30532f98974a8396d885650f3ba5a13026e/numpy-2.5.2-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:c081cbe16ba1ab53078e5ff29013621e33c509eedab055775d956427712c236e", size = 12025331, upload-time = "2026-08-09T13:47:52.646Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3a/2898e003a5fbaf87e76c039b4ee1f5eb390471b4ffe74887c1f34c4e791e/numpy-2.5.2-cp315-cp315t-macosx_14_0_arm64.whl", hash = "sha256:0090ccdd57ec2703e9b49d0bf554767370581c1dd0a6b2bb2b2d9def317d042a", size = 5472336, upload-time = "2026-08-09T13:47:55.403Z" }, + { url = "https://files.pythonhosted.org/packages/61/a5/23f69d07c544597b29758b31b55c27dc9d541012a2c1496189fef702aec2/numpy-2.5.2-cp315-cp315t-macosx_14_0_x86_64.whl", hash = "sha256:6a9bb119fb8dd21ba30b3f0e555b7e2b081bd9883af21ec9c1c633d161cda3a8", size = 6788387, upload-time = "2026-08-09T13:47:58.192Z" }, + { url = "https://files.pythonhosted.org/packages/15/ea/c0dbdbcf22f43782510a3e492dd3da73c6112b69cac8929d16d127536fc4/numpy-2.5.2-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a839318485284a6fb31be4f8f2c91c8f2cb22f4543c4a8903f12b0671ffe07cc", size = 15667096, upload-time = "2026-08-09T13:48:01.562Z" }, + { url = "https://files.pythonhosted.org/packages/fc/5e/29c73c31748cdb0f7566642125ba17fd5b56780cddf891b085dab27e4466/numpy-2.5.2-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ba0a474801b8dc67b66bf465548abc90e82b44d2611b5770f33008dcabffe8ec", size = 16751730, upload-time = "2026-08-09T13:48:05.706Z" }, + { url = "https://files.pythonhosted.org/packages/47/95/02501e8454796bb58dadf7a99d3181e0b464bf264e1003039572f9779fac/numpy-2.5.2-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:0a4035ae1129ff8777f08bfbd44f1e5d8e9c049ce0c2dd78fc0d92c13e7251c0", size = 17038686, upload-time = "2026-08-09T13:48:09.627Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b5/53a681d91b5c82687067d8ea5035e02d917b5509d6f334cb06484a954714/numpy-2.5.2-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:77843ca236b777e67f8d6b3660ea116e499612703a0ecd7093f316201eb9d8e2", size = 18507727, upload-time = "2026-08-09T13:48:13.744Z" }, + { url = "https://files.pythonhosted.org/packages/42/06/6e11443f7b64ee376c860506091103bf68f92d2cab9e8d96d4501babf07c/numpy-2.5.2-cp315-cp315t-win32.whl", hash = "sha256:7354826bc6f8f69402e9b7fe28d15fcd34feebd74f856f111585c5b0c9fb0251", size = 6269775, upload-time = "2026-08-09T13:48:17.543Z" }, + { url = "https://files.pythonhosted.org/packages/f1/18/195d6b86cd72dbbc501edfa778005fa6b87afd34c153e46028cd3a0938f4/numpy-2.5.2-cp315-cp315t-win_amd64.whl", hash = "sha256:e5651f3f87add730ee6608d915009e19c911fba0cb000c7e3ea994b7d768eb12", size = 12782559, upload-time = "2026-08-09T13:48:21.023Z" }, + { url = "https://files.pythonhosted.org/packages/b4/07/458c344f0f0c178f4481dad5cca790626ffe4c34eabf9467069d06ee4999/numpy-2.5.2-cp315-cp315t-win_arm64.whl", hash = "sha256:5f8e00be2ec6f45f4e8a41a527f68d44a7d96fee92a650e4d8b1326f77f61e6e", size = 10748103, upload-time = "2026-08-09T13:48:24.21Z" }, +] + +[[package]] +name = "openmpi" +version = "5.0.10" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/65/48/47b429693ecfb64bb97caead4ef274a09e1c960242a4239929c942c6ad46/openmpi-5.0.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6e9cd0cb0c29abd5157449343c93d31906f4aaaabf9e84fff5da67d305d1d55b", size = 3150518, upload-time = "2026-02-25T10:48:59.563Z" }, + { url = "https://files.pythonhosted.org/packages/87/16/5f6260bd68e8bd1ed4939ebb3e3fda89e874426ee3d62db9e2e56bc7242f/openmpi-5.0.10-py3-none-macosx_11_0_x86_64.whl", hash = "sha256:f4f524543cf443072b79b1600a71fa0019e20c2ff3291968092f4f0552a17829", size = 3527026, upload-time = "2026-02-25T10:49:01.444Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a7/731f37279225210f8ccfa42193e526555955c54fd7a58c59bcbb28b40add/openmpi-5.0.10-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:1ce81b0d03eeddccb11768b3b776fef24a5d43230c753bd0314cf8b3f6262b11", size = 6052929, upload-time = "2026-02-25T10:49:03.284Z" }, + { url = "https://files.pythonhosted.org/packages/b6/13/11d1b2cbe6034d88d938bceafe30b9d2f95db33a8f7fef06fddfbcad5399/openmpi-5.0.10-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:01eb48365f8b6e6627de29df16a080a9f82f184d61859debdbfcb1c9b76ffebf", size = 6551331, upload-time = "2026-02-25T10:49:04.801Z" }, +] + +[[package]] +name = "pandas" +version = "2.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/f7/f425a00df4fcc22b292c6895c6831c0c8ae1d9fac1e024d16f98a9ce8749/pandas-2.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:376c6446ae31770764215a6c937f72d917f214b43560603cd60da6408f183b6c", size = 11555763, upload-time = "2025-09-29T23:16:53.287Z" }, + { url = "https://files.pythonhosted.org/packages/13/4f/66d99628ff8ce7857aca52fed8f0066ce209f96be2fede6cef9f84e8d04f/pandas-2.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e19d192383eab2f4ceb30b412b22ea30690c9e618f78870357ae1d682912015a", size = 10801217, upload-time = "2025-09-29T23:17:04.522Z" }, + { url = "https://files.pythonhosted.org/packages/1d/03/3fc4a529a7710f890a239cc496fc6d50ad4a0995657dccc1d64695adb9f4/pandas-2.3.3-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5caf26f64126b6c7aec964f74266f435afef1c1b13da3b0636c7518a1fa3e2b1", size = 12148791, upload-time = "2025-09-29T23:17:18.444Z" }, + { url = "https://files.pythonhosted.org/packages/40/a8/4dac1f8f8235e5d25b9955d02ff6f29396191d4e665d71122c3722ca83c5/pandas-2.3.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd7478f1463441ae4ca7308a70e90b33470fa593429f9d4c578dd00d1fa78838", size = 12769373, upload-time = "2025-09-29T23:17:35.846Z" }, + { url = "https://files.pythonhosted.org/packages/df/91/82cc5169b6b25440a7fc0ef3a694582418d875c8e3ebf796a6d6470aa578/pandas-2.3.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4793891684806ae50d1288c9bae9330293ab4e083ccd1c5e383c34549c6e4250", size = 13200444, upload-time = "2025-09-29T23:17:49.341Z" }, + { url = "https://files.pythonhosted.org/packages/10/ae/89b3283800ab58f7af2952704078555fa60c807fff764395bb57ea0b0dbd/pandas-2.3.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:28083c648d9a99a5dd035ec125d42439c6c1c525098c58af0fc38dd1a7a1b3d4", size = 13858459, upload-time = "2025-09-29T23:18:03.722Z" }, + { url = "https://files.pythonhosted.org/packages/85/72/530900610650f54a35a19476eca5104f38555afccda1aa11a92ee14cb21d/pandas-2.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:503cf027cf9940d2ceaa1a93cfb5f8c8c7e6e90720a2850378f0b3f3b1e06826", size = 11346086, upload-time = "2025-09-29T23:18:18.505Z" }, + { url = "https://files.pythonhosted.org/packages/c1/fa/7ac648108144a095b4fb6aa3de1954689f7af60a14cf25583f4960ecb878/pandas-2.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:602b8615ebcc4a0c1751e71840428ddebeb142ec02c786e8ad6b1ce3c8dec523", size = 11578790, upload-time = "2025-09-29T23:18:30.065Z" }, + { url = "https://files.pythonhosted.org/packages/9b/35/74442388c6cf008882d4d4bdfc4109be87e9b8b7ccd097ad1e7f006e2e95/pandas-2.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8fe25fc7b623b0ef6b5009149627e34d2a4657e880948ec3c840e9402e5c1b45", size = 10833831, upload-time = "2025-09-29T23:38:56.071Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e4/de154cbfeee13383ad58d23017da99390b91d73f8c11856f2095e813201b/pandas-2.3.3-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b468d3dad6ff947df92dcb32ede5b7bd41a9b3cceef0a30ed925f6d01fb8fa66", size = 12199267, upload-time = "2025-09-29T23:18:41.627Z" }, + { url = "https://files.pythonhosted.org/packages/bf/c9/63f8d545568d9ab91476b1818b4741f521646cbdd151c6efebf40d6de6f7/pandas-2.3.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b98560e98cb334799c0b07ca7967ac361a47326e9b4e5a7dfb5ab2b1c9d35a1b", size = 12789281, upload-time = "2025-09-29T23:18:56.834Z" }, + { url = "https://files.pythonhosted.org/packages/f2/00/a5ac8c7a0e67fd1a6059e40aa08fa1c52cc00709077d2300e210c3ce0322/pandas-2.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d37b5848ba49824e5c30bedb9c830ab9b7751fd049bc7914533e01c65f79791", size = 13240453, upload-time = "2025-09-29T23:19:09.247Z" }, + { url = "https://files.pythonhosted.org/packages/27/4d/5c23a5bc7bd209231618dd9e606ce076272c9bc4f12023a70e03a86b4067/pandas-2.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db4301b2d1f926ae677a751eb2bd0e8c5f5319c9cb3f88b0becbbb0b07b34151", size = 13890361, upload-time = "2025-09-29T23:19:25.342Z" }, + { url = "https://files.pythonhosted.org/packages/8e/59/712db1d7040520de7a4965df15b774348980e6df45c129b8c64d0dbe74ef/pandas-2.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:f086f6fe114e19d92014a1966f43a3e62285109afe874f067f5abbdcbb10e59c", size = 11348702, upload-time = "2025-09-29T23:19:38.296Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fb/231d89e8637c808b997d172b18e9d4a4bc7bf31296196c260526055d1ea0/pandas-2.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d21f6d74eb1725c2efaa71a2bfc661a0689579b58e9c0ca58a739ff0b002b53", size = 11597846, upload-time = "2025-09-29T23:19:48.856Z" }, + { url = "https://files.pythonhosted.org/packages/5c/bd/bf8064d9cfa214294356c2d6702b716d3cf3bb24be59287a6a21e24cae6b/pandas-2.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3fd2f887589c7aa868e02632612ba39acb0b8948faf5cc58f0850e165bd46f35", size = 10729618, upload-time = "2025-09-29T23:39:08.659Z" }, + { url = "https://files.pythonhosted.org/packages/57/56/cf2dbe1a3f5271370669475ead12ce77c61726ffd19a35546e31aa8edf4e/pandas-2.3.3-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecaf1e12bdc03c86ad4a7ea848d66c685cb6851d807a26aa245ca3d2017a1908", size = 11737212, upload-time = "2025-09-29T23:19:59.765Z" }, + { url = "https://files.pythonhosted.org/packages/e5/63/cd7d615331b328e287d8233ba9fdf191a9c2d11b6af0c7a59cfcec23de68/pandas-2.3.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b3d11d2fda7eb164ef27ffc14b4fcab16a80e1ce67e9f57e19ec0afaf715ba89", size = 12362693, upload-time = "2025-09-29T23:20:14.098Z" }, + { url = "https://files.pythonhosted.org/packages/a6/de/8b1895b107277d52f2b42d3a6806e69cfef0d5cf1d0ba343470b9d8e0a04/pandas-2.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a68e15f780eddf2b07d242e17a04aa187a7ee12b40b930bfdd78070556550e98", size = 12771002, upload-time = "2025-09-29T23:20:26.76Z" }, + { url = "https://files.pythonhosted.org/packages/87/21/84072af3187a677c5893b170ba2c8fbe450a6ff911234916da889b698220/pandas-2.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:371a4ab48e950033bcf52b6527eccb564f52dc826c02afd9a1bc0ab731bba084", size = 13450971, upload-time = "2025-09-29T23:20:41.344Z" }, + { url = "https://files.pythonhosted.org/packages/86/41/585a168330ff063014880a80d744219dbf1dd7a1c706e75ab3425a987384/pandas-2.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:a16dcec078a01eeef8ee61bf64074b4e524a2a3f4b3be9326420cabe59c4778b", size = 10992722, upload-time = "2025-09-29T23:20:54.139Z" }, + { url = "https://files.pythonhosted.org/packages/cd/4b/18b035ee18f97c1040d94debd8f2e737000ad70ccc8f5513f4eefad75f4b/pandas-2.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:56851a737e3470de7fa88e6131f41281ed440d29a9268dcbf0002da5ac366713", size = 11544671, upload-time = "2025-09-29T23:21:05.024Z" }, + { url = "https://files.pythonhosted.org/packages/31/94/72fac03573102779920099bcac1c3b05975c2cb5f01eac609faf34bed1ca/pandas-2.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdcd9d1167f4885211e401b3036c0c8d9e274eee67ea8d0758a256d60704cfe8", size = 10680807, upload-time = "2025-09-29T23:21:15.979Z" }, + { url = "https://files.pythonhosted.org/packages/16/87/9472cf4a487d848476865321de18cc8c920b8cab98453ab79dbbc98db63a/pandas-2.3.3-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e32e7cc9af0f1cc15548288a51a3b681cc2a219faa838e995f7dc53dbab1062d", size = 11709872, upload-time = "2025-09-29T23:21:27.165Z" }, + { url = "https://files.pythonhosted.org/packages/15/07/284f757f63f8a8d69ed4472bfd85122bd086e637bf4ed09de572d575a693/pandas-2.3.3-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:318d77e0e42a628c04dc56bcef4b40de67918f7041c2b061af1da41dcff670ac", size = 12306371, upload-time = "2025-09-29T23:21:40.532Z" }, + { url = "https://files.pythonhosted.org/packages/33/81/a3afc88fca4aa925804a27d2676d22dcd2031c2ebe08aabd0ae55b9ff282/pandas-2.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4e0a175408804d566144e170d0476b15d78458795bb18f1304fb94160cabf40c", size = 12765333, upload-time = "2025-09-29T23:21:55.77Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0f/b4d4ae743a83742f1153464cf1a8ecfafc3ac59722a0b5c8602310cb7158/pandas-2.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2d9ab0fc11822b5eece72ec9587e172f63cff87c00b062f6e37448ced4493", size = 13418120, upload-time = "2025-09-29T23:22:10.109Z" }, + { url = "https://files.pythonhosted.org/packages/4f/c7/e54682c96a895d0c808453269e0b5928a07a127a15704fedb643e9b0a4c8/pandas-2.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:f8bfc0e12dc78f777f323f55c58649591b2cd0c43534e8355c51d3fede5f4dee", size = 10993991, upload-time = "2025-09-29T23:25:04.889Z" }, + { url = "https://files.pythonhosted.org/packages/f9/ca/3f8d4f49740799189e1395812f3bf23b5e8fc7c190827d55a610da72ce55/pandas-2.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:75ea25f9529fdec2d2e93a42c523962261e567d250b0013b16210e1d40d7c2e5", size = 12048227, upload-time = "2025-09-29T23:22:24.343Z" }, + { url = "https://files.pythonhosted.org/packages/0e/5a/f43efec3e8c0cc92c4663ccad372dbdff72b60bdb56b2749f04aa1d07d7e/pandas-2.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74ecdf1d301e812db96a465a525952f4dde225fdb6d8e5a521d47e1f42041e21", size = 11411056, upload-time = "2025-09-29T23:22:37.762Z" }, + { url = "https://files.pythonhosted.org/packages/46/b1/85331edfc591208c9d1a63a06baa67b21d332e63b7a591a5ba42a10bb507/pandas-2.3.3-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6435cb949cb34ec11cc9860246ccb2fdc9ecd742c12d3304989017d53f039a78", size = 11645189, upload-time = "2025-09-29T23:22:51.688Z" }, + { url = "https://files.pythonhosted.org/packages/44/23/78d645adc35d94d1ac4f2a3c4112ab6f5b8999f4898b8cdf01252f8df4a9/pandas-2.3.3-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:900f47d8f20860de523a1ac881c4c36d65efcb2eb850e6948140fa781736e110", size = 12121912, upload-time = "2025-09-29T23:23:05.042Z" }, + { url = "https://files.pythonhosted.org/packages/53/da/d10013df5e6aaef6b425aa0c32e1fc1f3e431e4bcabd420517dceadce354/pandas-2.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a45c765238e2ed7d7c608fc5bc4a6f88b642f2f01e70c0c23d2224dd21829d86", size = 12712160, upload-time = "2025-09-29T23:23:28.57Z" }, + { url = "https://files.pythonhosted.org/packages/bd/17/e756653095a083d8a37cbd816cb87148debcfcd920129b25f99dd8d04271/pandas-2.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c4fc4c21971a1a9f4bdb4c73978c7f7256caa3e62b323f70d6cb80db583350bc", size = 13199233, upload-time = "2025-09-29T23:24:24.876Z" }, + { url = "https://files.pythonhosted.org/packages/04/fd/74903979833db8390b73b3a8a7d30d146d710bd32703724dd9083950386f/pandas-2.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:ee15f284898e7b246df8087fc82b87b01686f98ee67d85a17b7ab44143a3a9a0", size = 11540635, upload-time = "2025-09-29T23:25:52.486Z" }, + { url = "https://files.pythonhosted.org/packages/21/00/266d6b357ad5e6d3ad55093a7e8efc7dd245f5a842b584db9f30b0f0a287/pandas-2.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1611aedd912e1ff81ff41c745822980c49ce4a7907537be8692c8dbc31924593", size = 10759079, upload-time = "2025-09-29T23:26:33.204Z" }, + { url = "https://files.pythonhosted.org/packages/ca/05/d01ef80a7a3a12b2f8bbf16daba1e17c98a2f039cbc8e2f77a2c5a63d382/pandas-2.3.3-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d2cefc361461662ac48810cb14365a365ce864afe85ef1f447ff5a1e99ea81c", size = 11814049, upload-time = "2025-09-29T23:27:15.384Z" }, + { url = "https://files.pythonhosted.org/packages/15/b2/0e62f78c0c5ba7e3d2c5945a82456f4fac76c480940f805e0b97fcbc2f65/pandas-2.3.3-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ee67acbbf05014ea6c763beb097e03cd629961c8a632075eeb34247120abcb4b", size = 12332638, upload-time = "2025-09-29T23:27:51.625Z" }, + { url = "https://files.pythonhosted.org/packages/c5/33/dd70400631b62b9b29c3c93d2feee1d0964dc2bae2e5ad7a6c73a7f25325/pandas-2.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c46467899aaa4da076d5abc11084634e2d197e9460643dd455ac3db5856b24d6", size = 12886834, upload-time = "2025-09-29T23:28:21.289Z" }, + { url = "https://files.pythonhosted.org/packages/d3/18/b5d48f55821228d0d2692b34fd5034bb185e854bdb592e9c640f6290e012/pandas-2.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6253c72c6a1d990a410bc7de641d34053364ef8bcd3126f7e7450125887dffe3", size = 13409925, upload-time = "2025-09-29T23:28:58.261Z" }, + { url = "https://files.pythonhosted.org/packages/a6/3d/124ac75fcd0ecc09b8fdccb0246ef65e35b012030defb0e0eba2cbbbe948/pandas-2.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:1b07204a219b3b7350abaae088f451860223a52cfb8a6c53358e7948735158e5", size = 11109071, upload-time = "2025-09-29T23:32:27.484Z" }, + { url = "https://files.pythonhosted.org/packages/89/9c/0e21c895c38a157e0faa1fb64587a9226d6dd46452cac4532d80c3c4a244/pandas-2.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2462b1a365b6109d275250baaae7b760fd25c726aaca0054649286bcfbb3e8ec", size = 12048504, upload-time = "2025-09-29T23:29:31.47Z" }, + { url = "https://files.pythonhosted.org/packages/d7/82/b69a1c95df796858777b68fbe6a81d37443a33319761d7c652ce77797475/pandas-2.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0242fe9a49aa8b4d78a4fa03acb397a58833ef6199e9aa40a95f027bb3a1b6e7", size = 11410702, upload-time = "2025-09-29T23:29:54.591Z" }, + { url = "https://files.pythonhosted.org/packages/f9/88/702bde3ba0a94b8c73a0181e05144b10f13f29ebfc2150c3a79062a8195d/pandas-2.3.3-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a21d830e78df0a515db2b3d2f5570610f5e6bd2e27749770e8bb7b524b89b450", size = 11634535, upload-time = "2025-09-29T23:30:21.003Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1e/1bac1a839d12e6a82ec6cb40cda2edde64a2013a66963293696bbf31fbbb/pandas-2.3.3-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e3ebdb170b5ef78f19bfb71b0dc5dc58775032361fa188e814959b74d726dd5", size = 12121582, upload-time = "2025-09-29T23:30:43.391Z" }, + { url = "https://files.pythonhosted.org/packages/44/91/483de934193e12a3b1d6ae7c8645d083ff88dec75f46e827562f1e4b4da6/pandas-2.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:d051c0e065b94b7a3cea50eb1ec32e912cd96dba41647eb24104b6c6c14c5788", size = 12699963, upload-time = "2025-09-29T23:31:10.009Z" }, + { url = "https://files.pythonhosted.org/packages/70/44/5191d2e4026f86a2a109053e194d3ba7a31a2d10a9c2348368c63ed4e85a/pandas-2.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3869faf4bd07b3b66a9f462417d0ca3a9df29a9f6abd5d0d0dbab15dac7abe87", size = 13202175, upload-time = "2025-09-29T23:31:59.173Z" }, +] + +[[package]] +name = "pandas" +version = "3.0.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "python-dateutil" }, + { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/be/4f/5f3422a2afec5ffc46308b79e53291365a93748b498ac2e58bead0197916/pandas-3.0.5.tar.gz", hash = "sha256:dca3734d6ab7c906e6730f0788b0a1dbb9f2467731f9711f77995c8e9d62d712", size = 4658219, upload-time = "2026-07-22T22:19:28.819Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/ef/f1fd7431d635bf20015489bf0bd69c17fff1018de773540f651455a3916b/pandas-3.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2946e77e4a53cd248cbde631a12f0e51c8324ce354c3eba4d20147c1ad6f4282", size = 10397178, upload-time = "2026-07-22T22:17:48.274Z" }, + { url = "https://files.pythonhosted.org/packages/31/b4/0eafac990a431561187694126de01f9b12559549b4d86360c0c4bd870fde/pandas-3.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:71ecc8fb7ed1a7aa4392316b5309a6347e8e7f832f38fd897846b3a1457a9298", size = 9990736, upload-time = "2026-07-22T22:17:52.388Z" }, + { url = "https://files.pythonhosted.org/packages/de/21/359880af3ea9b7cb23bea5b51e8e70ef3866c03be09da9a2787e18e330a8/pandas-3.0.5-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b173f5951ff6b8b0ec7675e20dff3c97b7e7a57dfcce387c2d7c5afe87cb7899", size = 10814438, upload-time = "2026-07-22T22:17:54.708Z" }, + { url = "https://files.pythonhosted.org/packages/d1/50/d6cc4d7e508bbccf5d6027314a8312bc7ac73d0ec7f195f53838daafab40/pandas-3.0.5-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2c0cf1dd9b55a22d105fc46c1b489af3bd42264fcba7c66297bf47a9a1d9c78a", size = 11323634, upload-time = "2026-07-22T22:17:56.858Z" }, + { url = "https://files.pythonhosted.org/packages/70/2b/d5f0a8c90dd0ae04e64ba53b871afb796ec026b615086d382ddc2ade729b/pandas-3.0.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0fac0010c75e4efb6b99e249c183a8993ce0dc95c240f9b120a5e67c727b7928", size = 11850860, upload-time = "2026-07-22T22:17:59.1Z" }, + { url = "https://files.pythonhosted.org/packages/5c/30/183aec2e19adf778a98d29b5729a0a68f4cc4ebf9b9c3b70d0297355bcb1/pandas-3.0.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:08d24fe11a17dc33bd6e937dc9c665f9cba08fbdc9f657f405713515febe300d", size = 12411100, upload-time = "2026-07-22T22:18:01.485Z" }, + { url = "https://files.pythonhosted.org/packages/fa/9a/31f4983f191af51ab2a8f2d0c7b33dff3a84da26533f982fff02c2f9e28b/pandas-3.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:b1261758dfb6cf12c3cff8300e21cefad30e7ec709abb4c24ac7318e6a52462a", size = 9968804, upload-time = "2026-07-22T22:18:03.903Z" }, + { url = "https://files.pythonhosted.org/packages/49/97/7886c89a39045c69ad82cbceaf3343810480c8ef49a216319ce8183860a6/pandas-3.0.5-cp311-cp311-win_arm64.whl", hash = "sha256:679f4e85b30ddb1515458ab1e788d3e260eae369b1f78da7a3aa4cac8ebf4a2a", size = 9205447, upload-time = "2026-07-22T22:18:06.134Z" }, + { url = "https://files.pythonhosted.org/packages/1c/54/1dc810ea558d1320b597aa140a514f2fdf1d2ea09c38cf556f13ea712ec9/pandas-3.0.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fa290c16964d4963fbfbc358928239cf3bd755b20e988ce944877def2f44471d", size = 10411717, upload-time = "2026-07-22T22:18:08.307Z" }, + { url = "https://files.pythonhosted.org/packages/68/56/fbe81c09195924d8b7b8d4461a20458fe80a6a5ed6b24f0314da684277e1/pandas-3.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c2e26bb46934b8a2ca0c3de1d3d606fc5f6746584791b2db264d58cf370e08dc", size = 9957095, upload-time = "2026-07-22T22:18:10.6Z" }, + { url = "https://files.pythonhosted.org/packages/e0/51/fac252f4a913ed5eabf3c11b880a9e8d5a6c10f0b2129d0462212d238b4d/pandas-3.0.5-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73fa87b08a7ef706f8aafda39ddaccf2a99047bea62d8c88a0361bcafb2237bc", size = 10485458, upload-time = "2026-07-22T22:18:12.834Z" }, + { url = "https://files.pythonhosted.org/packages/12/98/e976540c1addf70442be7842a18cf70884a964abbf69442504f4d2939989/pandas-3.0.5-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d373ce03ffd84010ed9839fa73672a9c8256990532e158440c0085db7d914b34", size = 10998091, upload-time = "2026-07-22T22:18:15.209Z" }, + { url = "https://files.pythonhosted.org/packages/a4/8c/1f29b5be8d3fc47dd7567eb167fabba2085879b31e0287ce7cba6d3d2ff4/pandas-3.0.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2a29c53d85ea98c5e792c59ef82ee9fbe6ca902c0d0adb6b23f45ef894cd7bf6", size = 11499501, upload-time = "2026-07-22T22:18:17.689Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e2/bd9c98ad2df7b38bde002adde4cdf353519da51881634323b126c55997f9/pandas-3.0.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a5ad3b02ed6bc7d7ae9b70804b2c6aa31827489d150f8e623ce82491b82085d7", size = 12060559, upload-time = "2026-07-22T22:18:20.147Z" }, + { url = "https://files.pythonhosted.org/packages/f3/9a/ffbd852d58bd74a617fe2f8ee6a58a96982271ce41cf981eab22190b4a4b/pandas-3.0.5-cp312-cp312-pyemscripten_2024_0_wasm32.whl", hash = "sha256:b2acb4650527eec6822c3dadb2b771277b65e7dae7a267d4bccf65fd1bb3fbce", size = 7197652, upload-time = "2026-07-22T22:18:22.502Z" }, + { url = "https://files.pythonhosted.org/packages/70/b5/d2d3e9ae73362ba4229651b0ee1455cf78073a1ce585f6ff693782ce263e/pandas-3.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:80a611068e8a3ac23f7398c6c14eb46dc974e5cc9997f653e2dcfd1da74edd41", size = 9831691, upload-time = "2026-07-22T22:18:24.534Z" }, + { url = "https://files.pythonhosted.org/packages/52/51/dea1e89d6a6796b9c43f85a09b484ee03edb8a4c4842e73e200a8c11301c/pandas-3.0.5-cp312-cp312-win_arm64.whl", hash = "sha256:25ff585b972a18ef1fe9ffa3ac6544d9950508aa76832e5147640b6022821e49", size = 9105796, upload-time = "2026-07-22T22:18:27.064Z" }, + { url = "https://files.pythonhosted.org/packages/bf/09/7b95c4a0025227d6f118c4039b423412ac6a982db02864166185d812fbc7/pandas-3.0.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c1c05a767fe8e5b4fe9e1c29806829c582052eaedb9120a3da83ba3f69e24a5b", size = 10385742, upload-time = "2026-07-22T22:18:29.346Z" }, + { url = "https://files.pythonhosted.org/packages/8d/0c/dc78fd8c4da477b4b5e8ad37295af352190d21ef63a9ee1bc071753074cc/pandas-3.0.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:b86765f268b56f7e665b93bce9d5df69dee7f99e595cf8fb839483ab315942a3", size = 9932067, upload-time = "2026-07-22T22:18:31.833Z" }, + { url = "https://files.pythonhosted.org/packages/3e/71/3592c055cf44df9808550f9368ceda80ff2b224d355ef73fe251dcda1802/pandas-3.0.5-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c597ecf5616b5c420372c1d4d4c00dbbfba7398bea857dcc984347e1ea48417b", size = 10466756, upload-time = "2026-07-22T22:18:34.195Z" }, + { url = "https://files.pythonhosted.org/packages/e3/70/4363150359f95b4cb4bcbb34ca23572bb5495749a621a8f3d5a1ddfd293c/pandas-3.0.5-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4b11c36e218331d0387cbe3a0a5f75162357a1d92d57b2b08a336ff94b19b2be", size = 10938525, upload-time = "2026-07-22T22:18:36.81Z" }, + { url = "https://files.pythonhosted.org/packages/f7/d0/317e7a0c67c0e69fa905a0161409397a7dc2d46ff611f6ca4803352c042b/pandas-3.0.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cf52e1f61d229496da17dc7ab54acdee627357e7008fd4fecba3d0ba2937fa58", size = 11489303, upload-time = "2026-07-22T22:18:39.287Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8d/36dade89b49e4f9d5cbdbe863772581f98c0c6d78fc39ad4c557f6f2e17e/pandas-3.0.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:db172144bb56422bd157812f3b021eacc255451470b31e2c633c349490a1cfee", size = 11989004, upload-time = "2026-07-22T22:18:42.208Z" }, + { url = "https://files.pythonhosted.org/packages/9c/ba/18c4ec8a746e177da05a9e7a7963781d8ea195780724f854601b6ebd6b78/pandas-3.0.5-cp313-cp313-win_amd64.whl", hash = "sha256:0d298e951f23016ce4699951d044ae6418dbc91bf68cefca0f77666fcbb4e5c6", size = 9826896, upload-time = "2026-07-22T22:18:44.539Z" }, + { url = "https://files.pythonhosted.org/packages/de/ec/28a57266b753799a87b8bc79e7887ac6fd981b8c6d2978a0b7e7b6bd708c/pandas-3.0.5-cp313-cp313-win_arm64.whl", hash = "sha256:66266d3442a5e8b3c90274c2b8b230bee42dd1c286bc822cc2f9f2c7e12b883e", size = 9094790, upload-time = "2026-07-22T22:18:47.468Z" }, + { url = "https://files.pythonhosted.org/packages/51/2f/cf6aae281264f4463f0875bcbb15fd2bb6d291cc535187dad1732475e4a9/pandas-3.0.5-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2f264fc46911cc8131a7322a16199bbf8e353d27c10bb211f5bd0c814324dc36", size = 10390034, upload-time = "2026-07-22T22:18:49.818Z" }, + { url = "https://files.pythonhosted.org/packages/06/ec/5189518c7a7659c4bdcc6b1eb32c46c6f3c86b0661ffd84143d1112c7732/pandas-3.0.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:53730687fcd161883b24e10411c06d6a4c0f2275d2faf3bb2bc25deb4ba8007c", size = 9980065, upload-time = "2026-07-22T22:18:52.249Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f1/598503ce8d7e3c35601e0747ba288c7864baae66380725bc12f13f884dfe/pandas-3.0.5-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:960d3ebcf249f75206899fcd2c6de53f736b7265759ced0d3e559df0b8b709b0", size = 10545532, upload-time = "2026-07-22T22:18:54.813Z" }, + { url = "https://files.pythonhosted.org/packages/fa/de/ceae2adf7034e07e9910299fe412e1819c4f0dd520700a888bcb03625448/pandas-3.0.5-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e94c2c5ca43bd3ca32bf64d32308887b65e5f9bfd8023ea52755107a999f93b", size = 10963120, upload-time = "2026-07-22T22:18:57.42Z" }, + { url = "https://files.pythonhosted.org/packages/66/25/86e0f4451874eb79e688deeebe3c451fec4557f8952005818d800ee8ac7e/pandas-3.0.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e819dd5f62966b481a8cb649d3299ebd886a1ea91ed5a99bf7ce77c98d18ab94", size = 11563178, upload-time = "2026-07-22T22:18:59.729Z" }, + { url = "https://files.pythonhosted.org/packages/f3/45/8643daa3b4147e433adfcccefdd0380d3aad79d86b15d8999730fe1944d5/pandas-3.0.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3c5ed2e7c06e91d340dfd091d7934f9bc82e4a36b95f647f090b9d1c9ac649da", size = 12028708, upload-time = "2026-07-22T22:19:02.164Z" }, + { url = "https://files.pythonhosted.org/packages/96/58/ad979ae617615576e8aafd569c9d4b62f1191d896e38f51d66ba06f3b89a/pandas-3.0.5-cp314-cp314-win_amd64.whl", hash = "sha256:cd8f7c6dc98527058ee6264219343f5392240a6f1bfa654fc5d79023020d0c92", size = 9951806, upload-time = "2026-07-22T22:19:04.596Z" }, + { url = "https://files.pythonhosted.org/packages/69/32/7ac03886b304049a9d2625ee88f59af760d8a93bd30ed9239bce7b9869a8/pandas-3.0.5-cp314-cp314-win_arm64.whl", hash = "sha256:5183427f5a8156d480f30333777bc978be93650a49a7c01db26adffe95b31e85", size = 9238297, upload-time = "2026-07-22T22:19:06.836Z" }, + { url = "https://files.pythonhosted.org/packages/be/ed/1d1f2ee5547d5167face2376d11c8b2a4c7bfff5a416ee7a9046891fab1e/pandas-3.0.5-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:303da736987d481074ca720ada325f8bd80c64ebc2d45ed79b29df3aaa4a26ca", size = 10849690, upload-time = "2026-07-22T22:19:09.391Z" }, + { url = "https://files.pythonhosted.org/packages/57/55/17e17152e98fbb0c4b1e562bc65387a2f20a80db0f4a86bf8d3a0e4248d4/pandas-3.0.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3b2801bbb049d0136f6c213eae02b5fca969384fc2064dd728d8620552aa49da", size = 10509945, upload-time = "2026-07-22T22:19:11.773Z" }, + { url = "https://files.pythonhosted.org/packages/88/90/817d44dbf83facf9556f33576d9af0a241981e7bb5c00606c0bcb5df8dda/pandas-3.0.5-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cce3a9d11d2b1f82c69a27ec1f4948a170e2c403c4bbfa8cca62e3fdebe2ef3a", size = 10392197, upload-time = "2026-07-22T22:19:14.024Z" }, + { url = "https://files.pythonhosted.org/packages/f1/da/889f00c0a6f5aa1545add70abbf01502dff87ab577adb855bd631c54d2f2/pandas-3.0.5-cp314-cp314t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef01af4d8dc6cd2c8d6c7736f149574ef93fe043811eeb5e445f2647154b5040", size = 10862726, upload-time = "2026-07-22T22:19:16.351Z" }, + { url = "https://files.pythonhosted.org/packages/bc/98/f1e934fb3c98fce859c6147c6785816c7b5b9ab7821115c5d8c4de9842b9/pandas-3.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e2759e890db96dfcffdbd9b86c3c2cb6afaf58def482820317e06163ec1066cd", size = 11414864, upload-time = "2026-07-22T22:19:18.981Z" }, + { url = "https://files.pythonhosted.org/packages/fe/be/d448af7d657d82e1888dd8551f79c6d6fb161080b5b9752d84d910ec2319/pandas-3.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b58b1b39d46a5862e3fb18f50d1a201398619d16a0f9f73f57eea5583cf0e63c", size = 11925105, upload-time = "2026-07-22T22:19:21.515Z" }, + { url = "https://files.pythonhosted.org/packages/29/c1/ccb4238212c8c4f496c584f3044d94e0c030ed8e1d68999db46c91c2242f/pandas-3.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:1c10461f6eeb35d8f05b6184c65c8b9991663b66c46b1d559b682cb34ae7c6ea", size = 10387612, upload-time = "2026-07-22T22:19:24.257Z" }, + { url = "https://files.pythonhosted.org/packages/d2/cf/6a51b2c38980e04c279fd2fa908a1b0982064e860444acfca4ec2e2c8359/pandas-3.0.5-cp314-cp314t-win_arm64.whl", hash = "sha256:3c5015fd1730fbf883647e88068176c839c102cea883ba1769a6f4593bfc1f8c", size = 9509776, upload-time = "2026-07-22T22:19:26.694Z" }, +] + +[[package]] +name = "proto-plus" +version = "1.28.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/6a/056256feb4bd000869aba5c16cf2aa911572ca2a2feb185f86e457b5171e/proto_plus-1.28.3.tar.gz", hash = "sha256:5f91b30dafa6bb38d432c5557a6ee1d35ffd40b4b1e0e3ca27260448560b91d9", size = 58051, upload-time = "2026-08-06T06:24:55.581Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/3a/cfee3c50294f55a2f0f9575052dec2c2a48891ad4b1c2a133b05a87026cd/proto_plus-1.28.3-py3-none-any.whl", hash = "sha256:dc76880b8ee951cca002098574376cf71e055f9f16d9ba6570fb8a06f726d281", size = 50795, upload-time = "2026-08-06T06:23:50.653Z" }, +] + +[[package]] +name = "protobuf" +version = "7.35.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/01/9ef0afd7999eb9badb3a768b4aedd78c86d4c65cfaf1958ab276199e76b4/protobuf-7.35.1.tar.gz", hash = "sha256:ce115a26fe0c39a2c29973d914d327e516a6455464489fe3cd1e51a1b354f81a", size = 458717, upload-time = "2026-06-11T21:55:40.257Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/03/8aeeb7458d22546bf64b5250ca1daeb5ff757d900e8e4a7476c6f0db843e/protobuf-7.35.1-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:24f857477359a85c0c235261b8ba905fd51b2562f4a64ca1df5473f29850cbf6", size = 433226, upload-time = "2026-06-11T21:55:31.719Z" }, + { url = "https://files.pythonhosted.org/packages/37/4b/dfb89eb0e652a1ff073c39a59fb5e3a83cfe9b57a2c83fa6d78270101767/protobuf-7.35.1-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:11d6b0ec246892d85215b0a13ca6e0233cf5284b68f0ac02646427f4ff88a799", size = 328847, upload-time = "2026-06-11T21:55:34.035Z" }, + { url = "https://files.pythonhosted.org/packages/0f/58/dc12f2cd484951524af6e3382c785869b9b3fb5e52ee95ae23add53ee8f9/protobuf-7.35.1-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:b73f9489a4b8b1c9cb1f8ed951c736392592edb24b9d6819f36d2e10b171d5b4", size = 344030, upload-time = "2026-06-11T21:55:34.941Z" }, + { url = "https://files.pythonhosted.org/packages/e4/be/5b3cfe508bfab6761414ff944e3366eb13be4fd71efcd69450f89ba39f43/protobuf-7.35.1-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:74758715c53d7158fb76caf4f0cfdacc5329a4b1bb994f865d6cf302d413a1c4", size = 327130, upload-time = "2026-06-11T21:55:35.921Z" }, + { url = "https://files.pythonhosted.org/packages/d8/bc/6d6c7ba8709c85f8f2c390b2b118d6fb08a783676a572271851bf45a7d22/protobuf-7.35.1-cp310-abi3-win32.whl", hash = "sha256:353652e4efd0bca5b5fc2656abf8307ef351f0cf938c9eba09f0e09c20a25c30", size = 428945, upload-time = "2026-06-11T21:55:37.034Z" }, + { url = "https://files.pythonhosted.org/packages/0a/19/8d0cb6f20a1ef7b18f1c8986ad5783f22f84cce39c6ce9a6e645ea55192e/protobuf-7.35.1-cp310-abi3-win_amd64.whl", hash = "sha256:230a75ddfc2de4806e56696ce9640c1cdfdb6543b7cfce98d42a4c0a0e7bdb87", size = 439996, upload-time = "2026-06-11T21:55:38.123Z" }, + { url = "https://files.pythonhosted.org/packages/19/c7/5f7c636ec43e0c545e28d1f1db71990108306f7bdcb89f069ba97e428e7f/protobuf-7.35.1-py3-none-any.whl", hash = "sha256:4bc97768d8fe4ad6743c8a19403e314511ed9f6d13205b687e52421c023ac1b9", size = 171659, upload-time = "2026-06-11T21:55:39.155Z" }, +] + +[[package]] +name = "pyasn1" +version = "0.6.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a4/9a/23310166d960def5897e91fe20e5b724601b02a22e84ba1f94232c0b7f67/pyasn1-0.6.4.tar.gz", hash = "sha256:9c447d8431c947fe4c8febc4ed9e760bc29011a5b01e5c74b67025bd9fb8ce81", size = 151262, upload-time = "2026-07-09T01:12:33.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3b/6163796d69c3977d1e4287bea4a6979161cbbdd170ebb430511e8e1999ce/pyasn1-0.6.4-py3-none-any.whl", hash = "sha256:deda9277cfd454080ec40b207fb6df82206a3a2688735233cdcd8d3d565f088b", size = 84410, upload-time = "2026-07-09T01:12:32.92Z" }, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "pytz" +version = "2026.3.post1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/48/fb042503b6ca6cd271261dc559fd6432f7d8c713153e9ec5c591af4dfc1c/pytz-2026.3.post1.tar.gz", hash = "sha256:2211d3fcf9a797d3405cac96ac7f61d80e6a644f72a3309607282fe8a2010c5d", size = 319745, upload-time = "2026-07-25T15:12:07.385Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/7b/39c34ca613b0b198cb866466651b26b045e2009864c5183c979a3b83f383/pytz-2026.3.post1-py2.py3-none-any.whl", hash = "sha256:dd95840dd199baea12d9cc096a1d452caa6596a1c1e4b5f3dbd1541855d5e815", size = 508283, upload-time = "2026-07-25T15:12:05.782Z" }, +] + +[[package]] +name = "requests" +version = "2.34.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, +] + +[[package]] +name = "s3transfer" +version = "0.10.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "botocore" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/0a/1cdbabf9edd0ea7747efdf6c9ab4e7061b085aa7f9bfc36bb1601563b069/s3transfer-0.10.4.tar.gz", hash = "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7", size = 145287, upload-time = "2024-11-20T21:06:05.981Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/05/7957af15543b8c9799209506df4660cba7afc4cf94bfb60513827e96bed6/s3transfer-0.10.4-py3-none-any.whl", hash = "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e", size = 83175, upload-time = "2024-11-20T21:06:03.961Z" }, +] + +[[package]] +name = "scikit-learn" +version = "1.7.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "joblib" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, + { name = "scipy", version = "1.15.3", source = { registry = "https://pypi.org/simple" } }, + { name = "threadpoolctl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/c2/a7855e41c9d285dfe86dc50b250978105dce513d6e459ea66a6aeb0e1e0c/scikit_learn-1.7.2.tar.gz", hash = "sha256:20e9e49ecd130598f1ca38a1d85090e1a600147b9c02fa6f15d69cb53d968fda", size = 7193136, upload-time = "2025-09-09T08:21:29.075Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ba/3e/daed796fd69cce768b8788401cc464ea90b306fb196ae1ffed0b98182859/scikit_learn-1.7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b33579c10a3081d076ab403df4a4190da4f4432d443521674637677dc91e61f", size = 9336221, upload-time = "2025-09-09T08:20:19.328Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ce/af9d99533b24c55ff4e18d9b7b4d9919bbc6cd8f22fe7a7be01519a347d5/scikit_learn-1.7.2-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:36749fb62b3d961b1ce4fedf08fa57a1986cd409eff2d783bca5d4b9b5fce51c", size = 8653834, upload-time = "2025-09-09T08:20:22.073Z" }, + { url = "https://files.pythonhosted.org/packages/58/0e/8c2a03d518fb6bd0b6b0d4b114c63d5f1db01ff0f9925d8eb10960d01c01/scikit_learn-1.7.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7a58814265dfc52b3295b1900cfb5701589d30a8bb026c7540f1e9d3499d5ec8", size = 9660938, upload-time = "2025-09-09T08:20:24.327Z" }, + { url = "https://files.pythonhosted.org/packages/2b/75/4311605069b5d220e7cf5adabb38535bd96f0079313cdbb04b291479b22a/scikit_learn-1.7.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4a847fea807e278f821a0406ca01e387f97653e284ecbd9750e3ee7c90347f18", size = 9477818, upload-time = "2025-09-09T08:20:26.845Z" }, + { url = "https://files.pythonhosted.org/packages/7f/9b/87961813c34adbca21a6b3f6b2bea344c43b30217a6d24cc437c6147f3e8/scikit_learn-1.7.2-cp310-cp310-win_amd64.whl", hash = "sha256:ca250e6836d10e6f402436d6463d6c0e4d8e0234cfb6a9a47835bd392b852ce5", size = 8886969, upload-time = "2025-09-09T08:20:29.329Z" }, + { url = "https://files.pythonhosted.org/packages/43/83/564e141eef908a5863a54da8ca342a137f45a0bfb71d1d79704c9894c9d1/scikit_learn-1.7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7509693451651cd7361d30ce4e86a1347493554f172b1c72a39300fa2aea79e", size = 9331967, upload-time = "2025-09-09T08:20:32.421Z" }, + { url = "https://files.pythonhosted.org/packages/18/d6/ba863a4171ac9d7314c4d3fc251f015704a2caeee41ced89f321c049ed83/scikit_learn-1.7.2-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:0486c8f827c2e7b64837c731c8feff72c0bd2b998067a8a9cbc10643c31f0fe1", size = 8648645, upload-time = "2025-09-09T08:20:34.436Z" }, + { url = "https://files.pythonhosted.org/packages/ef/0e/97dbca66347b8cf0ea8b529e6bb9367e337ba2e8be0ef5c1a545232abfde/scikit_learn-1.7.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89877e19a80c7b11a2891a27c21c4894fb18e2c2e077815bcade10d34287b20d", size = 9715424, upload-time = "2025-09-09T08:20:36.776Z" }, + { url = "https://files.pythonhosted.org/packages/f7/32/1f3b22e3207e1d2c883a7e09abb956362e7d1bd2f14458c7de258a26ac15/scikit_learn-1.7.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8da8bf89d4d79aaec192d2bda62f9b56ae4e5b4ef93b6a56b5de4977e375c1f1", size = 9509234, upload-time = "2025-09-09T08:20:38.957Z" }, + { url = "https://files.pythonhosted.org/packages/9f/71/34ddbd21f1da67c7a768146968b4d0220ee6831e4bcbad3e03dd3eae88b6/scikit_learn-1.7.2-cp311-cp311-win_amd64.whl", hash = "sha256:9b7ed8d58725030568523e937c43e56bc01cadb478fc43c042a9aca1dacb3ba1", size = 8894244, upload-time = "2025-09-09T08:20:41.166Z" }, + { url = "https://files.pythonhosted.org/packages/a7/aa/3996e2196075689afb9fce0410ebdb4a09099d7964d061d7213700204409/scikit_learn-1.7.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:8d91a97fa2b706943822398ab943cde71858a50245e31bc71dba62aab1d60a96", size = 9259818, upload-time = "2025-09-09T08:20:43.19Z" }, + { url = "https://files.pythonhosted.org/packages/43/5d/779320063e88af9c4a7c2cf463ff11c21ac9c8bd730c4a294b0000b666c9/scikit_learn-1.7.2-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:acbc0f5fd2edd3432a22c69bed78e837c70cf896cd7993d71d51ba6708507476", size = 8636997, upload-time = "2025-09-09T08:20:45.468Z" }, + { url = "https://files.pythonhosted.org/packages/5c/d0/0c577d9325b05594fdd33aa970bf53fb673f051a45496842caee13cfd7fe/scikit_learn-1.7.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e5bf3d930aee75a65478df91ac1225ff89cd28e9ac7bd1196853a9229b6adb0b", size = 9478381, upload-time = "2025-09-09T08:20:47.982Z" }, + { url = "https://files.pythonhosted.org/packages/82/70/8bf44b933837ba8494ca0fc9a9ab60f1c13b062ad0197f60a56e2fc4c43e/scikit_learn-1.7.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4d6e9deed1a47aca9fe2f267ab8e8fe82ee20b4526b2c0cd9e135cea10feb44", size = 9300296, upload-time = "2025-09-09T08:20:50.366Z" }, + { url = "https://files.pythonhosted.org/packages/c6/99/ed35197a158f1fdc2fe7c3680e9c70d0128f662e1fee4ed495f4b5e13db0/scikit_learn-1.7.2-cp312-cp312-win_amd64.whl", hash = "sha256:6088aa475f0785e01bcf8529f55280a3d7d298679f50c0bb70a2364a82d0b290", size = 8731256, upload-time = "2025-09-09T08:20:52.627Z" }, + { url = "https://files.pythonhosted.org/packages/ae/93/a3038cb0293037fd335f77f31fe053b89c72f17b1c8908c576c29d953e84/scikit_learn-1.7.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b7dacaa05e5d76759fb071558a8b5130f4845166d88654a0f9bdf3eb57851b7", size = 9212382, upload-time = "2025-09-09T08:20:54.731Z" }, + { url = "https://files.pythonhosted.org/packages/40/dd/9a88879b0c1104259136146e4742026b52df8540c39fec21a6383f8292c7/scikit_learn-1.7.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:abebbd61ad9e1deed54cca45caea8ad5f79e1b93173dece40bb8e0c658dbe6fe", size = 8592042, upload-time = "2025-09-09T08:20:57.313Z" }, + { url = "https://files.pythonhosted.org/packages/46/af/c5e286471b7d10871b811b72ae794ac5fe2989c0a2df07f0ec723030f5f5/scikit_learn-1.7.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:502c18e39849c0ea1a5d681af1dbcf15f6cce601aebb657aabbfe84133c1907f", size = 9434180, upload-time = "2025-09-09T08:20:59.671Z" }, + { url = "https://files.pythonhosted.org/packages/f1/fd/df59faa53312d585023b2da27e866524ffb8faf87a68516c23896c718320/scikit_learn-1.7.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a4c328a71785382fe3fe676a9ecf2c86189249beff90bf85e22bdb7efaf9ae0", size = 9283660, upload-time = "2025-09-09T08:21:01.71Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c7/03000262759d7b6f38c836ff9d512f438a70d8a8ddae68ee80de72dcfb63/scikit_learn-1.7.2-cp313-cp313-win_amd64.whl", hash = "sha256:63a9afd6f7b229aad94618c01c252ce9e6fa97918c5ca19c9a17a087d819440c", size = 8702057, upload-time = "2025-09-09T08:21:04.234Z" }, + { url = "https://files.pythonhosted.org/packages/55/87/ef5eb1f267084532c8e4aef98a28b6ffe7425acbfd64b5e2f2e066bc29b3/scikit_learn-1.7.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:9acb6c5e867447b4e1390930e3944a005e2cb115922e693c08a323421a6966e8", size = 9558731, upload-time = "2025-09-09T08:21:06.381Z" }, + { url = "https://files.pythonhosted.org/packages/93/f8/6c1e3fc14b10118068d7938878a9f3f4e6d7b74a8ddb1e5bed65159ccda8/scikit_learn-1.7.2-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:2a41e2a0ef45063e654152ec9d8bcfc39f7afce35b08902bfe290c2498a67a6a", size = 9038852, upload-time = "2025-09-09T08:21:08.628Z" }, + { url = "https://files.pythonhosted.org/packages/83/87/066cafc896ee540c34becf95d30375fe5cbe93c3b75a0ee9aa852cd60021/scikit_learn-1.7.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98335fb98509b73385b3ab2bd0639b1f610541d3988ee675c670371d6a87aa7c", size = 9527094, upload-time = "2025-09-09T08:21:11.486Z" }, + { url = "https://files.pythonhosted.org/packages/9c/2b/4903e1ccafa1f6453b1ab78413938c8800633988c838aa0be386cbb33072/scikit_learn-1.7.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:191e5550980d45449126e23ed1d5e9e24b2c68329ee1f691a3987476e115e09c", size = 9367436, upload-time = "2025-09-09T08:21:13.602Z" }, + { url = "https://files.pythonhosted.org/packages/b5/aa/8444be3cfb10451617ff9d177b3c190288f4563e6c50ff02728be67ad094/scikit_learn-1.7.2-cp313-cp313t-win_amd64.whl", hash = "sha256:57dc4deb1d3762c75d685507fbd0bc17160144b2f2ba4ccea5dc285ab0d0e973", size = 9275749, upload-time = "2025-09-09T08:21:15.96Z" }, + { url = "https://files.pythonhosted.org/packages/d9/82/dee5acf66837852e8e68df6d8d3a6cb22d3df997b733b032f513d95205b7/scikit_learn-1.7.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fa8f63940e29c82d1e67a45d5297bdebbcb585f5a5a50c4914cc2e852ab77f33", size = 9208906, upload-time = "2025-09-09T08:21:18.557Z" }, + { url = "https://files.pythonhosted.org/packages/3c/30/9029e54e17b87cb7d50d51a5926429c683d5b4c1732f0507a6c3bed9bf65/scikit_learn-1.7.2-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:f95dc55b7902b91331fa4e5845dd5bde0580c9cd9612b1b2791b7e80c3d32615", size = 8627836, upload-time = "2025-09-09T08:21:20.695Z" }, + { url = "https://files.pythonhosted.org/packages/60/18/4a52c635c71b536879f4b971c2cedf32c35ee78f48367885ed8025d1f7ee/scikit_learn-1.7.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9656e4a53e54578ad10a434dc1f993330568cfee176dff07112b8785fb413106", size = 9426236, upload-time = "2025-09-09T08:21:22.645Z" }, + { url = "https://files.pythonhosted.org/packages/99/7e/290362f6ab582128c53445458a5befd471ed1ea37953d5bcf80604619250/scikit_learn-1.7.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96dc05a854add0e50d3f47a1ef21a10a595016da5b007c7d9cd9d0bffd1fcc61", size = 9312593, upload-time = "2025-09-09T08:21:24.65Z" }, + { url = "https://files.pythonhosted.org/packages/8e/87/24f541b6d62b1794939ae6422f8023703bbf6900378b2b34e0b4384dfefd/scikit_learn-1.7.2-cp314-cp314-win_amd64.whl", hash = "sha256:bb24510ed3f9f61476181e4db51ce801e2ba37541def12dc9333b946fc7a9cf8", size = 8820007, upload-time = "2025-09-09T08:21:26.713Z" }, +] + +[[package]] +name = "scikit-learn" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "joblib" }, + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "numpy", version = "2.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" }, + { name = "scipy", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, + { name = "threadpoolctl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0e/d4/40988bf3b8e34feec1d0e6a051446b1f66225f8529b9309becaeef62b6c4/scikit_learn-1.8.0.tar.gz", hash = "sha256:9bccbb3b40e3de10351f8f5068e105d0f4083b1a65fa07b6634fbc401a6287fd", size = 7335585, upload-time = "2025-12-10T07:08:53.618Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/92/53ea2181da8ac6bf27170191028aee7251f8f841f8d3edbfdcaf2008fde9/scikit_learn-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:146b4d36f800c013d267b29168813f7a03a43ecd2895d04861f1240b564421da", size = 8595835, upload-time = "2025-12-10T07:07:39.385Z" }, + { url = "https://files.pythonhosted.org/packages/01/18/d154dc1638803adf987910cdd07097d9c526663a55666a97c124d09fb96a/scikit_learn-1.8.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f984ca4b14914e6b4094c5d52a32ea16b49832c03bd17a110f004db3c223e8e1", size = 8080381, upload-time = "2025-12-10T07:07:41.93Z" }, + { url = "https://files.pythonhosted.org/packages/8a/44/226142fcb7b7101e64fdee5f49dbe6288d4c7af8abf593237b70fca080a4/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e30adb87f0cc81c7690a84f7932dd66be5bac57cfe16b91cb9151683a4a2d3b", size = 8799632, upload-time = "2025-12-10T07:07:43.899Z" }, + { url = "https://files.pythonhosted.org/packages/36/4d/4a67f30778a45d542bbea5db2dbfa1e9e100bf9ba64aefe34215ba9f11f6/scikit_learn-1.8.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ada8121bcb4dac28d930febc791a69f7cb1673c8495e5eee274190b73a4559c1", size = 9103788, upload-time = "2025-12-10T07:07:45.982Z" }, + { url = "https://files.pythonhosted.org/packages/89/3c/45c352094cfa60050bcbb967b1faf246b22e93cb459f2f907b600f2ceda5/scikit_learn-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:c57b1b610bd1f40ba43970e11ce62821c2e6569e4d74023db19c6b26f246cb3b", size = 8081706, upload-time = "2025-12-10T07:07:48.111Z" }, + { url = "https://files.pythonhosted.org/packages/3d/46/5416595bb395757f754feb20c3d776553a386b661658fb21b7c814e89efe/scikit_learn-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:2838551e011a64e3053ad7618dda9310175f7515f1742fa2d756f7c874c05961", size = 7688451, upload-time = "2025-12-10T07:07:49.873Z" }, + { url = "https://files.pythonhosted.org/packages/90/74/e6a7cc4b820e95cc38cf36cd74d5aa2b42e8ffc2d21fe5a9a9c45c1c7630/scikit_learn-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5fb63362b5a7ddab88e52b6dbb47dac3fd7dafeee740dc6c8d8a446ddedade8e", size = 8548242, upload-time = "2025-12-10T07:07:51.568Z" }, + { url = "https://files.pythonhosted.org/packages/49/d8/9be608c6024d021041c7f0b3928d4749a706f4e2c3832bbede4fb4f58c95/scikit_learn-1.8.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:5025ce924beccb28298246e589c691fe1b8c1c96507e6d27d12c5fadd85bfd76", size = 8079075, upload-time = "2025-12-10T07:07:53.697Z" }, + { url = "https://files.pythonhosted.org/packages/dd/47/f187b4636ff80cc63f21cd40b7b2d177134acaa10f6bb73746130ee8c2e5/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4496bb2cf7a43ce1a2d7524a79e40bc5da45cf598dbf9545b7e8316ccba47bb4", size = 8660492, upload-time = "2025-12-10T07:07:55.574Z" }, + { url = "https://files.pythonhosted.org/packages/97/74/b7a304feb2b49df9fafa9382d4d09061a96ee9a9449a7cbea7988dda0828/scikit_learn-1.8.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a0bcfe4d0d14aec44921545fd2af2338c7471de9cb701f1da4c9d85906ab847a", size = 8931904, upload-time = "2025-12-10T07:07:57.666Z" }, + { url = "https://files.pythonhosted.org/packages/9f/c4/0ab22726a04ede56f689476b760f98f8f46607caecff993017ac1b64aa5d/scikit_learn-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:35c007dedb2ffe38fe3ee7d201ebac4a2deccd2408e8621d53067733e3c74809", size = 8019359, upload-time = "2025-12-10T07:07:59.838Z" }, + { url = "https://files.pythonhosted.org/packages/24/90/344a67811cfd561d7335c1b96ca21455e7e472d281c3c279c4d3f2300236/scikit_learn-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:8c497fff237d7b4e07e9ef1a640887fa4fb765647f86fbe00f969ff6280ce2bb", size = 7641898, upload-time = "2025-12-10T07:08:01.36Z" }, + { url = "https://files.pythonhosted.org/packages/03/aa/e22e0768512ce9255eba34775be2e85c2048da73da1193e841707f8f039c/scikit_learn-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0d6ae97234d5d7079dc0040990a6f7aeb97cb7fa7e8945f1999a429b23569e0a", size = 8513770, upload-time = "2025-12-10T07:08:03.251Z" }, + { url = "https://files.pythonhosted.org/packages/58/37/31b83b2594105f61a381fc74ca19e8780ee923be2d496fcd8d2e1147bd99/scikit_learn-1.8.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:edec98c5e7c128328124a029bceb09eda2d526997780fef8d65e9a69eead963e", size = 8044458, upload-time = "2025-12-10T07:08:05.336Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5a/3f1caed8765f33eabb723596666da4ebbf43d11e96550fb18bdec42b467b/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74b66d8689d52ed04c271e1329f0c61635bcaf5b926db9b12d58914cdc01fe57", size = 8610341, upload-time = "2025-12-10T07:08:07.732Z" }, + { url = "https://files.pythonhosted.org/packages/38/cf/06896db3f71c75902a8e9943b444a56e727418f6b4b4a90c98c934f51ed4/scikit_learn-1.8.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8fdf95767f989b0cfedb85f7ed8ca215d4be728031f56ff5a519ee1e3276dc2e", size = 8900022, upload-time = "2025-12-10T07:08:09.862Z" }, + { url = "https://files.pythonhosted.org/packages/1c/f9/9b7563caf3ec8873e17a31401858efab6b39a882daf6c1bfa88879c0aa11/scikit_learn-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:2de443b9373b3b615aec1bb57f9baa6bb3a9bd093f1269ba95c17d870422b271", size = 7989409, upload-time = "2025-12-10T07:08:12.028Z" }, + { url = "https://files.pythonhosted.org/packages/49/bd/1f4001503650e72c4f6009ac0c4413cb17d2d601cef6f71c0453da2732fc/scikit_learn-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:eddde82a035681427cbedded4e6eff5e57fa59216c2e3e90b10b19ab1d0a65c3", size = 7619760, upload-time = "2025-12-10T07:08:13.688Z" }, + { url = "https://files.pythonhosted.org/packages/d2/7d/a630359fc9dcc95496588c8d8e3245cc8fd81980251079bc09c70d41d951/scikit_learn-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7cc267b6108f0a1499a734167282c00c4ebf61328566b55ef262d48e9849c735", size = 8826045, upload-time = "2025-12-10T07:08:15.215Z" }, + { url = "https://files.pythonhosted.org/packages/cc/56/a0c86f6930cfcd1c7054a2bc417e26960bb88d32444fe7f71d5c2cfae891/scikit_learn-1.8.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:fe1c011a640a9f0791146011dfd3c7d9669785f9fed2b2a5f9e207536cf5c2fd", size = 8420324, upload-time = "2025-12-10T07:08:17.561Z" }, + { url = "https://files.pythonhosted.org/packages/46/1e/05962ea1cebc1cf3876667ecb14c283ef755bf409993c5946ade3b77e303/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72358cce49465d140cc4e7792015bb1f0296a9742d5622c67e31399b75468b9e", size = 8680651, upload-time = "2025-12-10T07:08:19.952Z" }, + { url = "https://files.pythonhosted.org/packages/fe/56/a85473cd75f200c9759e3a5f0bcab2d116c92a8a02ee08ccd73b870f8bb4/scikit_learn-1.8.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:80832434a6cc114f5219211eec13dcbc16c2bac0e31ef64c6d346cde3cf054cb", size = 8925045, upload-time = "2025-12-10T07:08:22.11Z" }, + { url = "https://files.pythonhosted.org/packages/cc/b7/64d8cfa896c64435ae57f4917a548d7ac7a44762ff9802f75a79b77cb633/scikit_learn-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ee787491dbfe082d9c3013f01f5991658b0f38aa8177e4cd4bf434c58f551702", size = 8507994, upload-time = "2025-12-10T07:08:23.943Z" }, + { url = "https://files.pythonhosted.org/packages/5e/37/e192ea709551799379958b4c4771ec507347027bb7c942662c7fbeba31cb/scikit_learn-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf97c10a3f5a7543f9b88cbf488d33d175e9146115a451ae34568597ba33dcde", size = 7869518, upload-time = "2025-12-10T07:08:25.71Z" }, + { url = "https://files.pythonhosted.org/packages/24/05/1af2c186174cc92dcab2233f327336058c077d38f6fe2aceb08e6ab4d509/scikit_learn-1.8.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:c22a2da7a198c28dd1a6e1136f19c830beab7fdca5b3e5c8bba8394f8a5c45b3", size = 8528667, upload-time = "2025-12-10T07:08:27.541Z" }, + { url = "https://files.pythonhosted.org/packages/a8/25/01c0af38fe969473fb292bba9dc2b8f9b451f3112ff242c647fee3d0dfe7/scikit_learn-1.8.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:6b595b07a03069a2b1740dc08c2299993850ea81cce4fe19b2421e0c970de6b7", size = 8066524, upload-time = "2025-12-10T07:08:29.822Z" }, + { url = "https://files.pythonhosted.org/packages/be/ce/a0623350aa0b68647333940ee46fe45086c6060ec604874e38e9ab7d8e6c/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29ffc74089f3d5e87dfca4c2c8450f88bdc61b0fc6ed5d267f3988f19a1309f6", size = 8657133, upload-time = "2025-12-10T07:08:31.865Z" }, + { url = "https://files.pythonhosted.org/packages/b8/cb/861b41341d6f1245e6ca80b1c1a8c4dfce43255b03df034429089ca2a2c5/scikit_learn-1.8.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fb65db5d7531bccf3a4f6bec3462223bea71384e2cda41da0f10b7c292b9e7c4", size = 8923223, upload-time = "2025-12-10T07:08:34.166Z" }, + { url = "https://files.pythonhosted.org/packages/76/18/a8def8f91b18cd1ba6e05dbe02540168cb24d47e8dcf69e8d00b7da42a08/scikit_learn-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:56079a99c20d230e873ea40753102102734c5953366972a71d5cb39a32bc40c6", size = 8096518, upload-time = "2025-12-10T07:08:36.339Z" }, + { url = "https://files.pythonhosted.org/packages/d1/77/482076a678458307f0deb44e29891d6022617b2a64c840c725495bee343f/scikit_learn-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:3bad7565bc9cf37ce19a7c0d107742b320c1285df7aab1a6e2d28780df167242", size = 7754546, upload-time = "2025-12-10T07:08:38.128Z" }, + { url = "https://files.pythonhosted.org/packages/2d/d1/ef294ca754826daa043b2a104e59960abfab4cf653891037d19dd5b6f3cf/scikit_learn-1.8.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:4511be56637e46c25721e83d1a9cea9614e7badc7040c4d573d75fbe257d6fd7", size = 8848305, upload-time = "2025-12-10T07:08:41.013Z" }, + { url = "https://files.pythonhosted.org/packages/5b/e2/b1f8b05138ee813b8e1a4149f2f0d289547e60851fd1bb268886915adbda/scikit_learn-1.8.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:a69525355a641bf8ef136a7fa447672fb54fe8d60cab5538d9eb7c6438543fb9", size = 8432257, upload-time = "2025-12-10T07:08:42.873Z" }, + { url = "https://files.pythonhosted.org/packages/26/11/c32b2138a85dcb0c99f6afd13a70a951bfdff8a6ab42d8160522542fb647/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c2656924ec73e5939c76ac4c8b026fc203b83d8900362eb2599d8aee80e4880f", size = 8678673, upload-time = "2025-12-10T07:08:45.362Z" }, + { url = "https://files.pythonhosted.org/packages/c7/57/51f2384575bdec454f4fe4e7a919d696c9ebce914590abf3e52d47607ab8/scikit_learn-1.8.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15fc3b5d19cc2be65404786857f2e13c70c83dd4782676dd6814e3b89dc8f5b9", size = 8922467, upload-time = "2025-12-10T07:08:47.408Z" }, + { url = "https://files.pythonhosted.org/packages/35/4d/748c9e2872637a57981a04adc038dacaa16ba8ca887b23e34953f0b3f742/scikit_learn-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:00d6f1d66fbcf4eba6e356e1420d33cc06c70a45bb1363cd6f6a8e4ebbbdece2", size = 8774395, upload-time = "2025-12-10T07:08:49.337Z" }, + { url = "https://files.pythonhosted.org/packages/60/22/d7b2ebe4704a5e50790ba089d5c2ae308ab6bb852719e6c3bd4f04c3a363/scikit_learn-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f28dd15c6bb0b66ba09728cf09fd8736c304be29409bd8445a080c1280619e8c", size = 8002647, upload-time = "2025-12-10T07:08:51.601Z" }, +] + +[[package]] +name = "scipy" +version = "1.15.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.11'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/2f/4966032c5f8cc7e6a60f1b2e0ad686293b9474b65246b0c642e3ef3badd0/scipy-1.15.3-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:a345928c86d535060c9c2b25e71e87c39ab2f22fc96e9636bd74d1dbf9de448c", size = 38702770, upload-time = "2025-05-08T16:04:20.849Z" }, + { url = "https://files.pythonhosted.org/packages/a0/6e/0c3bf90fae0e910c274db43304ebe25a6b391327f3f10b5dcc638c090795/scipy-1.15.3-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:ad3432cb0f9ed87477a8d97f03b763fd1d57709f1bbde3c9369b1dff5503b253", size = 30094511, upload-time = "2025-05-08T16:04:27.103Z" }, + { url = "https://files.pythonhosted.org/packages/ea/b1/4deb37252311c1acff7f101f6453f0440794f51b6eacb1aad4459a134081/scipy-1.15.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:aef683a9ae6eb00728a542b796f52a5477b78252edede72b8327a886ab63293f", size = 22368151, upload-time = "2025-05-08T16:04:31.731Z" }, + { url = "https://files.pythonhosted.org/packages/38/7d/f457626e3cd3c29b3a49ca115a304cebb8cc6f31b04678f03b216899d3c6/scipy-1.15.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:1c832e1bd78dea67d5c16f786681b28dd695a8cb1fb90af2e27580d3d0967e92", size = 25121732, upload-time = "2025-05-08T16:04:36.596Z" }, + { url = "https://files.pythonhosted.org/packages/db/0a/92b1de4a7adc7a15dcf5bddc6e191f6f29ee663b30511ce20467ef9b82e4/scipy-1.15.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:263961f658ce2165bbd7b99fa5135195c3a12d9bef045345016b8b50c315cb82", size = 35547617, upload-time = "2025-05-08T16:04:43.546Z" }, + { url = "https://files.pythonhosted.org/packages/8e/6d/41991e503e51fc1134502694c5fa7a1671501a17ffa12716a4a9151af3df/scipy-1.15.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2abc762b0811e09a0d3258abee2d98e0c703eee49464ce0069590846f31d40", size = 37662964, upload-time = "2025-05-08T16:04:49.431Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/3df8f83cb15f3500478c889be8fb18700813b95e9e087328230b98d547ff/scipy-1.15.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ed7284b21a7a0c8f1b6e5977ac05396c0d008b89e05498c8b7e8f4a1423bba0e", size = 37238749, upload-time = "2025-05-08T16:04:55.215Z" }, + { url = "https://files.pythonhosted.org/packages/93/3e/b3257cf446f2a3533ed7809757039016b74cd6f38271de91682aa844cfc5/scipy-1.15.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5380741e53df2c566f4d234b100a484b420af85deb39ea35a1cc1be84ff53a5c", size = 40022383, upload-time = "2025-05-08T16:05:01.914Z" }, + { url = "https://files.pythonhosted.org/packages/d1/84/55bc4881973d3f79b479a5a2e2df61c8c9a04fcb986a213ac9c02cfb659b/scipy-1.15.3-cp310-cp310-win_amd64.whl", hash = "sha256:9d61e97b186a57350f6d6fd72640f9e99d5a4a2b8fbf4b9ee9a841eab327dc13", size = 41259201, upload-time = "2025-05-08T16:05:08.166Z" }, + { url = "https://files.pythonhosted.org/packages/96/ab/5cc9f80f28f6a7dff646c5756e559823614a42b1939d86dd0ed550470210/scipy-1.15.3-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:993439ce220d25e3696d1b23b233dd010169b62f6456488567e830654ee37a6b", size = 38714255, upload-time = "2025-05-08T16:05:14.596Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/66ba30abe5ad1a3ad15bfb0b59d22174012e8056ff448cb1644deccbfed2/scipy-1.15.3-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:34716e281f181a02341ddeaad584205bd2fd3c242063bd3423d61ac259ca7eba", size = 30111035, upload-time = "2025-05-08T16:05:20.152Z" }, + { url = "https://files.pythonhosted.org/packages/4b/fa/a7e5b95afd80d24313307f03624acc65801846fa75599034f8ceb9e2cbf6/scipy-1.15.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3b0334816afb8b91dab859281b1b9786934392aa3d527cd847e41bb6f45bee65", size = 22384499, upload-time = "2025-05-08T16:05:24.494Z" }, + { url = "https://files.pythonhosted.org/packages/17/99/f3aaddccf3588bb4aea70ba35328c204cadd89517a1612ecfda5b2dd9d7a/scipy-1.15.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:6db907c7368e3092e24919b5e31c76998b0ce1684d51a90943cb0ed1b4ffd6c1", size = 25152602, upload-time = "2025-05-08T16:05:29.313Z" }, + { url = "https://files.pythonhosted.org/packages/56/c5/1032cdb565f146109212153339f9cb8b993701e9fe56b1c97699eee12586/scipy-1.15.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:721d6b4ef5dc82ca8968c25b111e307083d7ca9091bc38163fb89243e85e3889", size = 35503415, upload-time = "2025-05-08T16:05:34.699Z" }, + { url = "https://files.pythonhosted.org/packages/bd/37/89f19c8c05505d0601ed5650156e50eb881ae3918786c8fd7262b4ee66d3/scipy-1.15.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39cb9c62e471b1bb3750066ecc3a3f3052b37751c7c3dfd0fd7e48900ed52982", size = 37652622, upload-time = "2025-05-08T16:05:40.762Z" }, + { url = "https://files.pythonhosted.org/packages/7e/31/be59513aa9695519b18e1851bb9e487de66f2d31f835201f1b42f5d4d475/scipy-1.15.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:795c46999bae845966368a3c013e0e00947932d68e235702b5c3f6ea799aa8c9", size = 37244796, upload-time = "2025-05-08T16:05:48.119Z" }, + { url = "https://files.pythonhosted.org/packages/10/c0/4f5f3eeccc235632aab79b27a74a9130c6c35df358129f7ac8b29f562ac7/scipy-1.15.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:18aaacb735ab38b38db42cb01f6b92a2d0d4b6aabefeb07f02849e47f8fb3594", size = 40047684, upload-time = "2025-05-08T16:05:54.22Z" }, + { url = "https://files.pythonhosted.org/packages/ab/a7/0ddaf514ce8a8714f6ed243a2b391b41dbb65251affe21ee3077ec45ea9a/scipy-1.15.3-cp311-cp311-win_amd64.whl", hash = "sha256:ae48a786a28412d744c62fd7816a4118ef97e5be0bee968ce8f0a2fba7acf3bb", size = 41246504, upload-time = "2025-05-08T16:06:00.437Z" }, + { url = "https://files.pythonhosted.org/packages/37/4b/683aa044c4162e10ed7a7ea30527f2cbd92e6999c10a8ed8edb253836e9c/scipy-1.15.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6ac6310fdbfb7aa6612408bd2f07295bcbd3fda00d2d702178434751fe48e019", size = 38766735, upload-time = "2025-05-08T16:06:06.471Z" }, + { url = "https://files.pythonhosted.org/packages/7b/7e/f30be3d03de07f25dc0ec926d1681fed5c732d759ac8f51079708c79e680/scipy-1.15.3-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:185cd3d6d05ca4b44a8f1595af87f9c372bb6acf9c808e99aa3e9aa03bd98cf6", size = 30173284, upload-time = "2025-05-08T16:06:11.686Z" }, + { url = "https://files.pythonhosted.org/packages/07/9c/0ddb0d0abdabe0d181c1793db51f02cd59e4901da6f9f7848e1f96759f0d/scipy-1.15.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:05dc6abcd105e1a29f95eada46d4a3f251743cfd7d3ae8ddb4088047f24ea477", size = 22446958, upload-time = "2025-05-08T16:06:15.97Z" }, + { url = "https://files.pythonhosted.org/packages/af/43/0bce905a965f36c58ff80d8bea33f1f9351b05fad4beaad4eae34699b7a1/scipy-1.15.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:06efcba926324df1696931a57a176c80848ccd67ce6ad020c810736bfd58eb1c", size = 25242454, upload-time = "2025-05-08T16:06:20.394Z" }, + { url = "https://files.pythonhosted.org/packages/56/30/a6f08f84ee5b7b28b4c597aca4cbe545535c39fe911845a96414700b64ba/scipy-1.15.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05045d8b9bfd807ee1b9f38761993297b10b245f012b11b13b91ba8945f7e45", size = 35210199, upload-time = "2025-05-08T16:06:26.159Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1f/03f52c282437a168ee2c7c14a1a0d0781a9a4a8962d84ac05c06b4c5b555/scipy-1.15.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271e3713e645149ea5ea3e97b57fdab61ce61333f97cfae392c28ba786f9bb49", size = 37309455, upload-time = "2025-05-08T16:06:32.778Z" }, + { url = "https://files.pythonhosted.org/packages/89/b1/fbb53137f42c4bf630b1ffdfc2151a62d1d1b903b249f030d2b1c0280af8/scipy-1.15.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6cfd56fc1a8e53f6e89ba3a7a7251f7396412d655bca2aa5611c8ec9a6784a1e", size = 36885140, upload-time = "2025-05-08T16:06:39.249Z" }, + { url = "https://files.pythonhosted.org/packages/2e/2e/025e39e339f5090df1ff266d021892694dbb7e63568edcfe43f892fa381d/scipy-1.15.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ff17c0bb1cb32952c09217d8d1eed9b53d1463e5f1dd6052c7857f83127d539", size = 39710549, upload-time = "2025-05-08T16:06:45.729Z" }, + { url = "https://files.pythonhosted.org/packages/e6/eb/3bf6ea8ab7f1503dca3a10df2e4b9c3f6b3316df07f6c0ded94b281c7101/scipy-1.15.3-cp312-cp312-win_amd64.whl", hash = "sha256:52092bc0472cfd17df49ff17e70624345efece4e1a12b23783a1ac59a1b728ed", size = 40966184, upload-time = "2025-05-08T16:06:52.623Z" }, + { url = "https://files.pythonhosted.org/packages/73/18/ec27848c9baae6e0d6573eda6e01a602e5649ee72c27c3a8aad673ebecfd/scipy-1.15.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2c620736bcc334782e24d173c0fdbb7590a0a436d2fdf39310a8902505008759", size = 38728256, upload-time = "2025-05-08T16:06:58.696Z" }, + { url = "https://files.pythonhosted.org/packages/74/cd/1aef2184948728b4b6e21267d53b3339762c285a46a274ebb7863c9e4742/scipy-1.15.3-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:7e11270a000969409d37ed399585ee530b9ef6aa99d50c019de4cb01e8e54e62", size = 30109540, upload-time = "2025-05-08T16:07:04.209Z" }, + { url = "https://files.pythonhosted.org/packages/5b/d8/59e452c0a255ec352bd0a833537a3bc1bfb679944c4938ab375b0a6b3a3e/scipy-1.15.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8c9ed3ba2c8a2ce098163a9bdb26f891746d02136995df25227a20e71c396ebb", size = 22383115, upload-time = "2025-05-08T16:07:08.998Z" }, + { url = "https://files.pythonhosted.org/packages/08/f5/456f56bbbfccf696263b47095291040655e3cbaf05d063bdc7c7517f32ac/scipy-1.15.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0bdd905264c0c9cfa74a4772cdb2070171790381a5c4d312c973382fc6eaf730", size = 25163884, upload-time = "2025-05-08T16:07:14.091Z" }, + { url = "https://files.pythonhosted.org/packages/a2/66/a9618b6a435a0f0c0b8a6d0a2efb32d4ec5a85f023c2b79d39512040355b/scipy-1.15.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79167bba085c31f38603e11a267d862957cbb3ce018d8b38f79ac043bc92d825", size = 35174018, upload-time = "2025-05-08T16:07:19.427Z" }, + { url = "https://files.pythonhosted.org/packages/b5/09/c5b6734a50ad4882432b6bb7c02baf757f5b2f256041da5df242e2d7e6b6/scipy-1.15.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9deabd6d547aee2c9a81dee6cc96c6d7e9a9b1953f74850c179f91fdc729cb7", size = 37269716, upload-time = "2025-05-08T16:07:25.712Z" }, + { url = "https://files.pythonhosted.org/packages/77/0a/eac00ff741f23bcabd352731ed9b8995a0a60ef57f5fd788d611d43d69a1/scipy-1.15.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:dde4fc32993071ac0c7dd2d82569e544f0bdaff66269cb475e0f369adad13f11", size = 36872342, upload-time = "2025-05-08T16:07:31.468Z" }, + { url = "https://files.pythonhosted.org/packages/fe/54/4379be86dd74b6ad81551689107360d9a3e18f24d20767a2d5b9253a3f0a/scipy-1.15.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f77f853d584e72e874d87357ad70f44b437331507d1c311457bed8ed2b956126", size = 39670869, upload-time = "2025-05-08T16:07:38.002Z" }, + { url = "https://files.pythonhosted.org/packages/87/2e/892ad2862ba54f084ffe8cc4a22667eaf9c2bcec6d2bff1d15713c6c0703/scipy-1.15.3-cp313-cp313-win_amd64.whl", hash = "sha256:b90ab29d0c37ec9bf55424c064312930ca5f4bde15ee8619ee44e69319aab163", size = 40988851, upload-time = "2025-05-08T16:08:33.671Z" }, + { url = "https://files.pythonhosted.org/packages/1b/e9/7a879c137f7e55b30d75d90ce3eb468197646bc7b443ac036ae3fe109055/scipy-1.15.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3ac07623267feb3ae308487c260ac684b32ea35fd81e12845039952f558047b8", size = 38863011, upload-time = "2025-05-08T16:07:44.039Z" }, + { url = "https://files.pythonhosted.org/packages/51/d1/226a806bbd69f62ce5ef5f3ffadc35286e9fbc802f606a07eb83bf2359de/scipy-1.15.3-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:6487aa99c2a3d509a5227d9a5e889ff05830a06b2ce08ec30df6d79db5fcd5c5", size = 30266407, upload-time = "2025-05-08T16:07:49.891Z" }, + { url = "https://files.pythonhosted.org/packages/e5/9b/f32d1d6093ab9eeabbd839b0f7619c62e46cc4b7b6dbf05b6e615bbd4400/scipy-1.15.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:50f9e62461c95d933d5c5ef4a1f2ebf9a2b4e83b0db374cb3f1de104d935922e", size = 22540030, upload-time = "2025-05-08T16:07:54.121Z" }, + { url = "https://files.pythonhosted.org/packages/e7/29/c278f699b095c1a884f29fda126340fcc201461ee8bfea5c8bdb1c7c958b/scipy-1.15.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:14ed70039d182f411ffc74789a16df3835e05dc469b898233a245cdfd7f162cb", size = 25218709, upload-time = "2025-05-08T16:07:58.506Z" }, + { url = "https://files.pythonhosted.org/packages/24/18/9e5374b617aba742a990581373cd6b68a2945d65cc588482749ef2e64467/scipy-1.15.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a769105537aa07a69468a0eefcd121be52006db61cdd8cac8a0e68980bbb723", size = 34809045, upload-time = "2025-05-08T16:08:03.929Z" }, + { url = "https://files.pythonhosted.org/packages/e1/fe/9c4361e7ba2927074360856db6135ef4904d505e9b3afbbcb073c4008328/scipy-1.15.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9db984639887e3dffb3928d118145ffe40eff2fa40cb241a306ec57c219ebbbb", size = 36703062, upload-time = "2025-05-08T16:08:09.558Z" }, + { url = "https://files.pythonhosted.org/packages/b7/8e/038ccfe29d272b30086b25a4960f757f97122cb2ec42e62b460d02fe98e9/scipy-1.15.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:40e54d5c7e7ebf1aa596c374c49fa3135f04648a0caabcb66c52884b943f02b4", size = 36393132, upload-time = "2025-05-08T16:08:15.34Z" }, + { url = "https://files.pythonhosted.org/packages/10/7e/5c12285452970be5bdbe8352c619250b97ebf7917d7a9a9e96b8a8140f17/scipy-1.15.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:5e721fed53187e71d0ccf382b6bf977644c533e506c4d33c3fb24de89f5c3ed5", size = 38979503, upload-time = "2025-05-08T16:08:21.513Z" }, + { url = "https://files.pythonhosted.org/packages/81/06/0a5e5349474e1cbc5757975b21bd4fad0e72ebf138c5592f191646154e06/scipy-1.15.3-cp313-cp313t-win_amd64.whl", hash = "sha256:76ad1fb5f8752eabf0fa02e4cc0336b4e8f021e2d5f061ed37d6d264db35e3ca", size = 40308097, upload-time = "2025-05-08T16:08:27.627Z" }, +] + +[[package]] +name = "scipy" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.11.*' and sys_platform == 'win32'", + "python_full_version == '3.11.*' and sys_platform == 'emscripten'", + "python_full_version == '3.11.*' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675, upload-time = "2026-02-23T00:16:00.13Z" }, + { url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057, upload-time = "2026-02-23T00:16:09.456Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032, upload-time = "2026-02-23T00:16:17.358Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533, upload-time = "2026-02-23T00:16:25.791Z" }, + { url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057, upload-time = "2026-02-23T00:16:36.931Z" }, + { url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300, upload-time = "2026-02-23T00:16:49.108Z" }, + { url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333, upload-time = "2026-02-23T00:17:01.293Z" }, + { url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314, upload-time = "2026-02-23T00:17:12.576Z" }, + { url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512, upload-time = "2026-02-23T00:17:23.424Z" }, + { url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248, upload-time = "2026-02-23T00:17:34.561Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954, upload-time = "2026-02-23T00:17:49.855Z" }, + { url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662, upload-time = "2026-02-23T00:18:01.64Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366, upload-time = "2026-02-23T00:18:12.015Z" }, + { url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017, upload-time = "2026-02-23T00:18:21.502Z" }, + { url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842, upload-time = "2026-02-23T00:18:35.367Z" }, + { url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890, upload-time = "2026-02-23T00:18:49.188Z" }, + { url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557, upload-time = "2026-02-23T00:18:54.74Z" }, + { url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856, upload-time = "2026-02-23T00:19:00.307Z" }, + { url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682, upload-time = "2026-02-23T00:19:07.67Z" }, + { url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340, upload-time = "2026-02-23T00:19:12.024Z" }, + { url = "https://files.pythonhosted.org/packages/76/27/07ee1b57b65e92645f219b37148a7e7928b82e2b5dbeccecb4dff7c64f0b/scipy-1.17.1-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:5e3c5c011904115f88a39308379c17f91546f77c1667cea98739fe0fccea804c", size = 31590199, upload-time = "2026-02-23T00:19:17.192Z" }, + { url = "https://files.pythonhosted.org/packages/ec/ae/db19f8ab842e9b724bf5dbb7db29302a91f1e55bc4d04b1025d6d605a2c5/scipy-1.17.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:6fac755ca3d2c3edcb22f479fceaa241704111414831ddd3bc6056e18516892f", size = 28154001, upload-time = "2026-02-23T00:19:22.241Z" }, + { url = "https://files.pythonhosted.org/packages/5b/58/3ce96251560107b381cbd6e8413c483bbb1228a6b919fa8652b0d4090e7f/scipy-1.17.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:7ff200bf9d24f2e4d5dc6ee8c3ac64d739d3a89e2326ba68aaf6c4a2b838fd7d", size = 20325719, upload-time = "2026-02-23T00:19:26.329Z" }, + { url = "https://files.pythonhosted.org/packages/b2/83/15087d945e0e4d48ce2377498abf5ad171ae013232ae31d06f336e64c999/scipy-1.17.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4b400bdc6f79fa02a4d86640310dde87a21fba0c979efff5248908c6f15fad1b", size = 22683595, upload-time = "2026-02-23T00:19:30.304Z" }, + { url = "https://files.pythonhosted.org/packages/b4/e0/e58fbde4a1a594c8be8114eb4aac1a55bcd6587047efc18a61eb1f5c0d30/scipy-1.17.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2b64ca7d4aee0102a97f3ba22124052b4bd2152522355073580bf4845e2550b6", size = 32896429, upload-time = "2026-02-23T00:19:35.536Z" }, + { url = "https://files.pythonhosted.org/packages/f5/5f/f17563f28ff03c7b6799c50d01d5d856a1d55f2676f537ca8d28c7f627cd/scipy-1.17.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:581b2264fc0aa555f3f435a5944da7504ea3a065d7029ad60e7c3d1ae09c5464", size = 35203952, upload-time = "2026-02-23T00:19:42.259Z" }, + { url = "https://files.pythonhosted.org/packages/8d/a5/9afd17de24f657fdfe4df9a3f1ea049b39aef7c06000c13db1530d81ccca/scipy-1.17.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:beeda3d4ae615106d7094f7e7cef6218392e4465cc95d25f900bebabfded0950", size = 34979063, upload-time = "2026-02-23T00:19:47.547Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/88b1d2384b424bf7c924f2038c1c409f8d88bb2a8d49d097861dd64a57b2/scipy-1.17.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6609bc224e9568f65064cfa72edc0f24ee6655b47575954ec6339534b2798369", size = 37598449, upload-time = "2026-02-23T00:19:53.238Z" }, + { url = "https://files.pythonhosted.org/packages/35/e5/d6d0e51fc888f692a35134336866341c08655d92614f492c6860dc45bb2c/scipy-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:37425bc9175607b0268f493d79a292c39f9d001a357bebb6b88fdfaff13f6448", size = 36510943, upload-time = "2026-02-23T00:20:50.89Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fd/3be73c564e2a01e690e19cc618811540ba5354c67c8680dce3281123fb79/scipy-1.17.1-cp313-cp313-win_arm64.whl", hash = "sha256:5cf36e801231b6a2059bf354720274b7558746f3b1a4efb43fcf557ccd484a87", size = 24545621, upload-time = "2026-02-23T00:20:55.871Z" }, + { url = "https://files.pythonhosted.org/packages/6f/6b/17787db8b8114933a66f9dcc479a8272e4b4da75fe03b0c282f7b0ade8cd/scipy-1.17.1-cp313-cp313t-macosx_10_14_x86_64.whl", hash = "sha256:d59c30000a16d8edc7e64152e30220bfbd724c9bbb08368c054e24c651314f0a", size = 31936708, upload-time = "2026-02-23T00:19:58.694Z" }, + { url = "https://files.pythonhosted.org/packages/38/2e/524405c2b6392765ab1e2b722a41d5da33dc5c7b7278184a8ad29b6cb206/scipy-1.17.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:010f4333c96c9bb1a4516269e33cb5917b08ef2166d5556ca2fd9f082a9e6ea0", size = 28570135, upload-time = "2026-02-23T00:20:03.934Z" }, + { url = "https://files.pythonhosted.org/packages/fd/c3/5bd7199f4ea8556c0c8e39f04ccb014ac37d1468e6cfa6a95c6b3562b76e/scipy-1.17.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2ceb2d3e01c5f1d83c4189737a42d9cb2fc38a6eeed225e7515eef71ad301dce", size = 20741977, upload-time = "2026-02-23T00:20:07.935Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b8/8ccd9b766ad14c78386599708eb745f6b44f08400a5fd0ade7cf89b6fc93/scipy-1.17.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:844e165636711ef41f80b4103ed234181646b98a53c8f05da12ca5ca289134f6", size = 23029601, upload-time = "2026-02-23T00:20:12.161Z" }, + { url = "https://files.pythonhosted.org/packages/6d/a0/3cb6f4d2fb3e17428ad2880333cac878909ad1a89f678527b5328b93c1d4/scipy-1.17.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:158dd96d2207e21c966063e1635b1063cd7787b627b6f07305315dd73d9c679e", size = 33019667, upload-time = "2026-02-23T00:20:17.208Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c3/2d834a5ac7bf3a0c806ad1508efc02dda3c8c61472a56132d7894c312dea/scipy-1.17.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:74cbb80d93260fe2ffa334efa24cb8f2f0f622a9b9febf8b483c0b865bfb3475", size = 35264159, upload-time = "2026-02-23T00:20:23.087Z" }, + { url = "https://files.pythonhosted.org/packages/4d/77/d3ed4becfdbd217c52062fafe35a72388d1bd82c2d0ba5ca19d6fcc93e11/scipy-1.17.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:dbc12c9f3d185f5c737d801da555fb74b3dcfa1a50b66a1a93e09190f41fab50", size = 35102771, upload-time = "2026-02-23T00:20:28.636Z" }, + { url = "https://files.pythonhosted.org/packages/bd/12/d19da97efde68ca1ee5538bb261d5d2c062f0c055575128f11a2730e3ac1/scipy-1.17.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:94055a11dfebe37c656e70317e1996dc197e1a15bbcc351bcdd4610e128fe1ca", size = 37665910, upload-time = "2026-02-23T00:20:34.743Z" }, + { url = "https://files.pythonhosted.org/packages/06/1c/1172a88d507a4baaf72c5a09bb6c018fe2ae0ab622e5830b703a46cc9e44/scipy-1.17.1-cp313-cp313t-win_amd64.whl", hash = "sha256:e30bdeaa5deed6bc27b4cc490823cd0347d7dae09119b8803ae576ea0ce52e4c", size = 36562980, upload-time = "2026-02-23T00:20:40.575Z" }, + { url = "https://files.pythonhosted.org/packages/70/b0/eb757336e5a76dfa7911f63252e3b7d1de00935d7705cf772db5b45ec238/scipy-1.17.1-cp313-cp313t-win_arm64.whl", hash = "sha256:a720477885a9d2411f94a93d16f9d89bad0f28ca23c3f8daa521e2dcc3f44d49", size = 24856543, upload-time = "2026-02-23T00:20:45.313Z" }, + { url = "https://files.pythonhosted.org/packages/cf/83/333afb452af6f0fd70414dc04f898647ee1423979ce02efa75c3b0f2c28e/scipy-1.17.1-cp314-cp314-macosx_10_14_x86_64.whl", hash = "sha256:a48a72c77a310327f6a3a920092fa2b8fd03d7deaa60f093038f22d98e096717", size = 31584510, upload-time = "2026-02-23T00:21:01.015Z" }, + { url = "https://files.pythonhosted.org/packages/ed/a6/d05a85fd51daeb2e4ea71d102f15b34fedca8e931af02594193ae4fd25f7/scipy-1.17.1-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:45abad819184f07240d8a696117a7aacd39787af9e0b719d00285549ed19a1e9", size = 28170131, upload-time = "2026-02-23T00:21:05.888Z" }, + { url = "https://files.pythonhosted.org/packages/db/7b/8624a203326675d7746a254083a187398090a179335b2e4a20e2ddc46e83/scipy-1.17.1-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:3fd1fcdab3ea951b610dc4cef356d416d5802991e7e32b5254828d342f7b7e0b", size = 20342032, upload-time = "2026-02-23T00:21:09.904Z" }, + { url = "https://files.pythonhosted.org/packages/c9/35/2c342897c00775d688d8ff3987aced3426858fd89d5a0e26e020b660b301/scipy-1.17.1-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:7bdf2da170b67fdf10bca777614b1c7d96ae3ca5794fd9587dce41eb2966e866", size = 22678766, upload-time = "2026-02-23T00:21:14.313Z" }, + { url = "https://files.pythonhosted.org/packages/ef/f2/7cdb8eb308a1a6ae1e19f945913c82c23c0c442a462a46480ce487fdc0ac/scipy-1.17.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adb2642e060a6549c343603a3851ba76ef0b74cc8c079a9a58121c7ec9fe2350", size = 32957007, upload-time = "2026-02-23T00:21:19.663Z" }, + { url = "https://files.pythonhosted.org/packages/0b/2e/7eea398450457ecb54e18e9d10110993fa65561c4f3add5e8eccd2b9cd41/scipy-1.17.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eee2cfda04c00a857206a4330f0c5e3e56535494e30ca445eb19ec624ae75118", size = 35221333, upload-time = "2026-02-23T00:21:25.278Z" }, + { url = "https://files.pythonhosted.org/packages/d9/77/5b8509d03b77f093a0d52e606d3c4f79e8b06d1d38c441dacb1e26cacf46/scipy-1.17.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d2650c1fb97e184d12d8ba010493ee7b322864f7d3d00d3f9bb97d9c21de4068", size = 35042066, upload-time = "2026-02-23T00:21:31.358Z" }, + { url = "https://files.pythonhosted.org/packages/f9/df/18f80fb99df40b4070328d5ae5c596f2f00fffb50167e31439e932f29e7d/scipy-1.17.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:08b900519463543aa604a06bec02461558a6e1cef8fdbb8098f77a48a83c8118", size = 37612763, upload-time = "2026-02-23T00:21:37.247Z" }, + { url = "https://files.pythonhosted.org/packages/4b/39/f0e8ea762a764a9dc52aa7dabcfad51a354819de1f0d4652b6a1122424d6/scipy-1.17.1-cp314-cp314-win_amd64.whl", hash = "sha256:3877ac408e14da24a6196de0ddcace62092bfc12a83823e92e49e40747e52c19", size = 37290984, upload-time = "2026-02-23T00:22:35.023Z" }, + { url = "https://files.pythonhosted.org/packages/7c/56/fe201e3b0f93d1a8bcf75d3379affd228a63d7e2d80ab45467a74b494947/scipy-1.17.1-cp314-cp314-win_arm64.whl", hash = "sha256:f8885db0bc2bffa59d5c1b72fad7a6a92d3e80e7257f967dd81abb553a90d293", size = 25192877, upload-time = "2026-02-23T00:22:39.798Z" }, + { url = "https://files.pythonhosted.org/packages/96/ad/f8c414e121f82e02d76f310f16db9899c4fcde36710329502a6b2a3c0392/scipy-1.17.1-cp314-cp314t-macosx_10_14_x86_64.whl", hash = "sha256:1cc682cea2ae55524432f3cdff9e9a3be743d52a7443d0cba9017c23c87ae2f6", size = 31949750, upload-time = "2026-02-23T00:21:42.289Z" }, + { url = "https://files.pythonhosted.org/packages/7c/b0/c741e8865d61b67c81e255f4f0a832846c064e426636cd7de84e74d209be/scipy-1.17.1-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:2040ad4d1795a0ae89bfc7e8429677f365d45aa9fd5e4587cf1ea737f927b4a1", size = 28585858, upload-time = "2026-02-23T00:21:47.706Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1b/3985219c6177866628fa7c2595bfd23f193ceebbe472c98a08824b9466ff/scipy-1.17.1-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:131f5aaea57602008f9822e2115029b55d4b5f7c070287699fe45c661d051e39", size = 20757723, upload-time = "2026-02-23T00:21:52.039Z" }, + { url = "https://files.pythonhosted.org/packages/c0/19/2a04aa25050d656d6f7b9e7b685cc83d6957fb101665bfd9369ca6534563/scipy-1.17.1-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9cdc1a2fcfd5c52cfb3045feb399f7b3ce822abdde3a193a6b9a60b3cb5854ca", size = 23043098, upload-time = "2026-02-23T00:21:56.185Z" }, + { url = "https://files.pythonhosted.org/packages/86/f1/3383beb9b5d0dbddd030335bf8a8b32d4317185efe495374f134d8be6cce/scipy-1.17.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e3dcd57ab780c741fde8dc68619de988b966db759a3c3152e8e9142c26295ad", size = 33030397, upload-time = "2026-02-23T00:22:01.404Z" }, + { url = "https://files.pythonhosted.org/packages/41/68/8f21e8a65a5a03f25a79165ec9d2b28c00e66dc80546cf5eb803aeeff35b/scipy-1.17.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a9956e4d4f4a301ebf6cde39850333a6b6110799d470dbbb1e25326ac447f52a", size = 35281163, upload-time = "2026-02-23T00:22:07.024Z" }, + { url = "https://files.pythonhosted.org/packages/84/8d/c8a5e19479554007a5632ed7529e665c315ae7492b4f946b0deb39870e39/scipy-1.17.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a4328d245944d09fd639771de275701ccadf5f781ba0ff092ad141e017eccda4", size = 35116291, upload-time = "2026-02-23T00:22:12.585Z" }, + { url = "https://files.pythonhosted.org/packages/52/52/e57eceff0e342a1f50e274264ed47497b59e6a4e3118808ee58ddda7b74a/scipy-1.17.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a77cbd07b940d326d39a1d1b37817e2ee4d79cb30e7338f3d0cddffae70fcaa2", size = 37682317, upload-time = "2026-02-23T00:22:18.513Z" }, + { url = "https://files.pythonhosted.org/packages/11/2f/b29eafe4a3fbc3d6de9662b36e028d5f039e72d345e05c250e121a230dd4/scipy-1.17.1-cp314-cp314t-win_amd64.whl", hash = "sha256:eb092099205ef62cd1782b006658db09e2fed75bffcae7cc0d44052d8aa0f484", size = 37345327, upload-time = "2026-02-23T00:22:24.442Z" }, + { url = "https://files.pythonhosted.org/packages/07/39/338d9219c4e87f3e708f18857ecd24d22a0c3094752393319553096b98af/scipy-1.17.1-cp314-cp314t-win_arm64.whl", hash = "sha256:200e1050faffacc162be6a486a984a0497866ec54149a01270adc8a59b7c7d21", size = 25489165, upload-time = "2026-02-23T00:22:29.563Z" }, +] + +[[package]] +name = "scipy" +version = "1.18.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'win32'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform == 'emscripten'", + "python_full_version >= '3.12' and python_full_version < '3.14' and sys_platform != 'emscripten' and sys_platform != 'win32'", +] +dependencies = [ + { name = "numpy", version = "2.5.2", source = { registry = "https://pypi.org/simple" } }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/25/c2700dfaf6442b4effaa91af24ebce5dc9d31bb4a69706313aae70d72cd0/scipy-1.18.0.tar.gz", hash = "sha256:67b2ad2ad54c72ca6d04975a9b2df8c3638c34ddd5b28738e94fc2b57929d378", size = 30774447, upload-time = "2026-06-19T15:01:43.456Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/19/ca10ead60b0acc80b2b833c2c4a4f2ff753d0f58b811f70d911c7e94a25c/scipy-1.18.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:7bd21faaf5a1a3b2eff922d02db5f191b99a6518db9078a8fb23169f6d22259a", size = 31056519, upload-time = "2026-06-19T14:59:45.203Z" }, + { url = "https://files.pythonhosted.org/packages/96/72/1e6442a00cd2924d361aa1b642ab6373ec35c6fabf311a760be9f76e0f13/scipy-1.18.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:265915e79107de9f946b855e50d7470d5893ec3f54b342e1aa6201cbdcd8bb6b", size = 28681889, upload-time = "2026-06-19T14:59:48.103Z" }, + { url = "https://files.pythonhosted.org/packages/9b/2d/11dd93d21e147a73ba22bd75c0b9208d3a2e0ec76d53170ce7d9029b1015/scipy-1.18.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9ab7b758be6940954a713ee466e2043e9f6e2ed965c1fce5c91039f4be3d90a9", size = 20423580, upload-time = "2026-06-19T14:59:50.665Z" }, + { url = "https://files.pythonhosted.org/packages/9c/01/93552f75e0d2a7dd115a45e59209c51e8d514daff02fc887d2623be06fe1/scipy-1.18.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:97b6cddaaee0a779ef6b5ca83c9604b27cc16b2b8fc22c142652df8793319fb8", size = 23054441, upload-time = "2026-06-19T14:59:53.564Z" }, + { url = "https://files.pythonhosted.org/packages/3c/23/21f5e703643d66f21faa6b4c73195bfcad70c55efcb4f1ab327cd7c4101a/scipy-1.18.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:52a96e21517c7292375c0e27dd796a811f03fcea5fd4d108fdfea8145dcf17ab", size = 33968720, upload-time = "2026-06-19T14:59:56.415Z" }, + { url = "https://files.pythonhosted.org/packages/dd/aa/1b939f6c67ed68635bb538e6752d3dacc02f66535182e939a89581a44e9c/scipy-1.18.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f55797419e16e7f30cf88ffb3113ce0467f00cfe3f70d5c281730b21769bfc2", size = 35287115, upload-time = "2026-06-19T14:59:59.411Z" }, + { url = "https://files.pythonhosted.org/packages/b6/ff/eec46be7e9234208f801062b53e1983085eddebd693f6c9bfb03b459830d/scipy-1.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ad033410e2e0672ffdc1042110cef20e1c46f8fd0616cee1d44d8d58fad8fc11", size = 35577989, upload-time = "2026-06-19T15:00:02.235Z" }, + { url = "https://files.pythonhosted.org/packages/84/ca/210d4759c7210bb7d269437421959b39a33434e2776b60c5cb8a763bb30a/scipy-1.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a55985d54c769c872e64b7f4c8a81cc30ef700cc04296abbbf3705439c126de", size = 37421717, upload-time = "2026-06-19T15:00:05.102Z" }, + { url = "https://files.pythonhosted.org/packages/2b/54/9a9edb45345bd6744da5ddfb6628e5d5185920494c6a67ec45b6381004cb/scipy-1.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:71ccc8faa2dd16ac310233203474a8b5cb67f10dedd54a3116d34943f4b19132", size = 36597428, upload-time = "2026-06-19T15:00:08.112Z" }, + { url = "https://files.pythonhosted.org/packages/99/0e/33f32a2a58987e26aec0f7df252cbbad1e90ae77bdbc76f40dd4ed0cf0ea/scipy-1.18.0-cp312-cp312-win_arm64.whl", hash = "sha256:d88363fd9d8fbd3511bd273f1a49efb2a540773ddf92a91d57498ce7dd7f3e76", size = 24351481, upload-time = "2026-06-19T15:00:11.103Z" }, + { url = "https://files.pythonhosted.org/packages/05/52/9c0136c2de7ae0779b7b366447766cec6d9f0702c56bb8ffeb04c8fd3af4/scipy-1.18.0-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:09143f676d157d9f546d663504ef9c1becb819824f1afc018814176411942446", size = 31036107, upload-time = "2026-06-19T15:00:14.03Z" }, + { url = "https://files.pythonhosted.org/packages/02/73/0291a64843270f4efb86cdcf2ee0f2048631b65ec6b405398b2b4dbf11bf/scipy-1.18.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:5efe260f69417b97ddae455bfb5a95e8359f7f66ad7fa9522a60feb66f169520", size = 28663303, upload-time = "2026-06-19T15:00:16.819Z" }, + { url = "https://files.pythonhosted.org/packages/d3/0f/10ffa0b697a572f4e0d48b92a88895d366422f019f723e7e14a84c050dac/scipy-1.18.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:68363b7eaacd8b5dd426df56d782cc156468ac79a127a1b87ca597d6e2e82197", size = 20404960, upload-time = "2026-06-19T15:00:19.635Z" }, + { url = "https://files.pythonhosted.org/packages/7e/d2/e896cea21ba8edd6c81d4c55b1ffcc717e79698dcbebf9641b4cfb4c6622/scipy-1.18.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:c5557d8be5da8e41353fcd4d21491fdbab83b062fc579e94dc09a7c8ab4f669b", size = 23034074, upload-time = "2026-06-19T15:00:22.107Z" }, + { url = "https://files.pythonhosted.org/packages/ea/b2/e83ea34279a52c03374477c74006256ec78df65fc877baa4617d6de1d202/scipy-1.18.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0d13bca67c096d89fb95ced0d8921807300fce0275643aef9533cc63a0773468", size = 33942038, upload-time = "2026-06-19T15:00:24.964Z" }, + { url = "https://files.pythonhosted.org/packages/f6/af/e8fe5fb136f51e2b01678b92cb4106d10d8cd68ec147ead2e7cb0ac75398/scipy-1.18.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a46f9273dbd0eb1cefba61c9b8648b4dfe3cbc14a080176f9a73e44b8336dc7f", size = 35266390, upload-time = "2026-06-19T15:00:28.059Z" }, + { url = "https://files.pythonhosted.org/packages/3a/49/2c5cbb907b56695fc67517811d1db234dfd83381a84814ec220aded2794d/scipy-1.18.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5aba46108853ddfc77906b6557aac839d2b52e900c1d72a1180adaaab58d265f", size = 35551324, upload-time = "2026-06-19T15:00:31.014Z" }, + { url = "https://files.pythonhosted.org/packages/bb/73/eda39f7a2d306ff0ffc574afd13c0bbb6d10a603d9a413998ee269487a80/scipy-1.18.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b6f758e35f12757b5d95c00bc6de2438e229c2664b7a92e96f205959d9f2dfa4", size = 37404785, upload-time = "2026-06-19T15:00:34.072Z" }, + { url = "https://files.pythonhosted.org/packages/b7/d2/ae881ee28d014f38e0ccbfd974a06a919ba9af34f1f74bf42b5301891d63/scipy-1.18.0-cp313-cp313-win_amd64.whl", hash = "sha256:1afac4a847207c7ff8efd321734a50b06d0280b3b2a2c0fc2f413101747ad7c7", size = 36554943, upload-time = "2026-06-19T15:00:36.903Z" }, + { url = "https://files.pythonhosted.org/packages/70/3a/21154e2d54eb3639c6bf4dbae2e531c68356bfe95990daa30df33b30d556/scipy-1.18.0-cp313-cp313-win_arm64.whl", hash = "sha256:c5dbddf60e58c2312316d097271a8e73d40eaf2eabfa4d95ed7d3695bbf2ce7b", size = 24350911, upload-time = "2026-06-19T15:00:40.062Z" }, + { url = "https://files.pythonhosted.org/packages/78/b5/915a19b3de2f7430062b509653563db1633ddbb6f021b06731521115d4e2/scipy-1.18.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:4c256ee70c0d1a8a2ace807e199ccd4e3f57037433842abb3fb36bc17eaa9578", size = 31036253, upload-time = "2026-06-19T15:00:43.216Z" }, + { url = "https://files.pythonhosted.org/packages/d7/88/b72def7262e150d16be13fca37a96481138d624e700340bc3362a7588929/scipy-1.18.0-cp314-cp314-macosx_12_0_arm64.whl", hash = "sha256:2ef3abc54a4ffc53765374b0d5728532dfdd2585ed23f6b11c206a1f0b1b9af8", size = 28673758, upload-time = "2026-06-19T15:00:46.663Z" }, + { url = "https://files.pythonhosted.org/packages/91/02/2e636a61a525632c373cf6a9c24442a3ffb79e364d38e98b32042964ac32/scipy-1.18.0-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:f2a6af57bd9e4a75d70e4117e78a1bbee84f79ae3fbb6d0111005d6ebcc4cb8d", size = 20415514, upload-time = "2026-06-19T15:00:49.399Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b6/2135974442f6aba159d9d39d774a1c8cb19947016725d69fecc685df45bf/scipy-1.18.0-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:3f1ac564d3bf6c03d861d2cd87a1bea0da2887136f7fb1bf519c05a8971452d6", size = 23034398, upload-time = "2026-06-19T15:00:51.941Z" }, + { url = "https://files.pythonhosted.org/packages/f6/e6/ba89ec5abf6ee9257c0d1ec985573f3ae32742c24bc03e016388a40b1b15/scipy-1.18.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:40395a5fcd1abee49a5c7aaa98c29db393eedc835138560a588c47ec16156690", size = 33998032, upload-time = "2026-06-19T15:00:54.838Z" }, + { url = "https://files.pythonhosted.org/packages/7f/c4/bc41eb19b0fd0db868f4132920879019318d80cc522ad8f2bca4611af808/scipy-1.18.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ca01e8ae69f1b18e9a58d91afead31be3cef0dd905a10249dac559ee15460a0", size = 35283333, upload-time = "2026-06-19T15:00:58.152Z" }, + { url = "https://files.pythonhosted.org/packages/53/a4/cbdeef6eb3830a8462a9d4ada814de5fc984345cc9ecf17cbec51a036f1e/scipy-1.18.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7a7f3b01647384dbc3a711e8c6778e0aabbe93959249fef5c7393396bcac0867", size = 35610216, upload-time = "2026-06-19T15:01:01.155Z" }, + { url = "https://files.pythonhosted.org/packages/80/4d/b2b82502b65f661d1b789c1665dcdf315d5f12194e06fc0b37946294ebae/scipy-1.18.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6aa94e78ec192a30063a5e72e561c28af769dc311190b24fe91774eff1969709", size = 37418960, upload-time = "2026-06-19T15:01:04.155Z" }, + { url = "https://files.pythonhosted.org/packages/93/3e/902d836831474b0ab5a37d16404f7bc5fafd9efba632890e271ba952635f/scipy-1.18.0-cp314-cp314-win_amd64.whl", hash = "sha256:2d8bbdc6c817f5b4006a54d799d4f5bab6f910193cbb9a1ff310833d4d270f61", size = 37288845, upload-time = "2026-06-19T15:01:07.822Z" }, + { url = "https://files.pythonhosted.org/packages/b6/43/8d73b337a3bdb14daa0314f0434210747c02d79d729ce1777574a817dcf6/scipy-1.18.0-cp314-cp314-win_arm64.whl", hash = "sha256:18e9575f1569b2c54174e6159d32942e03731177f63dce7975f0a0c88d102f5b", size = 24988971, upload-time = "2026-06-19T15:01:11.076Z" }, + { url = "https://files.pythonhosted.org/packages/b4/b4/f11918b0508a2787031a0499a03fbe3546f3bb5ca05d01038c45b278c09a/scipy-1.18.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:f351e0dd702687d12a402b867a1b4146a256923e1c38317cbc472f6372b94707", size = 31399325, upload-time = "2026-06-19T15:01:13.723Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d1/1f287b57c0ff0ee5185dff3946d92c8017d39b0e431f0ae79a3ff1859512/scipy-1.18.0-cp314-cp314t-macosx_12_0_arm64.whl", hash = "sha256:7c7a51b33ce387193c97f228320cf8e87361daa1bba750638677729598b3e677", size = 29092110, upload-time = "2026-06-19T15:01:16.908Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1a/7b74eb6c392fdcb27d414c0e7558a6d0231eb3b6d73571f479bb81ea8794/scipy-1.18.0-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:84031d7b052a54fae2f8632e0ec802073d385476eb9a63079bce6e23ef9283d4", size = 20833811, upload-time = "2026-06-19T15:01:20.488Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ad/f3941716320a7b9cb4d68734a903b45fe16eff5fb7da7e16f2e619304979/scipy-1.18.0-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:56abf29a7c067dde59be8b9a22d606a4ea1b2f2a4b756d9d903c62818f5dacce", size = 23396644, upload-time = "2026-06-19T15:01:23.364Z" }, + { url = "https://files.pythonhosted.org/packages/22/22/1446b62ffe07f9719b7d9b1b6a4e05a772833ae8f441fe4c22c34c9b250f/scipy-1.18.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1ad44305cfa24b1ba5803cbbebf033590ccbac1aa5d612d727b785325ab408b0", size = 34079318, upload-time = "2026-06-19T15:01:26.002Z" }, + { url = "https://files.pythonhosted.org/packages/56/3b/b87da667098bb470fa30c7011b0ba351ee976dd395c78798c66e941665a3/scipy-1.18.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:945c1761b93f38d7f99ae81ae80c63e621471608c7eeead563f6df025585cd58", size = 35324320, upload-time = "2026-06-19T15:01:28.881Z" }, + { url = "https://files.pythonhosted.org/packages/f8/a1/c7932f91909759b0267f75fdea34e91309f96b895757534b76a90b6b4344/scipy-1.18.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1a4441f15d620578772a49e5ab48c0ee1f7a0220e387110283062729136b2553", size = 35699541, upload-time = "2026-06-19T15:01:31.968Z" }, + { url = "https://files.pythonhosted.org/packages/f7/86/5185061a1fcc41d18c5dc2463969b3a3964b31d9ac67b2fb05d4c7ff7670/scipy-1.18.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9aac6192fac56bf2ca534389d24623f07b39ff83317d58287285e7fbd622ff76", size = 37472480, upload-time = "2026-06-19T15:01:35.136Z" }, + { url = "https://files.pythonhosted.org/packages/31/8e/f04c68e39919a010d34f2ee1367fd705b0a25a02f609d755f0bfbc0a15fc/scipy-1.18.0-cp314-cp314t-win_amd64.whl", hash = "sha256:e40baea28ae7f5475c779741e2d90b1247c78531207b49c7030e698ff81cee3f", size = 37365390, upload-time = "2026-06-19T15:01:38.091Z" }, + { url = "https://files.pythonhosted.org/packages/d5/19/969dc072906c84dd0a3b05dcf57ea750936087d7873549e408b35cfc3f97/scipy-1.18.0-cp314-cp314t-win_arm64.whl", hash = "sha256:368e0a705903c466aa5f08eefb39e6b1b6b2d659e7352a31fd9e2438365be0f8", size = 25279661, upload-time = "2026-06-19T15:01:40.817Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "threadpoolctl" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" }, +] + +[[package]] +name = "tzdata" +version = "2026.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/ff/5a28bdfd8c3ebec42564ac7d0e54ca3db65044a9314a97f9564fa7a1e926/tzdata-2026.3.tar.gz", hash = "sha256:4a1518b8993086a7982523e071643f3c0e5f213e75b21318e78bcabfff9d1415", size = 198674, upload-time = "2026-07-10T08:50:37.887Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/6d/b53b99a9f2766d095985947a5782f1702cabb129a34f7a802d7197af832f/tzdata-2026.3-py2.py3-none-any.whl", hash = "sha256:dc096730c87af6cab1b171c9d532be840741ff5d459015e7f6947bd7d7e54931", size = 348168, upload-time = "2026-07-10T08:50:36.46Z" }, +] + +[[package]] +name = "urllib3" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" }, +] diff --git a/zensical.toml b/zensical.toml new file mode 100644 index 00000000..1c40d0d0 --- /dev/null +++ b/zensical.toml @@ -0,0 +1,126 @@ +[project] +site_name = "Khiops Python" +site_url = "https://khiopsml.github.io/khiops-python/" +repo_url = "https://github.com/KhiopsML/khiops-python" +repo_name = "KhiopsML/khiops-python" +docs_dir = "doc/site" +site_dir = "doc/build/html" +extra_css = ["_static/css/custom.css"] + +nav = [ + { "Home" = "index.md" }, + { "API Reference" = [ + { "sklearn" = [ + "sklearn/index.md", + { "estimators" = "sklearn/estimators.md" }, + { "helpers" = "sklearn/helpers.md" }, + ] }, + { "core" = [ + "core/index.md", + { "api" = "core/api.md" }, + { "dictionary" = "core/dictionary.md" }, + { "analysis_results" = "core/analysis_results.md" }, + { "coclustering_results" = "core/coclustering_results.md" }, + { "exceptions" = "core/exceptions.md" }, + { "helpers" = "core/helpers.md" }, + ] }, + { "tools" = [ + "tools/index.md" + ] }, + { "internals" = [ + "internal/index.md", + { "dataset" = "internal/dataset.md" }, + { "common" = "internal/common.md" }, + { "filesystems" = "internal/filesystems.md" }, + { "io" = "internal/io.md" }, + { "runner" = "internal/runner.md" }, + { "scenario" = "internal/scenario.md" }, + { "task" = "internal/task.md" }, + { "types" = "internal/types.md" }, + { "version" = "internal/version.md" }, + ] }, + ] }, + { "Tutorials and Code Samples" = [ + { "Tutorials" = "tutorials/index.md" }, + { "Samples sklearn" = "samples/samples_sklearn.md" }, + { "Samples core" = "samples/samples.md" }, + ] }, + { "Other Topics" = [ + { "Multi-Table Learning Primer" = "multi_table_primer.md" }, + { "Notes" = "notes.md" }, + ] }, +] + +[project.theme] +name = "material" +logo = "_static/images/orange_small_logo.png" +favicon = "_static/images/orange_small_logo.png" +features = [ + "navigation.sections", + "navigation.expand", + "navigation.indexes", + "navigation.top", + "search.highlight", + "search.suggest", + "content.code.copy", +] + +[project.theme.icon] +repo = "fontawesome/brands/github" + +[[project.theme.palette]] +scheme = "default" +primary = "custom" +toggle = { icon = "material/brightness-7", name = "Switch to dark mode" } + +[[project.theme.palette]] +scheme = "slate" +primary = "custom" +toggle = { icon = "material/brightness-4", name = "Switch to light mode" } + +[project.markdown_extensions] +admonition = {} +"pymdownx.details" = {} +"pymdownx.superfences" = {} +"pymdownx.highlight" = { anchor_linenums = true } +"pymdownx.inlinehilite" = {} +"pymdownx.snippets" = {} +"pymdownx.tabbed" = { alternate_style = true } +toc = { permalink = true } +attr_list = {} +md_in_html = {} +def_list = {} + +[project.plugins.search] +[project.plugins.autorefs] + +[project.plugins.mkdocstrings] +custom_templates = "doc/site/_templates" + +[project.plugins.mkdocstrings.handlers.python] +inventories = [ + "https://docs.python.org/3/objects.inv", + "https://pandas.pydata.org/pandas-docs/dev/objects.inv", + { url = "https://scikit-learn.org/stable/objects.inv", domains = ["py", "std"] }, + "https://numpy.org/doc/stable/objects.inv", + "https://docs.scipy.org/doc/scipy/objects.inv", +] + +[project.plugins.mkdocstrings.handlers.python.options] +docstring_style = "numpy" +docstring_section_style = "spacy" +scoped_crossrefs = true +show_root_heading = true +show_root_full_path = true +show_symbol_type_heading = false +show_symbol_type_toc = false +show_source = false +group_by_category = true +show_category_heading = true +summary = true +members_order = "source" +merge_init_into_class = true +show_signature_annotations = true +signature_crossrefs = true +inherited_members = false +show_if_no_docstring = false