Differential abundance and state analysis for cytometry data. Compare cell populations between conditions using statistical methods. Use when testing for significant changes in cell frequencies or marker expression between groups.
npx skills add https://github.com/BioTender-max/awesome-bio-agent-skills --skill bio-flow-cytometry-differential-analysis
Reference examples tested with: R stats (base), edgeR 4.0+, ggplot2 3.5+, limma 3.58+
Before using code patterns, verify installed versions match. If versions differ:
packageVersion('<pkg>') then ?function_name to verify parametersIf code throws ImportError, AttributeError, or TypeError, introspect the installed
package and adapt the example to match the actual API rather than retrying.
"Compare cell populations between my conditions" → Test for significant changes in cell type frequencies (differential abundance) or marker expression levels (differential state) between experimental groups.
CATALYST::testDA_edgeR() or diffcyt::testDA_GLMM()Goal: Test which cell population clusters differ in frequency between experimental conditions.
Approach: Create a design matrix and contrast from sample metadata, then run edgeR-based differential abundance testing on cluster counts per sample using testDA_edgeR from the diffcyt framework.
library(CATALYST)
library(diffcyt)
# Load clustered data
sce <- readRDS('sce_clustered.rds')
# Create design matrix
design <- createDesignMatrix(ei(sce), cols_design = 'condition')
# Create contrast
contrast <- createContrast(c(0, 1)) # Treatment vs Control
# Differential abundance test
res_DA <- testDA_edgeR(sce, design, contrast, cluster_id = 'meta20')
# View results
rowData(res_DA)$cluster_id
rowData(res_DA)$p_adj
# Significant clusters
sig_DA <- rowData(res_DA)$p_adj < 0.05
table(sig_DA)
# Test for marker expression differences within clusters
res_DS <- testDS_limma(sce, design, contrast,
cluster_id = 'meta20',
markers_include = rownames(sce)[rowData(sce)$marker_class == 'state'])
# Results per marker per cluster
ds_results <- rowData(res_DS)
# DA results heatmap
plotDiffHeatmap(sce, res_DA, all = TRUE, fdr = 0.05)
# DS results heatmap
plotDiffHeatmap(sce, res_DS, all = TRUE, fdr = 0.05)
# Abundance by condition
plotAbundances(sce, k = 'meta20', by = 'cluster_id', group_by = 'condition')
library(tidyverse)
# Get cluster frequencies per sample
freqs <- colData(sce) %>%
as.data.frame() %>%
group_by(sample_id, condition, cluster_id = cluster_ids(sce, 'meta20')) %>%
summarise(n = n(), .groups = 'drop') %>%
group_by(sample_id) %>%
mutate(freq = n / sum(n) * 100)
# Test each cluster
test_abundance <- function(df, cluster) {
cluster_data <- filter(df, cluster_id == cluster)
ctrl <- filter(cluster_data, condition == 'Control')$freq
treat <- filter(cluster_data, condition == 'Treatment')$freq
if (length(ctrl) >= 2 && length(treat) >= 2) {
test <- t.test(treat, ctrl)
return(data.frame(
cluster = cluster,
fc = mean(treat) / mean(ctrl),
pvalue = test$p.value
))
}
return(NULL)
}
results <- map_dfr(unique(freqs$cluster_id), ~test_abundance(freqs, .x))
results$padj <- p.adjust(results$pvalue, method = 'BH')
library(lme4)
library(lmerTest)
# For paired/repeated measures designs
# Random effect for patient/donor
fit_mixed <- function(df, cluster) {
cluster_data <- filter(df, cluster_id == cluster)
model <- lmer(freq ~ condition + (1|patient_id), data = cluster_data)
coef <- summary(model)$coefficients
return(data.frame(
cluster = cluster,
estimate = coef[2, 'Estimate'],
pvalue = coef[2, 'Pr(>|t|)']
))
}
library(citrus)
# Prepare data
fcs_files <- list.files('data', pattern = '\\.fcs$', full.names = TRUE)
labels <- c(rep('Control', 2), rep('Treatment', 2))
# Run CITRUS
citrus_result <- citrus(
fcs_files,
labels,
fileSampleSize = 1000,
featureType = 'abundances',
modelType = 'glmnet',
family = 'classification'
)
# Get significant clusters
citrus_plot(citrus_result)
library(ggplot2)
# From DA results
da_df <- as.data.frame(rowData(res_DA))
da_df$significant <- da_df$p_adj < 0.05
ggplot(da_df, aes(x = logFC, y = -log10(p_adj), color = significant)) +
geom_point() +
geom_hline(yintercept = -log10(0.05), linetype = 'dashed') +
geom_vline(xintercept = c(-1, 1), linetype = 'dashed') +
scale_color_manual(values = c('gray', 'red')) +
theme_bw() +
labs(title = 'Differential Abundance')
# Combine DA and DS results
da_results <- as.data.frame(rowData(res_DA))
da_results$analysis <- 'DA'
ds_results <- as.data.frame(rowData(res_DS))
ds_results$analysis <- 'DS'
# Save
write.csv(da_results, 'da_results.csv', row.names = FALSE)
write.csv(ds_results, 'ds_results.csv', row.names = FALSE)
# For multiple conditions
design_full <- model.matrix(~ 0 + condition, data = ei(sce))
colnames(design_full) <- levels(factor(ei(sce)$condition))
# Multiple contrasts
contrasts <- makeContrasts(
TreatA_vs_Ctrl = TreatmentA - Control,
TreatB_vs_Ctrl = TreatmentB - Control,
TreatA_vs_B = TreatmentA - TreatmentB,
levels = design_full
)
# Test each contrast
res_list <- lapply(1:ncol(contrasts), function(i) {
testDA_edgeR(sce, design_full, contrasts[, i], cluster_id = 'meta20')
})
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup
Use when implementing any feature or bugfix, before writing implementation code
Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes
Use when about to claim work is complete, fixed, or passing, before committing or creating PRs - requires running verification commands and confirming output before making any success claims; evidence before assertions always
Expert guidance for systematic backtesting of trading strategies. Use when developing, testing, stress-testing, or validating quantitative trading strategies. Covers "beating ideas to death" methodology, parameter robustness testing, slippage modeling, bias prevention, and interpreting backtest results. Applicable when user asks about backtesting, strategy validation, robustness testing, avoiding overfitting, or systematic trading development.
Cloud laboratory platform for automated protein testing and validation. Use when designing proteins and needing experimental validation including binding assays, expression testing, thermostability measurements, enzyme activity assays, or protein sequence optimization. Also use for submitting experiments via API, tracking experiment status, downloading results, optimizing protein sequences for better expression using computational tools (NetSolP, SoluProt, SolubleMPNN, ESM), or managing protein design workflows with wet-lab validation.
This skill should be used for time series machine learning tasks including classification, regression, clustering, forecasting, anomaly detection, segmentation, and similarity search. Use when working with temporal data, sequential patterns, or time-indexed observations requiring specialized algorithms beyond standard ML approaches. Particularly suited for univariate and multivariate time series analysis with scikit-learn compatible APIs.
Take biotender-max/bio-flow-cytometry-differential-analysis from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.