Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0805d46
Update ANNotate for local annotation
anashen Jul 16, 2026
c1910c8
Improve client-server interaction and user-facing messages
anashen Jul 16, 2026
b8571ef
Align arguments with panhumanpy defaults
anashen Jul 16, 2026
629c636
Update download process for rds files returned in cloud-based annotation
anashen Jul 16, 2026
dc00d6d
Update user-facing messages in CloudAzimuth
anashen Jul 16, 2026
01d7718
Add minor fixes for cmd check warnings
anashen Jul 16, 2026
af762ab
Refactor utility functions
anashen Jul 16, 2026
80208d9
Add tests for interface + packaging
anashen Jul 16, 2026
509c23f
Update intro vignette
anashen Jul 16, 2026
2670281
Add github action for tests
anashen Jul 16, 2026
95ad8db
Update documentation
anashen Jul 16, 2026
569286f
Clean up docs
anashen Jul 16, 2026
2425981
Update ignore directives
anashen Jul 16, 2026
665b8ee
Add _pkgdown.yml
anashen Jul 16, 2026
68abb18
Update NAMESPACE
anashen Jul 16, 2026
be8004e
Update DESCRIPTION
anashen Jul 16, 2026
e30ef9a
Bump version
anashen Jul 16, 2026
947b229
Remove test_obj.rds
anashen Jul 16, 2026
6cc1273
Update README.md
anashen Jul 16, 2026
5cdafa4
Update r-tests.yml
anashen Jul 16, 2026
7bcc7e3
Update test_interface.R
anashen Jul 16, 2026
e5d57b7
Minor edit to user-facing message in ANNotate
anashen Jul 16, 2026
e4762a2
Update API version check to include timeout
anashen Jul 16, 2026
611bc0d
Point default IP back to azimuthapi.satijalab.org
anashen Jul 16, 2026
4650d81
Update documentation
anashen Jul 16, 2026
a0c9007
Make minor update to vignette
anashen Jul 16, 2026
2570315
Revert change to model_version in argument parser
anashen Jul 16, 2026
1fda87b
Fix defaults for ANNotate
anashen Jul 16, 2026
1a53598
Update documentation
anashen Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
^scratch$
^test_obj\.rds$
^utils$
^\.github$
^_pkgdown\.yml$
21 changes: 21 additions & 0 deletions .github/workflows/r-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: R tests

on:
pull_request:

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: r-lib/actions/setup-r@v2

- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: any::rcmdcheck
needs: test

