Metadata-Version: 2.4
Name: biocimpute
Version: 0.1.0
Summary: Imputation for microarray data: a Python port of Bioconductor's impute (KNN)
Keywords: imputation,knn,microarray,gene expression,bioconductor
Author: Mingsen Wang
Author-email: Mingsen Wang <mingsen.wang.sh@gmail.com>
License-Expression: GPL-2.0-only
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
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 :: Bio-Informatics
Requires-Dist: numba>=0.68.0
Requires-Dist: numpy>=2.2.6
Requires-Dist: pandas>=2.3.3 ; extra == 'pandas'
Requires-Python: >=3.10
Project-URL: Homepage, https://github.com/Icosahedral-Dice/biocimpute
Project-URL: Repository, https://github.com/Icosahedral-Dice/biocimpute
Project-URL: Issues, https://github.com/Icosahedral-Dice/biocimpute/issues
Project-URL: Original R package, https://bioconductor.org/packages/release/bioc/html/impute.html
Provides-Extra: pandas
Description-Content-Type: text/markdown

# biocimpute

A Python port of the Bioconductor package
[impute](https://bioconductor.org/packages/release/bioc/html/impute.html)
(version 1.86.0): k-nearest-neighbor imputation of missing values in gene
expression matrices, with recursive two-means clustering for large matrices.

Results match R's `impute.knn` exactly, including the random two-means split
(R's Mersenne-Twister `set.seed()`/`sample()` are reimplemented). The test
suite checks every imputed value against fixtures generated by the R package.

## Installation

```sh
uv add biocimpute            # or: pip install biocimpute
uv add "biocimpute[pandas]"  # DataFrame support and the khanmiss dataset
```

## Usage

```python
import numpy as np
from biocimpute import impute_knn, load_khanmiss

# Same as R: khan.expr <- khanmiss[-1, -(1:2)]; impute.knn(as.matrix(khan.expr))
khan_expr = load_khanmiss().iloc[1:, 2:].to_numpy(dtype=float)
result = impute_knn(khan_expr)
result.data      # imputed matrix
result.rng_seed  # 362436069
```

`impute_knn(data, k=10, rowmax=0.5, colmax=0.8, maxp=1500, rng_seed=362436069)`
takes the same arguments as `impute.knn` (genes in rows, samples in columns,
missing values as NaN). A pandas DataFrame input gives a DataFrame output
with the same index and columns, as R keeps dimnames.

| R | biocimpute |
|---|---|
| `impute.knn(...)` | `impute_knn(...)` |
| `rng.seed` | `rng_seed` |
| `$data`, `$rng.seed` | `.data`, `.rng_seed` |
| `$rng.state` | not returned: biocimpute never touches a global RNG |
| `stop(...)` on `colmax` | `ValueError` |
| `warning(...)` on `rowmax` | `UserWarning` |
| `cat("Cluster size ...")` | `logging.getLogger("biocimpute").info(...)` |
| `data(khanmiss)` | `load_khanmiss()` |

## Behavior inherited from R

For parity, these behaviors of `impute.knn` are kept:

- Two-means clustering (when there are more than `maxp` rows) runs on the
  matrix with missing values replaced by 0.
- In a cluster with at most `k` rows, missing values are left as 0.
- If exactly one row has more than `rowmax` missing, its missing values are
  left as 0. With two or more such rows, column means are used.
- A column with no observed values among the rows used for KNN stays NaN.

The one deliberate difference: when a block has `k` or fewer rows, R's
Fortran reads past the end of its neighbor array (undefined behavior).
biocimpute uses all other rows of the block as neighbors.

## Layout

The code follows the R package's structure:

- `src/biocimpute/_impute.py`: `R/allknnimp.R`, one function per R function
- `src/biocimpute/_kernels/`: `src/knnimpute.f`, ported to Numba. The
  `_kernels` package is the only place a backend is imported, so another
  backend (e.g. Rust) can be swapped in there.
- `src/biocimpute/_rng.py`: R's default RNG and `sample()`
- `reference/`: the original R and Fortran sources

## Development

```sh
uv sync
uv run pytest
```

To regenerate the R fixtures in `tests/fixtures` (needs R and gfortran):

```sh
tests/r/install_impute.sh          # installs impute into tests/r/lib
Rscript tests/r/generate_fixtures.R
```

## License

GPL-2.0, like the original impute package (Trevor Hastie, Robert Tibshirani,
Balasubramanian Narasimhan, Gilbert Chu).
