Metadata-Version: 2.5
Name: deeplife
Version: 1.0.4
Summary: Official DeepLife Python package: TwinCell (`deeplife.twincell`), pseudo-bulk (`deeplife.pseudobulk`), differential expression (`deeplife.differential_expression`). Tutorial notebooks: https://twincell.deeplife.co/docs/tutorials/ (optional `[notebook]` extra for Jupyter).
Project-URL: Homepage, https://deeplife.co
Project-URL: Documentation, https://twincell.deeplife.co/docs/
Project-URL: Repository, https://github.com/deeplifeai/deeplife
Project-URL: Issues, https://github.com/deeplifeai/deeplife/issues
Project-URL: Changelog, https://github.com/deeplifeai/deeplife/releases
Author-email: DeepLife <hello@deeplife.co>
License: MIT
License-File: LICENSE
Keywords: anndata,api,bioinformatics,deeplife,differential-expression,digital-twin,drug-discovery,pseudobulk,scanpy,single-cell,twincell
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Healthcare Industry
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: anndata>=0.12.7
Requires-Dist: boto3>=1.35.0
Requires-Dist: decoupler>=2.1.1
Requires-Dist: dotenv>=0.9.9
Requires-Dist: gseapy>=1.1.13
Requires-Dist: h5py>=3.10.0
Requires-Dist: hdf5plugin>=5.1.0
Requires-Dist: httpx>=0.27.2
Requires-Dist: hydra-core>=1.3.0
Requires-Dist: ipykernel>=7.1.0
Requires-Dist: ipywidgets>=8.1.8
Requires-Dist: kaleido>=1.0.0
Requires-Dist: mkdocs>=1.6.1
Requires-Dist: networkx>=3.2.1
Requires-Dist: numpy>=1.26.4
Requires-Dist: omegaconf>=2.3.0
Requires-Dist: pandas>=2.2.3
Requires-Dist: plotly>=6.3.0
Requires-Dist: pyarrow>=21.0.0
Requires-Dist: pydantic>=2.8.2
Requires-Dist: pydeseq2>=0.5.2
Requires-Dist: pyyaml>=6.0
Requires-Dist: scanpy[leiden]>=1.11.4
Requires-Dist: tenacity>=9.0.0
Requires-Dist: tqdm>=4.67.1
Provides-Extra: notebook
Requires-Dist: adjusttext>=0.8.0; extra == 'notebook'
Requires-Dist: comm>=0.1.1; extra == 'notebook'
Requires-Dist: gseapy>=1.1.9; extra == 'notebook'
Requires-Dist: ipython>=8.26.0; extra == 'notebook'
Requires-Dist: six>=1.5; extra == 'notebook'
Description-Content-Type: text/markdown

# deeplife

