Metadata-Version: 2.4
Name: python-som
Version: 0.4.0
Summary: Python implementation of the Self-Organizing Map
Project-URL: Homepage, https://github.com/andremsouza/python-som
Project-URL: Documentation, https://andremsouza.github.io/python-som/
Project-URL: Changelog, https://github.com/andremsouza/python-som/blob/master/CHANGELOG.md
Project-URL: Issues, https://github.com/andremsouza/python-som/issues
Author-email: André Moreira Souza <msouza.andre@hotmail.com>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
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: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: numpy>=1.24
Provides-Extra: analysis
Requires-Dist: bandit==1.9.4; extra == 'analysis'
Requires-Dist: pip-audit==2.10.1; extra == 'analysis'
Requires-Dist: pylint==4.0.6; extra == 'analysis'
Provides-Extra: cli
Requires-Dist: tqdm>=4.66; extra == 'cli'
Provides-Extra: dev
Requires-Dist: hypothesis==6.163.0; extra == 'dev'
Requires-Dist: mypy==2.3.0; extra == 'dev'
Requires-Dist: pandas-stubs==3.0.3.260530; (python_version >= '3.11') and extra == 'dev'
Requires-Dist: pandas==2.3.3; (python_version < '3.11') and extra == 'dev'
Requires-Dist: pandas==3.0.5; (python_version >= '3.11') and extra == 'dev'
Requires-Dist: pre-commit==4.6.1; extra == 'dev'
Requires-Dist: pytest-cov==7.1.0; extra == 'dev'
Requires-Dist: pytest==9.1.1; extra == 'dev'
Requires-Dist: ruff==0.16.0; extra == 'dev'
Requires-Dist: scikit-learn==1.7.2; (python_version < '3.11') and extra == 'dev'
Requires-Dist: scikit-learn==1.9.0; (python_version >= '3.11') and extra == 'dev'
Requires-Dist: tomli==2.4.1; (python_version < '3.11') and extra == 'dev'
Requires-Dist: twine==7.0.0; extra == 'dev'
Requires-Dist: types-tqdm==4.69.0.20260728; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material==9.7.7; extra == 'docs'
Requires-Dist: mkdocs-redirects==1.2.2; extra == 'docs'
Requires-Dist: mkdocstrings-python==2.0.5; extra == 'docs'
Provides-Extra: examples
Requires-Dist: matplotlib>=3.8; extra == 'examples'
Requires-Dist: pandas>=2.0; extra == 'examples'
Requires-Dist: scikit-learn>=1.3; extra == 'examples'
Requires-Dist: seaborn>=0.13; extra == 'examples'
Description-Content-Type: text/markdown

# python-som

[![CI](https://github.com/andremsouza/python-som/actions/workflows/ci.yml/badge.svg)](https://github.com/andremsouza/python-som/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/python-som.svg)](https://pypi.org/project/python-som/)
[![Python versions](https://img.shields.io/pypi/pyversions/python-som.svg)](https://pypi.org/project/python-som/)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/andremsouza/python-som/blob/master/LICENSE)

Implementation of Kohonen's 2-D self-organizing map. NumPy is the only dependency. Accepts NumPy
arrays, pandas DataFrames, polars, pyarrow, and anything else implementing the `__array__` protocol.

[Documentation](https://andremsouza.github.io/python-som/) ·
[Changelog](https://github.com/andremsouza/python-som/blob/master/CHANGELOG.md)

## Install

```bash
pip install python-som              # requires Python 3.10+
pip install "python-som[cli]"       # adds tqdm progress bars
```

## Quick start

```python
import numpy as np
import python_som

rng = np.random.default_rng(0)
data = rng.normal(size=(150, 4))

som = python_som.SOM(x=20, y=None, input_len=4, data=data, random_seed=42)
som.weight_initialization(mode="linear", data=data)
error = som.train(data, n_iteration=len(data), mode="batch")

umatrix = som.distance_matrix()
winner = som.winner(data[0])
```

A full worked example with plots is in [examples/iris.py](https://github.com/andremsouza/python-som/blob/master/examples/iris.py) and in the
[getting-started guide](https://andremsouza.github.io/python-som/tutorial/first-map/).

![U-matrix of a SOM trained on Iris](https://raw.githubusercontent.com/andremsouza/python-som/master/docs/assets/iris.png)

## Features

* Stepwise and batch training
* Random, random-sampling and linear (PCA) weight initialization
* Automatic selection of the map size ratio, from PCA
* Cyclic arrays, for toroidal maps
* Gaussian, bubble and Mexican hat neighborhood functions
* Custom decay functions
* Visualization support: U-matrix, activation matrix
* Supervised labelling, via the label map
* Fully type-annotated, with a `py.typed` marker

## Neighborhood functions

All three are functions of the distance between two nodes in the grid, `sqdist(c, i)` in Eq. (5) of
Kohonen (2013).

| Name | Shape | Notes |
| --- | --- | --- |
| `'gaussian'` | `exp(-r² / 2σ²)` | Strictly positive, monotonically decreasing. The default. |
| `'bubble'` | `1` for `max(dx, dy) ≤ σ`, else `0` | The truncated inner lobe of the Mexican hat. Uses the Chebyshev metric, so the region is a square. |
| `'mexicanhat'` | `(1 - u)·exp(-u)`, `u = r² / 2σ²` | Excitatory near the winner, inhibitory beyond it. Zero at `r = √2·σ`, minimum `-e⁻²` at `r = 2σ`. |

The Mexican hat takes negative values, so it **cannot be used with `mode='batch'`**: the batch
update of Kohonen Eq. (8) is a weighted mean whose denominator is not sign-definite for a signed
neighborhood function. Use `mode='random'` or `mode='sequential'`; `mode='batch'` raises a
`ValueError`.

See [Neighborhood functions](https://andremsouza.github.io/python-som/reference/neighborhood-functions/) for
the derivations, including why the Mexican hat is not an outer product of two 1-D wavelets.

## Upgrading

0.3.0 corrects several methodology defects, so numerical results are not comparable with earlier
versions. In particular **`random_seed` no longer reproduces pre-0.3.0 maps**: the generator is now
per-instance rather than a call to `np.random.seed` on NumPy's global state. To reproduce figures
made with an older version, pin `python-som==0.2.0`.

Each change and the passage of Kohonen (2013) behind it is in the
[changelog](https://github.com/andremsouza/python-som/blob/master/CHANGELOG.md).

## Development

```bash
uv sync --all-extras
uv run pytest --cov          # tests and coverage
uv run ruff check .          # lint
uv run ruff format --check . # formatting
uv run mypy                  # type-check
uv run mkdocs serve          # docs, locally
pre-commit install           # optional, run the gates on commit
```

If you use the SonarQube for IDE (SonarLint) VS Code extension, it will also apply Sonar's Python
rules locally; the ruff configuration is set up to cover most of the same ground.

## References

Based on:

Teuvo Kohonen,
Essentials of the self-organizing map,
Neural Networks,
Volume 37,
2013,
Pages 52-65,
ISSN 0893-6080,
<https://doi.org/10.1016/j.neunet.2012.09.018>

The Mexican hat neighborhood follows the lateral-interaction formulation in:

O. J. Vrieze,
Kohonen network,
in: Artificial Neural Networks: An Introduction to ANN Theory and Practice,
Lecture Notes in Computer Science, Volume 931,
Springer, Berlin, Heidelberg,
1995,
Pages 83-100,
<https://doi.org/10.1007/BFb0027024>

## License

MIT. See [LICENSE](https://github.com/andremsouza/python-som/blob/master/LICENSE).
