# oximiR_utils

Python utility package for [OximiR](https://github.com/GuerreroVazquez/OximiR) and [OximiR_studio](https://github.com/GuerreroVazquez/OximiR_studio).  
Contains the shared Python scripts used for the analysis of miRNA oxidation (OximiR) data.

---

## Installation

```bash
pip install oximir-utils
```

Or, directly from the repository:

```bash
pip install git+https://github.com/GuerreroVazquez/oximiR_utils.git
```

For development (editable) mode:

```bash
git clone https://github.com/GuerreroVazquez/oximiR_utils.git
cd oximiR_utils
pip install -e ".[dev]"
```

### Optional heavy dependencies (DESeq2 pipeline)

```bash
pip install "oximir-utils[deseq]"
```

---

## Repository structure

```
oximiR_utils/
├── src/                          # Package source modules
│   ├── __init__.py               # Stable public API (re-exports all symbols)
│   ├── analysis_functions.py     # Burden/richness, enrichment, G>T analysis
│   ├── qc_functions.py           # PCA, correlation, outlier detection
│   ├── pipeline_utils.py         # Data loading, elbow threshold, DESeq2 wrapper
│   └── plotting.py               # All visualisation helpers
├── tests/                        # pytest test suite
│   ├── test_burden_richness.py
│   ├── test_enrichment.py
│   ├── test_gt_mutations.py
│   ├── test_mirna_counts.py
│   ├── test_plots.py
│   └── test_qc.py
├── .github/
│   └── workflows/
│       └── publish.yml           # Publish to PyPI on version tags
├── conftest.py                   # pytest root configuration
├── pyproject.toml                # Python package configuration
├── package.json                  # npm / TypeScript tooling metadata
├── tsconfig.json                 # TypeScript build configuration
├── .npmrc                        # npm local-dev settings
├── LICENSE                       # MIT License
└── README.md
```

---

## Quick-start usage

```python
import pandas as pd
from oximiR_utils import (
    filter_mirnas,
    calculate_burden_richness,
    calc_enrichment_matrix,
    get_metadata_mapping,
    run_distribution_tests,
    filter_enrichment_noise,
)

# 1. Load your data (CPM matrix + metadata)
cpm_df     = pd.read_csv("counts_cpm.csv", index_col=0)
samples_df = pd.read_csv("metadata.csv")

# 2. Quality control: keep miRNAs with ≥ 10 CPM in at least 3 samples
filtered = filter_mirnas(cpm_df, threshold=10, min_samples=3)

# 3. Global oxidative burden & richness
analysis_df, stats = calculate_burden_richness(
    filtered, group="Treatment", samples_df=samples_df
)

# 4. Paired enrichment (IP vs NAT)
pairs = get_metadata_mapping(samples_df)
enrichment = calc_enrichment_matrix(filtered, pairs)
clean      = filter_enrichment_noise(enrichment, filtered, pairs, threshold=5.0)

group_1 = [k for k, v in pairs.items() if v.get("Treatment") == "Veh"]
group_2 = [k for k, v in pairs.items() if v.get("Treatment") == "KO"]
report  = run_distribution_tests(clean, group_1, group_2)
print(report)
```

---

## Public API

### `oximiR_utils.analysis_functions`

| Function | Description |
|---|---|
| `calculate_burden_richness` | Cumulative CPM sum (Burden) and unique-species count (Richness) per sample |
| `get_metadata_mapping` | Pairs IP / NAT samples per individual from a metadata DataFrame |
| `calc_enrichment_matrix` | log₂((IP + ε) / (NAT + ε)) enrichment score per miRNA per mouse |
| `get_enrichment_score` | Core log-ratio formula |
| `filter_enrichment_noise` | Removes low-signal miRNAs to prevent zero-inflation |
| `run_distribution_tests` | T-test (per-mouse) + KS-test (transcriptome-wide) |
| `calculate_gt_rates` | G>T transversion count / total reads per miRNA |
| `extract_gt_positions` | Finds first G>T position in the `pos:mut` column |
| `run_diff_mutation_test` | miRNA-level Welch's t-test on G>T rates |
| `run_mutation_rate_burden` | Global G>T burden comparison between two groups |
| `find_differentially_oxidized_mirs` | Full differential oxidation analysis with filtering |
| `extract_first_position` | Parses `pos:mut` to extract first G>T position |

### `oximiR_utils.qc_functions`

| Function | Description |
|---|---|
| `filter_mirnas` | CPM threshold + minimum-sample filter |
| `run_pca_and_plot_samples` | PCA scatter plot coloured by group |
| `get_correlation_matrix` | Pearson correlation heatmap |
| `run_outlier_detection` | Euclidean distance + Z-score outlier flags |

### `oximiR_utils.pipeline_utils`

| Function | Description |
|---|---|
| `prepare_core_data` | Loads and standardises CPM, raw-counts, and metadata CSVs |
| `get_elbow_threshold` | Knee/elbow CPM threshold via `kneed` |
| `calculate_group_consistency` | Average inter-replicate Pearson correlation |
| `get_exclusion_lists` | miRNAs unique to each group |

### `oximiR_utils.plotting`

| Function | Description |
|---|---|
| `plot_barplot_difference_mirs` | Bar chart of average expressed miRNAs per group |
| `plot_boxplot_difference_mirs` | Boxplot distribution of expressed miRNAs per group |
| `plot_burden_and_richness` | Two-panel Burden / Richness boxplots |
| `plot_global_enrichment` | Boxplot of mean per-mouse enrichment + t-test |
| `plot_kde` | KDE density plot of enrichment distributions |
| `ECDF_plot` | ECDF comparison with KS-test annotation |
| `plot_positional_vulnerability` | Line graph of average G>T rate across positions 1–25 |
| `plot_differential_heatmap` | Heatmap of top differentially oxidised miRNAs |
| `plot_pca_separation` | PCA scatter highlighting group separation |
| `plot_enrichment_distribution` | Histogram/KDE of enrichment distributions |
| `plot_mir` | Boxplot of mutation rate for a single miRNA |

---

## Running the tests

```bash
# Install dev dependencies first
pip install -e ".[dev]"

# Run the full test suite
pytest
```

---

## Releasing a new version

1. Bump the version in both `pyproject.toml` (`version = "..."`) and `src/__init__.py` (`__version__ = "..."`).
2. Commit and push the changes.
3. Create and push a version tag:
   ```bash
   git tag v0.2.0
   git push --tags
   ```
4. The [Publish to PyPI](.github/workflows/publish.yml) GitHub Actions workflow will automatically build and upload the package to PyPI.

> **Note:** configure PyPI Trusted Publishing **or** set the `PYPI_API_TOKEN` repository secret before the first release.

---

## TypeScript / npm tooling

The repository includes lightweight TypeScript build scaffolding for use by companion JavaScript/TypeScript consumers (e.g. OximiR_studio).

```bash
# Install TypeScript compiler locally
npm install

# Type-check (no output files)
npm run typecheck

# Compile TypeScript → dist/
npm run build

# Compile in watch mode
npm run build:watch
```

---

## Contributing

1. Fork the repository.
2. Create a feature branch (`git checkout -b feature/my-feature`).
3. Commit your changes (`git commit -m "Add my feature"`).
4. Push to the branch (`git push origin feature/my-feature`).
5. Open a Pull Request.

---

## License

Distributed under the [MIT License](LICENSE).