[![PyPI version](https://img.shields.io/pypi/v/deeplife)](https://pypi.org/project/deeplife/)
[![CI](https://github.com/deeplifeai/deeplife/actions/workflows/ci.yml/badge.svg)](https://github.com/deeplifeai/deeplife/actions/workflows/ci.yml)
[![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

Official **[DeepLife](https://deeplife.co/)** Python **toolkit** for **TwinCell** and related analysis: call the Open API from Python, **pseudo-bulk** with **`deeplife.pseudobulk`**, and run **sample-level differential expression** with **`deeplife.differential_expression`** (suitable for pseudo-bulk, bulk, or other compatible count tables). TwinCell code is split into focused subpackages under **`deeplife.twincell`** (HTTP client, workflows, preprocess, validation); **`deeplife.twincell`** itself still exposes a flat import surface for notebooks.

**Source:** [github.com/deeplifeai/deeplife](https://github.com/deeplifeai/deeplife) · **Documentation:** [twincell.deeplife.co/docs](https://twincell.deeplife.co/docs/) · **Python:** 3.12+ (see [`pyproject.toml`](https://github.com/deeplifeai/deeplife/blob/main/pyproject.toml)).

---

## Install

```bash
pip install deeplife
```

**API key:** you need a DeepLife key (usually `dl_…`). Create or copy one from the **[TwinCell console](https://twincell.deeplife.co/)** (sign in, then open **API keys** / **Keys**). In code, set `DEEPLIFE_API_KEY` in the environment or pass `api_key=` when constructing the client; the client sends it as the **`X-API-Key`** header on every request (not `Authorization: Bearer`).

**Network:** the TwinCell **Open API** is **public**. The client defaults to **`https://open-deeplife-api.deeplife.co`**, reachable over the Internet. Interactive REST docs are at [`/docs`](https://open-deeplife-api.deeplife.co/docs) and the schema at [`/openapi.json`](https://open-deeplife-api.deeplife.co/openapi.json); pass `base_url=` to target another environment. Local-only features (pseudo-bulk, differential expression on `.h5ad`) do **not** require API connectivity.

**From a git clone** (contributors):

```bash
uv sync --group dev
```

---

## Documentation

Hosted docs (same content as the **`Documentation`** link on [PyPI](https://pypi.org/project/deeplife/)):

| Guide | Description |
|--------|----------------|
| [Quick Start](https://twincell.deeplife.co/docs/quickstart/) | `pip install`, optional **`[notebook]`**, API keys, first TwinCell workflow |
| [Tutorials](https://twincell.deeplife.co/docs/tutorials/) | End-to-end single-cell target-validation notebook (Deucravacitinib / psoriasis) |
| [API Reference](https://twincell.deeplife.co/docs/api/) | `TwinCell`, preprocessing, validation, pseudo-bulk, differential expression |

Source: [docs/index.md](https://github.com/deeplifeai/deeplife/blob/main/docs/index.md).

---

## Package layout

### TwinCell (`deeplife.twincell`)

| Import | Role |
|--------|------|
| **`deeplife.twincell`** | Convenience namespace: `DeepLifeClient`, `TwinCell`, `TwinCellSession`, `TwinCellStudy` (alias of `TwinCell`), `read_h5ad`, `adata_to_h5ad_bytes`, etc.; plus the preprocess helpers `pseudobulk`, `pydeseq2` (and lazy `preprocessing`). |
| **`deeplife.twincell.http`** | REST client (`DeepLifeClient`, `AsyncDeepLifeClient`), request/response models, errors, HTTP helpers, logging (`DeepLifeClient` lives in `twincell.http.client`). |
| **`deeplife.twincell.workflows`** | High-level session and study code: import submodules explicitly, e.g. `twincell.workflows.workflows` (`TwinCellSession`, …), `twincell.workflows.study` (`TwinCell`, …), `twincell.validation.upload_helpers` (`adata_to_h5ad_bytes` for client-side serialization), `twincell.workflows.h5ad_io` (`read_h5ad` for local paths and remote `.h5ad` URIs). The package `__init__` resolves names lazily to avoid import cycles. |
| **`deeplife.twincell.preprocess`** | Notebook-oriented pseudo-bulk and PyDESeq2 helpers (`preprocess.pseudobulk`, `preprocess.pydeseq2`). |
| **`deeplife.twincell.validation`** | Local checks for the **split inference** upload path (`validate_twincell_split_anndata`, shared with the HTTP client). Exceptions live under **`validation.core`**; shared result types under **`validation.checks`**. |

### Other packages

| Import | Role |
|--------|------|
| `deeplife.pseudobulk`, `deeplife.differential_expression` | Pseudo-bulk from single-cell `AnnData`, and sample-level DE (CLIs `twincell-pseudobulk`, `twincell-diffexpr`) |

Install with **`pip install deeplife`**. **Import** **`deeplife.twincell`** (flat or by submodule), **`deeplife.pseudobulk`**, and **`deeplife.differential_expression`**.

---

## Minimal API usage

End-to-end flow: build a **control** and a **perturbed** `AnnData` plus a **DEG** list yourself → submit them as a **split** target-validation run → poll → read the target score and causal mechanism.

The notebook-oriented **`TwinCell`** class is the shortest path. It validates the split inputs locally, checks API connectivity on construction, and submits with `job_type="target_validation"`:

```python
import os
from deeplife.twincell import TwinCell

tc = TwinCell(
    pdata_control=pdata_control,  # AnnData, raw counts in .X
    pdata_pert=pdata_pert,        # AnnData, raw counts in .X
    degs=degs,                    # list[str] of HGNC-style symbols
    api_key=os.environ["DEEPLIFE_API_KEY"],
)
prediction_id = tc.target_validation(target="TYK2|PROTEIN")
print(tc.get_target_score(prediction_id=prediction_id))
```

The same run through the HTTP client, when you want to manage polling yourself:

```python
import os
from deeplife.twincell.http import DeepLifeClient

client = DeepLifeClient(api_key=os.environ["DEEPLIFE_API_KEY"])
prediction = client.create_prediction_split(
    pdata_control=pdata_control,
    pdata_pert=pdata_pert,
    degs=degs,
    job_type="target_validation",
    target="TYK2|PROTEIN",
)
final = client.wait_for_prediction(prediction_id=prediction.prediction_id)
print(final.status)
```

> **External API keys are limited to split target validation.** A merged single-file upload via `create_prediction(dataset=…)` returns **403** (`prediction_external_split_required`), and leaving `job_type` at its default `"target_id"` returns **403** (`prediction_external_target_validation_only`). Both paths are internal-only; use `create_prediction_split(..., job_type="target_validation")` as above.

The Python arguments are **`pdata_control`** / **`pdata_pert`**; the underlying multipart REST fields on `POST /v1/predictions` are named `dataset_control` / `dataset_pert`. Don't pass the REST names to the SDK.

**Defaults:** the client targets `https://open-deeplife-api.deeplife.co`; pass `base_url=` for another environment. Retries apply to safe **GET**-style calls (polling), not duplicate uploads on `POST`. Polling waits **5s** between status requests (`poll_interval_seconds`) to stay well inside the edge rate limit of 30 requests/minute **per IP** — lower it only if you know the address isn't shared. For TLS/proxy issues, use `tls_verify=` and `trust_env=` on the client—see **`DeepLifeClient`** in **`deeplife.twincell.http.client`**.

For richer AnnData preparation (QC, column mapping, pseudo-bulk), use **`deeplife.twincell.preprocess`** or the [tutorial notebooks](#tutorials).

---

## Tutorials

Tutorial **`.ipynb`** files are published on the **[docs site](https://twincell.deeplife.co/docs/tutorials/)** (interactive + download). **`target-validation-deucravacitinib-psoriasis.ipynb`** is the end-to-end walkthrough: it loads a public psoriasis skin atlas ([GSE162183](https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi?acc=GSE162183)), builds pseudo-bulk profiles for psoriatic vs. normal dendritic cells, derives the disease signature with PyDESeq2, then uses TwinCell to test whether Deucravacitinib's target (**TYK2**) causally explains it.

**Run locally from a git clone:**

```bash
pip install "deeplife[notebook]" jupyterlab
git clone https://github.com/deeplifeai/deeplife
cd deeplife
jupyter lab tutorials/
```

Set **`DEEPLIFE_API_KEY`** (or use the notebook `getpass` prompt) before the TwinCell API steps. Everything up to that point runs locally; the API cells need an Internet connection.

| Location | Role |
|----------|------|
| [twincell.deeplife.co/docs/tutorials/](https://twincell.deeplife.co/docs/tutorials/) | Canonical hosted tutorials (browse or download) |
| [`tutorials/`](tutorials/) in a **git clone** | Same notebooks for local Jupyter |

**Contributors** can use `uv sync --group dev --extra notebook` instead of `pip` if you prefer the locked lockfile.

> After `pip install -U deeplife`, confirm imports match the [layout](#package-layout) you expect; the latest release is always on [PyPI](https://pypi.org/project/deeplife/).

---

## Development

```bash
uv sync --group dev
make check-all    # or: ruff, mypy, pytest — see Makefile
```

**CI:** [`.github/workflows/ci.yml`](https://github.com/deeplifeai/deeplife/blob/main/.github/workflows/ci.yml) runs on pushes and PRs to **`main`** / **`master`**: `uv sync --frozen --group dev`, **Ruff**, **mypy**, **pytest**, **`uv build`**, **`twine check --strict`**. [`.github/dependabot.yml`](https://github.com/deeplifeai/deeplife/blob/main/.github/dependabot.yml) bumps **GitHub Actions** weekly.

**Releases to PyPI:** [`.github/workflows/pypi-publish.yml`](https://github.com/deeplifeai/deeplife/blob/main/.github/workflows/pypi-publish.yml) runs the same checks, then publishes with **OIDC trusted publishing** (GitHub environment **`pypi`**, trusted publisher configured on the **`deeplife`** PyPI project). Bump **`version`** in `pyproject.toml`, push to **`main`**, then either push a tag matching **`v*`** or run the workflow manually from the **Actions** tab.
