Metadata-Version: 2.3
Name: generate-test-data-for-wknn
Version: 0.0.9
Summary: Test-data generator for validating the weighted k-NN (wkNN) method.
Keywords: knn,weighted-knn,wknn,machine-learning,test-data
Author: Ferubko Andrey, Kazakov Oleg
Author-email: Ferubko Andrey <andrey.ferubko@petsplace.ru>
License: MIT
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Dist: numpy>=1.24
Maintainer: Ferubko Andrey, Kazakov Oleg
Maintainer-email: Ferubko Andrey <andrey.ferubko@petsplace.ru>
Requires-Python: >=3.10
Project-URL: Homepage, https://github.com/89605502155/generate-test-data-for-wknn
Project-URL: Repository, https://github.com/89605502155/generate-test-data-for-wknn
Project-URL: Issues, https://github.com/89605502155/generate-test-data-for-wknn/issues
Description-Content-Type: text/markdown

# generate-test-data-for-wknn

[![PyPI](https://img.shields.io/pypi/v/generate-test-data-for-wknn.svg)](https://pypi.org/project/generate-test-data-for-wknn/)
[![Python](https://img.shields.io/pypi/pyversions/generate-test-data-for-wknn.svg)](https://pypi.org/project/generate-test-data-for-wknn/)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

**EN** — Synthetic test-data generator for validating the **weighted k-NN (wkNN)** method.
**RU** — Генератор синтетических тестовых данных для проверки метода **взвешенного k-NN (wkNN)**.

*Authors / Авторы:* Ferubko Andrey, Kazakov Oleg
*Organization / Организация:* Bryansk State Technological University of Engineering /
Брянский государственный инженерно-технологический университет.

---

## Installation / Установка

```bash
pip install generate-test-data-for-wknn
# or with uv:
uv add generate-test-data-for-wknn
```

---

## English documentation

The library exposes a single public function, `generate` (also available as
`WknnDataGenerator.generate`), which builds a labelled dataset `(x, y)` for
testing weighted k-NN.

**Model.** Each class is given one random **anchor** point inside the cube
`[min_coord, max_abs_coord]^dim`. The anchor is the **maximum** of that class's
weight field (and is itself excluded from the output). For a point `p` and class
`c` with anchor `A_c`, the weight uses the **per-coordinate** distances
`x_i = |p_i − A_{c,i}|`. A point is labelled by the class with the largest
weight, and is **kept only where that class leads the runner-up by at least
`margin`** (default 10%) — so samples come from regions of strong class
superiority rather than the decision boundary. This keeps each point's nearest
neighbour in the same class with high probability.

Points are drawn and classified iteratively until every class reaches its
balanced target (stratified top-up). Small samples work: a binary request for
2 objects yields exactly 1 point per class.

### Weight functions and parameter layout

Weights use **per-coordinate** distances, so `params` has `2·dim + 1` shared
values `[a₁, b₁, …, a_d, b_d, f]` — the **same** coefficients for every class.
Both types require `aᵢ ≥ 0` and `bᵢ > 0` (weight decreases with distance).

| `weight_type`  | Formula                        | `params` length | Constraint |
|----------------|--------------------------------|-----------------|------------|
| `"inverse"`    | `w = Σᵢ aᵢ / xᵢ^bᵢ + f`        | `2·dim + 1`     | `aᵢ ≥ 0`, `bᵢ > 0` |
| `"exponential"`| `w = Σᵢ aᵢ·exp(−bᵢ·xᵢ) + f`    | `2·dim + 1`     | `aᵢ ≥ 0`, `bᵢ > 0` |

> Example: `dim=2`, `inverse`, `params=[2, 1, 3, 4, 1]` →
> `w = 2/x₁¹ + 3/x₂⁴ + 1`.

> **Note on `exponential`:** distances are normalised by the cube width, so
> choose `bᵢ` on the order of `1` (not `0.01`). Very small `bᵢ` make
> `exp(−bᵢ·xᵢ)` nearly constant across the cube, so no class clears the margin
> and generation fails with a clear diagnostic.

### Parameters

| Name | Default | Meaning |
|------|---------|---------|
| `n_classes` | `2` | number of classes (binary by default) |
| `dim` | `2` | dimensionality of each point |
| `n_samples` | `30` | number of objects in the dataset (exact) |
| `weight_type` | `"inverse"` | `"inverse"` or `"exponential"` |
| `params` | `None` | shared coefficient array `[a₁,b₁,…,a_d,b_d,f]` (see table) |
| `max_abs_coord` | `1e4` | upper coordinate bound |
| `min_coord` | `-1e4` | lower coordinate bound (use `0` for e.g. time series) |
| `outlier_ratio` | `0.0` | fraction of the *most borderline* labels to flip, in `[0, 1]` |
| `margin` | `0.10` | required relative superiority of the winning class (`w_top ≥ (1+margin)·w_second`) for a point to be kept |
| `random_state` | `None` | RNG seed |

**Returns:** `(x, y)` — `x` is `float64` `(m, dim)`, `y` is `int64` `(m,)`,
`m == n_samples`.

### Behavioural guarantees

- **Anchors are maxima:** each class's weight field peaks at its anchor.
- **Stratification:** classes are balanced (counts differ by at most 1).
- **Margin:** kept points beat the runner-up class by ≥ `margin` (default 10%),
  drawing samples from regions of strong class superiority.
- **Nearest neighbour (soft):** because samples avoid the borderlands, each
  point's nearest neighbour is in the same class with high probability.
- **Outliers:** the requested fraction of the *most borderline* points is
  relabelled to the runner-up class (sample size unchanged).
- **Monotonicity guard:** `aᵢ < 0` or `bᵢ ≤ 0` raises `ValueError`.
- **Flat-field guard:** if no candidate clears the margin (e.g. `exponential`
  with tiny `bᵢ` over a wide cube), a clear `ValueError` explains the cause.

### Example

```python
import numpy as np
from generate_test_data_for_wknn import generate

# Binary, inverse-distance weights, 2-D, reproducible
x, y = generate(
    n_classes=2,
    dim=2,
    n_samples=100,
    weight_type="inverse",
    params=np.array([2.0, 1.0, 3.0, 4.0, 1.0]),  # a1,b1,a2,b2,f (shared)
    max_abs_coord=100.0,
    min_coord=0.0,
    outlier_ratio=0.1,
    margin=0.10,
    random_state=42,
)
print(x.shape, y.shape, np.bincount(y))
```

---

## Документация на русском

Библиотека предоставляет одну публичную функцию `generate` (также доступна как
`WknnDataGenerator.generate`), которая строит размеченный набор `(x, y)` для
проверки взвешенного k-NN.

**Модель.** Для каждого класса создаётся одна случайная **опорная точка** внутри
куба `[min_coord, max_abs_coord]^dim`. Опорная точка — это **максимум** весового
поля класса (и в выборку она не входит). Для точки `p` и класса `c` с опорной
точкой `A_c` вес использует **покоординатные** расстояния `xᵢ = |pᵢ − A_{c,i}|`.
Точка размечается классом с наибольшим весом и **сохраняется только там, где
этот класс превосходит второй как минимум на `margin`** (по умолчанию 10%) — то
есть выборка берётся из областей уверенного превосходства класса, а не с границы.
Это обеспечивает, что ближайший сосед каждой точки с высокой вероятностью того же
класса.

Точки генерируются и классифицируются итеративно, пока каждый класс не наберёт
свою сбалансированную квоту (стратифицированное дозаполнение). Малые выборки
работают: бинарный запрос на 2 объекта даёт ровно по 1 точке на класс.

### Весовые функции и формат параметров

Веса используют **покоординатные** расстояния, поэтому `params` содержит
`2·dim + 1` общих значений `[a₁, b₁, …, a_d, b_d, f]` — **одинаковых** для всех
классов. Оба типа требуют `aᵢ ≥ 0` и `bᵢ > 0`.

| `weight_type`  | Формула                        | Длина `params` | Ограничение |
|----------------|--------------------------------|----------------|-------------|
| `"inverse"`    | `w = Σᵢ aᵢ / xᵢ^bᵢ + f`        | `2·dim + 1`    | `aᵢ ≥ 0`, `bᵢ > 0` |
| `"exponential"`| `w = Σᵢ aᵢ·exp(−bᵢ·xᵢ) + f`    | `2·dim + 1`    | `aᵢ ≥ 0`, `bᵢ > 0` |

> **Замечание про `exponential`:** расстояния нормируются по ширине куба, поэтому
> выбирайте `bᵢ` порядка `1` (а не `0.01`). Слишком малые `bᵢ` делают
> `exp(−bᵢ·xᵢ)` почти постоянным по кубу, ни один класс не преодолевает отступ, и
> генерация завершается понятной ошибкой.

### Параметры

| Имя | По умолчанию | Смысл |
|-----|--------------|-------|
| `n_classes` | `2` | число классов |
| `dim` | `2` | размерность точки |
| `n_samples` | `30` | число объектов (точно) |
| `weight_type` | `"inverse"` | `"inverse"` или `"exponential"` |
| `params` | `None` | общий массив коэффициентов `[a₁,b₁,…,a_d,b_d,f]` |
| `max_abs_coord` | `1e4` | верхняя граница координат |
| `min_coord` | `-1e4` | нижняя граница (для временных рядов задайте `0`) |
| `outlier_ratio` | `0.0` | доля наиболее пограничных меток для переворота, из `[0, 1]` |
| `margin` | `0.10` | требуемое относительное превосходство класса-победителя (`w_top ≥ (1+margin)·w_second`) |
| `random_state` | `None` | зерно генератора |

**Возвращает:** `(x, y)` — массивы NumPy; `m == n_samples`.

### Гарантии поведения

- **Опорные точки — максимумы:** весовое поле класса достигает пика в его опоре.
- **Стратификация:** классы сбалансированы (разница ≤ 1 объект).
- **Отступ (margin):** сохранённые точки превосходят второй класс на ≥ `margin`
  (по умолчанию 10%), выборка берётся из областей уверенного превосходства.
- **Ближайший сосед (мягкое условие):** так как выборка избегает границ,
  ближайший сосед каждой точки с высокой вероятностью того же класса.
- **Выбросы:** заданная доля наиболее пограничных точек переразмечается в
  класс-«второе место» (размер выборки не меняется).
- **Проверка монотонности:** `aᵢ < 0` или `bᵢ ≤ 0` → `ValueError`.
- **Защита от плоского поля:** если ни одна точка не преодолевает отступ (например,
  `exponential` с крошечными `bᵢ` на широком кубе), возбуждается понятный `ValueError`.

### Пример

```python
import numpy as np
from generate_test_data_for_wknn import generate

x, y = generate(
    n_classes=3,
    dim=2,
    n_samples=90,
    weight_type="exponential",
    params=np.array([5.0, 1.0, 4.0, 1.5, 0.0]),  # a1,b1,a2,b2,f (shared)
    outlier_ratio=0.15,
    margin=0.10,
    random_state=7,
)
print(x.shape, y.shape, np.bincount(y))
```

---

## License / Лицензия

MIT © Ferubko Andrey, Kazakov Oleg — Bryansk State Technological University of Engineering.