- name: Run rcmdcheck
run: Rscript -e 'rcmdcheck::rcmdcheck(args = c("--no-manual"), error_on = "error")'
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
docs
*.csv
*.json
20 changes: 13 additions & 7 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
Package: AzimuthAPI
Title: Pan-Azimuth Web API Interface
Version: 0.2.0
Version: 0.9.0
Authors@R:
person("Satija", "Lab", email = "satijalabnygc@gmail.com", role = c("aut", "cre"))
Description: An R package providing an interface to the Pan-Azimuth Web API for single-cell RNA sequencing analysis.
License: MIT + file LICENSE
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.3
VignetteBuilder: knitr
Imports:
Seurat,
SeuratObject,
argparse,
cli,
curl,
dplyr,
ggplot2,
tibble,
httr,
jsonlite,
curl,
RCurl,
reticulate,
Matrix,
argparse,
yaml,
methods,
tibble,
RCurl,
reticulate,
rlang,
stats,
utils
Suggests:
knitr,
rmarkdown,
testthat
11 changes: 4 additions & 7 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,8 @@
export(ANNotate)
export(CloudAzimuth)
export(PrepLabel)
export(get_data)
export(if_gpu)
export(listen_to_progress)
export(make_QC_heatmap)
export(make_azimuth_QC_heatmaps)
export(package_obj)
export(process_azimuth_results)
export(read_obj_min)
export(run_azimuth_api)
importFrom(Matrix,Matrix)
importFrom(RCurl,url.exists)
importFrom(Seurat,"DefaultAssay<-")
Expand All @@ -35,6 +28,7 @@ importFrom(argparse,ArgumentParser)
importFrom(curl,curl_fetch_stream)
importFrom(curl,form_file)
importFrom(curl,handle_setform)
importFrom(curl,handle_setopt)
importFrom(curl,new_handle)
importFrom(dplyr,"%>%")
importFrom(dplyr,across)
Expand All @@ -54,13 +48,16 @@ importFrom(httr,GET)
importFrom(httr,POST)
importFrom(httr,content)
importFrom(httr,status_code)
importFrom(httr,timeout)
importFrom(httr,upload_file)
importFrom(httr,write_disk)
importFrom(jsonlite,fromJSON)
importFrom(methods,"slot<-")
importFrom(methods,slot)
importFrom(methods,slotNames)
importFrom(reticulate,r_to_py)
importFrom(rlang,"%||%")
importFrom(rlang,.data)
importFrom(stats,cor)
importFrom(stats,dist)
importFrom(stats,hclust)
Expand Down
109 changes: 79 additions & 30 deletions R/annotation.R
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
#' Run Pan-Human Azimuth annotation
#' Run Pan-human Azimuth annotation locally
#'
#' This function runs the Pan-human Azimuth model on a Seurat object to annotate cell types, via reticulate and the `panhumanpy` Python package. **We recommend using the `CloudAzimuth` function, which runs cloud-based annotation, can handle large datasets, and performs robust error handling.**
#'
#' This function requires the `panhumanpy` Python package to be installed and accessible via reticulate.
#'
#' @param query_obj Seurat object to annotate
#' @param feature_names_col Column name for feature names
Expand All @@ -8,8 +12,10 @@
#' @param norm_check_batch_size Batch size to inspect normalization of data
#' @param output_mode Output mode for annotated cell metadata
#' @param refine_labels Whether to refine labels
#' @param extract_embeddings Whether to azimuth embeddings
#' @param umap_embeddings Whether to include UMAP embeddings
#' @param map_to_cl One or more annotation columns to map to Cell Ontology labels
#' @param include_cl_id Whether to add Cell Ontology IDs to the output metadata
#' @param extract_embeddings Whether to extract Azimuth embeddings
#' @param umap_embeddings Whether to include UMAP of Azimuth embeddings
#' @param n_neighbors Number of neighbors for UMAP
#' @param n_components Number of components for UMAP
#' @param metric Distance metric for UMAP
Expand All @@ -23,55 +29,67 @@
#' @param cutoff_abs Absolute cutoff for label filtering
#' @param cutoff_frac Fractional cutoff for label filtering
#' @param model_version Version of the model to use
#' @param assay Assay to use for annotation
#'
#' @importFrom SeuratObject Idents<-
#' @importFrom reticulate r_to_py
#' @importFrom rlang %||%
#'
#' @concept annotation
#' @return Annotated Seurat object
#'
#' @export
ANNotate <- function(
query_obj,
feature_names_col = NULL,
annotation_pipeline = 'supervised',
eval_batch_size = 40960,
eval_batch_size = 8192L,
normalization_override = FALSE,
norm_check_batch_size = 1000,
norm_check_batch_size = 100L,
output_mode = 'minimal',
refine_labels = TRUE,
map_to_cl = NULL,
include_cl_id = FALSE,
extract_embeddings = TRUE,
umap_embeddings = TRUE,
n_neighbors = 30,
n_components = 2,
n_neighbors = 30L,
n_components = 2L,
metric = "cosine",
min_dist = 0.3,
umap_lr = 1.0,
umap_seed = 42,
umap_seed = 42L,
spread = 1.0,
verbose = TRUE,
model_version = "v0",
init = "spectral",
model_version = "v0",
process_obj = TRUE,
cutoff_abs = 5,
cutoff_frac = 0.001
cutoff_frac = 0.001,
assay = NULL
) {
options(warn = -1)
#source_data_dir <- paste0(python_module_path, source_data_dir)
cat("Running Pan-Human Azimuth:\n")
# python dependencies
annotate <- reticulate::import("panhumanpy.ANNotate")
sp <- reticulate::import("scipy.sparse")

cat("Running Pan-human Azimuth:\n")
cat("\n")

# Convert integers
eval_batch_size <- as.integer(eval_batch_size)
n_neighbors <- as.integer(n_neighbors)
n_components <- as.integer(n_components)
umap_seed <- as.integer(umap_seed)

assay <- assay %||% DefaultAssay(query_obj)

# Read and process the Seurat object
query <- read_obj_min(query_obj, feature_names_col)
X_query <- sp$csr_matrix(r_to_py(query$X_query))
query <- read_obj_min(query_obj, feature_names_col, assay_default = assay)
X_query <- sp$csr_matrix(reticulate::r_to_py(query$X_query))
query_features <- query$query_features
cells_meta <- query$query_cells_df

assay_cells <- query$assay_cells

# Run annotation core
core_outputs <- annotate$annotate_core(
X_query,
Expand All @@ -83,6 +101,8 @@ ANNotate <- function(
norm_check_batch_size,
output_mode,
refine_labels,
map_to_cl,
include_cl_id,
extract_embeddings,
umap_embeddings,
n_neighbors,
Expand All @@ -93,27 +113,56 @@ ANNotate <- function(
umap_seed,
spread,
verbose,
model_version,
init
init,
model_version
)

embeddings_mode <- core_outputs[[3]]
embeddings_dict <- core_outputs[[4]]
query_cells_df <- core_outputs[[10]]
if_umap_embeddings <- core_outputs[[11]]
umap_embeddings_dict <- core_outputs[[12]]
embeddings_dict <- core_outputs$embeddings_dict
umap_embeddings_dict <- core_outputs$umap_dict
cells_meta <- core_outputs$cells_meta

annotated_obj <- package_obj(embeddings_mode, embeddings_dict, if_umap_embeddings,
umap_embeddings_dict, query_cells_df, query_obj)
annotated_obj = package_obj(
extract_embeddings,
embeddings_dict,
umap_embeddings,
umap_embeddings_dict,
cells_meta,
assay_cells,
query_obj
)

if (process_obj) {
annotated_obj <- PrepLabel(annotated_obj,
label_id = 'final_level_label',
cutoff = min(cutoff_abs, cutoff_frac * ncol(annotated_obj)),
cutid = 'Other',
newid = 'azimuth_label')
if (process_obj){
annotated_obj <- PrepLabel(annotated_obj,
label_id = 'final_level_labels',
cutoff = min(cutoff_abs, cutoff_frac * ncol(annotated_obj)),
cutid = 'Other',
newid = 'azimuth_label')
Idents(annotated_obj) <- 'azimuth_label'
}

return(annotated_obj)
}

#' Prepare labels for annotation
#'
#' @param object Seurat object
#' @param label_id Column name for labels
#' @param newid New column name for processed labels
#' @param cutid Label for rejected cells
#' @param cutoff Minimum count threshold
#' @return Updated Seurat object
#' @concept annotation
#' @export
PrepLabel <- function(
object,
label_id = 'final_level_label',
newid = 'PrepLabel',
cutid = 'Other',
cutoff=10
) {
rejected_names <- names(which(table(object@meta.data[,label_id])<cutoff))
object@meta.data[,newid]=as.character(object@meta.data[,label_id])
rejected_cells <- which(object@meta.data[,label_id]%in%rejected_names)
object@meta.data[rejected_cells,newid]=cutid
return(object)
}
Loading
Loading