## Workflow 1: Cell Type Annotation using Marker Genes

### Overview
This workflow demonstrates how to perform automated cell type annotation for single-cell RNA-seq data. By leveraging enrichment analysis with a database of known cell type marker genes (like PanglaoDB), it calculates enrichment scores for each cell cluster. These scores help in assigning putative cell type labels, providing a robust starting point for more detailed manual annotation.

### Standard Steps
1.  **Data Input**
    -   **Data Source**: `AnnData` object from local memory.
    -   **Data Format**: The `.X` attribute should contain a log-normalized gene expression matrix (cells x genes).
    -   **Data Structure Check**: The object must contain cell cluster information, typically stored in `adata.obs['leiden']` or a similar categorical column.

2.  **External Resources**
    -   **Database Calls**: Uses `decoupler` to access `PanglaoDB`, a database of cell type markers.
    -   **Reference Data**: A table of cell type marker genes for a specific organism (e.g., human).

3.  **Preprocessing**
    -   **Quality Control**: The marker gene database is filtered to retain high-confidence markers. In the tutorial, this involves selecting "canonical" markers with high sensitivity for the specified organism.
    -   **Data Transformation**: The filtered marker data is formatted into a two-column DataFrame with `source` (cell type) and `target` (gene) columns, which is the standard input format for `decoupler`.

4.  **Core Analysis**
    -   **Method Name**: Enrichment scores are calculated using a univariate linear model via the `decoupler.mt.ulm` function.
    -   **Key Parameters**: `tmin=3`, which filters out cell types with fewer than 3 marker genes in the dataset.
    -   **Comparison Logic**: To identify the best cell type annotation for each cluster, the `decoupler.tl.rankby_group` function is used. It performs a statistical test (e.g., t-test) to compare the enrichment scores of each cluster against all others (`reference='rest'`).

5.  **Result Storage**
    -   **Data Structure Updates**: The primary `AnnData` object is updated with the analysis results.
    -   **Result Location**: Enrichment scores are stored in `adata.obsm['score_ulm']` and corresponding adjusted p-values in `adata.obsm['padj_ulm']`.
    -   **Output Format**: The `rankby_group` function returns a pandas DataFrame with ranked cell types per cluster.

6.  **Visualization**
    -   **Chart Types**: UMAPs, violin plots, and matrix plots (heatmaps) are used.
    -   **Visualization Tools**: `scanpy.pl.umap`, `scanpy.pl.violin`, `scanpy.pl.matrixplot`.
    -   **Presentation Purpose**:
        -   **UMAP**: To visualize the spatial distribution of cell type enrichment scores across the single-cell embedding.
        -   **Violin Plot**: To compare the distribution of scores for a specific cell type across different clusters.
        -   **Matrix Plot**: To display a summary of the top-scoring cell types for each cluster, facilitating final annotation.

### Technical Details
-   **Key Functions**:
    -   `decoupler.op.resource('PanglaoDB', ...)`: Fetches cell type marker genes.
    -   `decoupler.mt.ulm()`: Runs the enrichment analysis.
    -   `decoupler.pp.get_obsm()`: Extracts the results from `adata.obsm` into a new, clean `AnnData` object for easy plotting.
    -   `decoupler.tl.rankby_group()`: Identifies "marker" cell types for each cluster based on enrichment scores.

-   **Data Object Changes**:
    1.  **Initial State**: `adata` with expression data in `.X` and clusters in `.obs['leiden']`.
    2.  **After `dc.mt.ulm`**: `adata.obsm` is populated with `'score_ulm'` (DataFrame: cells x cell types) and `'padj_ulm'`.
    3.  **For Visualization**: A new `AnnData` object, `score`, is created using `dc.pp.get_obsm()`. This object contains the enrichment scores in its `.X` matrix, making it directly compatible with `scanpy` plotting functions.
    4.  **After Annotation**: The categories of `adata.obs['leiden']` are renamed based on the top-ranked cell types.

-   **Data Object Management**: A key step is the creation of a separate `AnnData` object (`score`) to handle the enrichment scores. This separates the primary expression data from the derived scores, simplifying downstream analysis and visualization of the results.

-   **Result Location**:
    -   Raw scores: `adata.obsm['score_ulm']`
    -   Ranked annotations: A pandas DataFrame generated by `dc.tl.rankby_group()`.

-   **Data Flow Tracking**:
    `adata` (expression) + `markers` (PanglaoDB) → `dc.mt.ulm` → `adata.obsm['score_ulm']` → `dc.pp.get_obsm` → `score` AnnData object → `dc.tl.rankby_group` → Ranked DataFrame → Manual/automatic annotation logic → `adata.obs` updated with cell type names.

### Generality Assessment
-   **Dependency Conditions**: The workflow's biological interpretation is dependent on a species-specific marker gene database (e.g., `organism='human'`). The computational method itself is species-agnostic.
-   **Extensibility**: Very high. Any custom or public marker gene set can be used. The enrichment method (`ulm`) can be replaced with other methods available in `decoupler`. The ranking logic in `rankby_group` is also customizable.
-   **Applicable Scenarios**: This workflow is broadly applicable to any clustered single-cell transcriptomics dataset where the goal is to assign cell type identities. It is an effective method for generating an initial, data-driven annotation draft.

### CODE EXAMPLE
```python
# CODE EXAMPLE for Cell Type Annotation using Marker Genes

import scanpy as sc
import decoupler as dc

# Load pre-processed AnnData object with expression and clusters
adata = dc.ds.pbmc3k()

# --- 1. External Resources & Preprocessing ---
# Fetch cell type markers from PanglaoDB for human
markers = dc.op.resource("PanglaoDB", organism="human")

# Filter for high-quality markers
markers = markers[
    markers["human"].astype(bool)
    & markers["canonical_marker"].astype(bool)
    & (markers["human_sensitivity"].astype(float) > 0.5)
]
# Format for decoupler: 'source' (cell type), 'target' (gene)
markers = markers.rename(columns={"cell_type": "source", "genesymbol": "target"})
markers = markers[["source", "target"]].drop_duplicates()

# --- 2. Core Analysis ---
# Run enrichment analysis using univariate linear model (ulm)
# Results are stored in adata.obsm
dc.mt.ulm(data=adata, net=markers, tmin=3)

# --- 3. Result Handling & Visualization ---
# Extract scores into a new AnnData object for plotting
score = dc.pp.get_obsm(adata, key="score_ulm")

# Visualize scores for a specific cell type on UMAP
sc.pl.umap(score, color=["NK cells", "leiden"], cmap="RdBu_r", vcenter=0)

# Rank potential cell types for each cluster
ranked_df = dc.tl.rankby_group(adata=score, groupby="leiden", method="t-test_overestim_var")

# Get top predicted cell type for each cluster
top_celltypes = ranked_df[ranked_df["stat"] > 0].groupby("group").head(1).set_index("group")["name"].to_dict()

# Visualize top predictions in a heatmap
sc.pl.matrixplot(
    adata=score,
    var_names=top_celltypes,
    groupby="leiden",
    dendrogram=True,
    standard_scale="var",
    cmap="RdBu_r",
)

# Annotate the original AnnData object
adata.obs['leiden'] = adata.obs['leiden'].cat.rename_categories(top_celltypes)
sc.pl.umap(adata, color='leiden', title='Final Automated Annotation')
```