Back to skills
extension
Category: Data & AnalyticsNo API key required

空间解卷积-CARD-R版

CARD空间反卷积,使用R进行计算和可视化,适用于胰腺导管腺癌等真实数据案例

personAuthor: user_30836134hubcommunity

CARD — 空间转录组细胞类型反卷积(R 原生版)

概述

CARD(Conditional Autoregressive Regression for Deconvolution)是一个 R 包,用于从空间转录组数据反卷积细胞类型组成。它结合 scRNA-seq 参考数据和空间表达数据,使用条件自回归模型估计每个 spot 的细胞类型比例。

本 Skill 提供 R 全流程:使用 CARD 包进行反卷积计算 + 使用 CARD 原生函数进行可视化,并提供基于 ggplot2 的扩展可视化作为补充。

核心特性:

  • R 原生实现:不依赖 Python 版本
  • 多分辨率支持:Visium、Visium HD(16µm/8µm)
  • 官方可视化:饼图、空间分布、相关性热图、基因表达图
  • 扩展可视化:平均比例条形图、主导细胞类型空间分布
  • 真实数据案例:胰腺导管腺癌(PDAC)Visium HD 16µm

适用场景

当用户需要执行以下任务时使用此 Skill:

  • 使用 R 进行空间转录组细胞类型反卷积
  • 从 Python 版 CARD 结果转换为 R 可视化兼容格式
  • 提取 Visium HD 空间坐标用于下游分析
  • 生成 CARD 官方可视化(饼图、空间分布、相关性热图等)
  • 基于 ggplot2 生成自定义扩展可视化

安装

环境要求

  • R: >= 4.0.0
  • 操作系统: Windows 10, macOS, Ubuntu 18.04+

安装步骤

1. 安装 R

CRAN 下载并安装 R >= 4.0.0

2. 安装 CARD R 包

install.packages("devtools")
devtools::install_github("YingMa0107/CARD")

3. 安装依赖包

install.packages(c(
  "SingleCellExperiment",
  "SummarizedExperiment",
  "concaveman",
  "sp",
  "Matrix",
  "ggplot2",
  "ggcorrplot",
  "MuSiC",
  "fields",
  "MCMCpack",
  "dplyr"
))

验证安装

library(CARD)
print("CARD 安装成功!")

常见安装问题

| 问题 | 解决方案 | |---|---| | installation of package 'CARD' had non-zero exit status | 确保 R >= 4.0.0;Windows 安装 Rtools | | 依赖包安装失败 | 逐个安装依赖包,查看具体错误 |


CARD 反卷积原理

CARD 使用条件自回归(CAR)模型,将空间表达视为空间相邻 spot 的平滑函数,结合 scRNA-seq 参考,估计每个 spot 的细胞类型比例矩阵。

核心步骤:

  1. 构建参考:从 scRNA-seq 数据获取每种细胞类型的 marker 基因表达
  2. 创建 CARD 对象:合并 scRNA-seq 参考和空间表达/坐标
  3. 反卷积计算:使用 CAR 模型估计比例矩阵
  4. 可视化:官方 CARD.visualize.* 函数 + 自定义 ggplot2

输入数据要求

| 数据 | 格式 | 说明 | |---|---|---| | scRNA-seq 表达矩阵 | matrix / dgCMatrix | 行=基因,列=细胞 | | scRNA-seq 元数据 | data.frame | 至少包含 cellTypesampleInfo 列 | | 空间表达矩阵 | matrix / dgCMatrix | 行=基因,列=spot | | 空间坐标 | data.frame | 列名 xy;行名=spot ID |

基因名需在 scRNA-seq 和空间数据之间一致。


完整可运行脚本

以下脚本均为最小可运行示例,按需复制到 .R 文件执行。

注意:脚本中 I:/TRAE skill 数据分析/... 为示例路径,使用时请替换为实际数据路径。


脚本 1 — 提取 CARD 分析用空间坐标

# Author: LKP <kunpeng.liao@abiosciences.com>
# Date:   2026-08-18
#
# extract_card_coordinates.R
# 提取CARD分析用的空间坐标

library(Seurat)

# 设置工作目录
setwd("I:/TRAE skill 数据分析/untested-skills-test/spatial-16um-test")

# 读取比例数据以获取spot名称
prop_df <- read.csv("card_16um_results/card_cell_type_proportions_16um.csv", row.names = 1)
print("比例数据spot数量:")
print(nrow(prop_df))
print("前5个spot名称:")
print(rownames(prop_df)[1:5])

# 读取空间数据
spatial_dir <- "I:/TRAE skill 数据分析/测试数据/空间-HD/binned_outputs/square_016um"
spatial_data <- Load10X_Spatial(
    data.dir = spatial_dir,
    filename = "filtered_feature_bc_matrix.h5"
)

# 提取空间坐标
spatial_location <- GetTissueCoordinates(spatial_data)
spatial_location <- spatial_location[, c("x", "y")]

print("空间数据spot数量:")
print(nrow(spatial_location))
print("前5个spot名称:")
print(rownames(spatial_location)[1:5])

# 只保留与CARD结果匹配的spots
spatial_location_matched <- spatial_location[rownames(prop_df), ]

print("匹配后的spot数量:")
print(nrow(spatial_location_matched))
print("前5行:")
print(head(spatial_location_matched, 5))

# 保存坐标
write.csv(spatial_location_matched, "card_16um_results/card_spatial_coordinates_16um.csv")
print("✓ 空间坐标已保存到 card_16um_results/card_spatial_coordinates_16um.csv")

脚本 2 — Python 版 CARD 结果转 R 兼容 RDS

# Author: LKP <kunpeng.liao@abiosciences.com>
# Date:   2026-08-18
#
# convert_card_to_rds.R
# 将CARD Python版数据转换为RDS格式并修复S4类型

library(CARD)
library(Seurat)

# 设置工作目录
setwd("I:/TRAE skill 数据分析/untested-skills-test/spatial-16um-test")

print("==================================================")
print("CARD数据转换 - Python版转RDS格式")
print("==================================================")

# 1. 读取Python版CARD结果
print("\n【1/5】读取Python版CARD结果...")
prop_df <- read.csv("card_16um_results/card_cell_type_proportions_16um.csv", row.names = 1)
print(paste("   比例数据:", nrow(prop_df), "spots x", ncol(prop_df), "cell types"))

# 读取空间坐标
spatial_coord <- read.csv("card_16um_results/card_spatial_coordinates_16um.csv", row.names = 1)
print(paste("   空间坐标:", nrow(spatial_coord), "spots"))

# 2. 创建最小化的CARD对象结构
print("\n【2/5】创建CARD兼容的数据结构...")

# 将比例数据转换为普通矩阵(避免S4类型问题)
prop_matrix <- as.matrix(prop_df)
print(paste("   比例矩阵类型:", class(prop_matrix)))
print(paste("   比例矩阵维度:", nrow(prop_matrix), "x", ncol(prop_matrix)))

# 确保空间坐标是data.frame
spatial_loc <- as.data.frame(spatial_coord[, c("x", "y")])
print(paste("   空间坐标类型:", class(spatial_loc)))
print(paste("   空间坐标维度:", nrow(spatial_loc), "x", ncol(spatial_loc)))

# 3. 尝试创建S4对象(可选)
print("\n【3/5】创建S4对象...")

# 定义一个简单的S4类来存储结果
setClass("CARDResult",
         slots = list(
             Proportion_CARD = "matrix",
             spatial_location = "data.frame"
         ))

# 创建对象
CARD_result <- new("CARDResult",
                   Proportion_CARD = prop_matrix,
                   spatial_location = spatial_loc)

print("   S4对象创建成功")
print(paste("   Proportion_CARD类型:", class(CARD_result@Proportion_CARD)))

# 4. 保存为RDS
print("\n【4/5】保存为RDS格式...")
saveRDS(CARD_result, "card_16um_results/CARD_result_converted.rds")
print("   ✓ 已保存到 card_16um_results/CARD_result_converted.rds")

# 同时保存为RData(兼容性更好)
save(CARD_result, prop_matrix, spatial_loc, file = "card_16um_results/CARD_result_converted.RData")
print("   ✓ 已保存到 card_16um_results/CARD_result_converted.RData")

# 5. 测试直接可视化(不通过S4对象)
print("\n【5/5】测试直接可视化...")

# 定义颜色
colors <- c("#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd",
            "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf",
            "#aec7e8", "#ffbb78", "#98df8a", "#ff9896")

# 测试1:饼图
print("   测试饼图...")
tryCatch({
    p_pie <- CARD.visualize.pie(
        proportion = prop_matrix,
        spatial_location = spatial_loc,
        colors = colors,
        radius = 0.52
    )
    print("   ✓ 饼图函数调用成功")

    # 保存
    pdf("card_16um_results/card_r_visualization_pie_test.pdf", width = 10, height = 8)
    print(p_pie)
    dev.off()
    print("   ✓ 饼图已保存")
}, error = function(e) {
    print(paste("   ⚠️ 饼图失败:", e$message))
})

# 测试2:单个细胞类型分布
print("   测试单个细胞类型分布...")
ct.visualize <- colnames(prop_matrix)[1:min(4, ncol(prop_matrix))]
print(paste("   可视化细胞类型:", paste(ct.visualize, collapse = ", ")))

tryCatch({
    p_prop <- CARD.visualize.prop(
        proportion = prop_matrix,
        spatial_location = spatial_loc,
        ct.visualize = ct.visualize,
        colors = c("lightblue", "lightyellow", "red"),
        NumCols = 2,
        pointSize = 3.0
    )
    print("   ✓ 单个细胞类型分布函数调用成功")

    # 保存
    pdf("card_16um_results/card_r_visualization_prop_test.pdf", width = 12, height = 10)
    print(p_prop)
    dev.off()
    print("   ✓ 单个细胞类型分布图已保存")
}, error = function(e) {
    print(paste("   ⚠️ 单个细胞类型分布失败:", e$message))
})

# 测试3:相关性热图
print("   测试相关性热图...")
tryCatch({
    p_cor <- CARD.visualize.Cor(prop_matrix, colors = NULL)
    print("   ✓ 相关性热图函数调用成功")

    # 保存
    pdf("card_16um_results/card_r_visualization_cor_test.pdf", width = 10, height = 8)
    print(p_cor)
    dev.off()
    print("   ✓ 相关性热图已保存")
}, error = function(e) {
    print(paste("   ⚠️ 相关性热图失败:", e$message))
})

print("\n==================================================")
print("转换完成!")
print("==================================================")
print("\n生成的文件:")
print("  📦 CARD_result_converted.rds - S4对象")
print("  📦 CARD_result_converted.RData - 包含所有变量")
print("  🖼️  card_r_visualization_*_test.pdf - 测试可视化")

脚本 3 — CARD 官方可视化(基于 RDS 对象)

# Author: LKP <kunpeng.liao@abiosciences.com>
# Date:   2026-08-18
#
# card_official_visualization.R
# 基于CARD包原生函数生成可视化

library(CARD)
library(ggplot2)

# 设置工作目录
setwd("I:/TRAE skill 数据分析/untested-skills-test/spatial-16um-test")

# 读取CARD结果
CARD_obj <- readRDS("card_16um_results/card_object_16um.rds")

print("CARD对象内容:")
print(slotNames(CARD_obj))

# 读取比例数据
prop_df <- read.csv("card_16um_results/card_cell_type_proportions_16um.csv", row.names = 1)
print("比例数据维度:")
print(dim(prop_df))
print("前5行:")
print(head(prop_df, 5))

# 获取空间坐标
spatial_loc <- CARD_obj@spatial_location
print("空间坐标维度:")
print(dim(spatial_loc))
print("前5行:")
print(head(spatial_loc, 5))

# 定义颜色
colors <- c("#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd",
            "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf",
            "#aec7e8", "#ffbb78", "#98df8a", "#ff9896")

# 1. 饼图可视化 (适合<500 spots)
print("生成饼图...")
tryCatch({
    p_pie <- CARD.visualize.pie(
        proportion = CARD_obj@Proportion_CARD,
        spatial_location = CARD_obj@spatial_location,
        colors = colors,
        radius = 0.52
    )

    # 保存PDF
    pdf("card_official_pie.pdf", width = 10, height = 8)
    print(p_pie)
    dev.off()

    # 保存PNG
    png("card_official_pie.png", width = 1000, height = 800, res = 150)
    print(p_pie)
    dev.off()

    print("✅ 饼图生成成功")
}, error = function(e) {
    print(paste("⚠️ 饼图生成失败:", e$message))
})

# 2. 单个细胞类型空间分布
print("生成单个细胞类型分布图...")
ct.visualize <- colnames(prop_df)[1:min(8, ncol(prop_df))]
print(paste("可视化细胞类型:", paste(ct.visualize, collapse = ", ")))

tryCatch({
    p_prop <- CARD.visualize.prop(
        proportion = CARD_obj@Proportion_CARD,
        spatial_location = CARD_obj@spatial_location,
        ct.visualize = ct.visualize,
        colors = c("lightblue", "lightyellow", "red"),
        NumCols = 4,
        pointSize = 3.0
    )

    # 保存PDF
    pdf("card_official_prop_individual.pdf", width = 16, height = 12)
    print(p_prop)
    dev.off()

    # 保存PNG
    png("card_official_prop_individual.png", width = 1600, height = 1200, res = 150)
    print(p_prop)
    dev.off()

    print("✅ 单个细胞类型分布图生成成功")
}, error = function(e) {
    print(paste("⚠️ 单个细胞类型分布图生成失败:", e$message))
})

# 3. 两个细胞类型对比
print("生成两个细胞类型对比图...")
if(ncol(prop_df) >= 2) {
    ct2.visualize <- colnames(prop_df)[1:2]
    print(paste("对比细胞类型:", paste(ct2.visualize, collapse = " vs ")))

    tryCatch({
        p_2ct <- CARD.visualize.prop.2CT(
            proportion = CARD_obj@Proportion_CARD,
            spatial_location = CARD_obj@spatial_location,
            ct2.visualize = ct2.visualize,
            colors = list(
                c("lightblue", "lightyellow", "red"),
                c("lightblue", "lightyellow", "black")
            )
        )

        # 保存PDF
        pdf("card_official_2celltypes.pdf", width = 12, height = 10)
        print(p_2ct)
        dev.off()

        # 保存PNG
        png("card_official_2celltypes.png", width = 1200, height = 1000, res = 150)
        print(p_2ct)
        dev.off()

        print("✅ 两个细胞类型对比图生成成功")
    }, error = function(e) {
        print(paste("⚠️ 两个细胞类型对比图生成失败:", e$message))
    })
}

# 4. 细胞类型相关性热图
print("生成细胞类型相关性热图...")
tryCatch({
    p_cor <- CARD.visualize.Cor(CARD_obj@Proportion_CARD, colors = NULL)

    # 保存PDF
    pdf("card_official_correlation.pdf", width = 10, height = 8)
    print(p_cor)
    dev.off()

    # 保存PNG
    png("card_official_correlation.png", width = 1000, height = 800, res = 150)
    print(p_cor)
    dev.off()

    print("✅ 细胞类型相关性热图生成成功")
}, error = function(e) {
    print(paste("⚠️ 细胞类型相关性热图生成失败:", e$message))
})

# 5. 基因表达可视化 (使用空间数据)
print("生成基因表达图...")
# 获取高表达基因
spatial_counts <- CARD_obj@spatial_countMat
mean_exp <- rowMeans(spatial_counts)
top_genes <- names(sort(mean_exp, decreasing = TRUE))[1:6]
print(paste("可视化基因:", paste(top_genes, collapse = ", ")))

tryCatch({
    p_gene <- CARD.visualize.gene(
        spatial_expression = CARD_obj@spatial_countMat,
        spatial_location = CARD_obj@spatial_location,
        gene.visualize = top_genes,
        colors = NULL,
        NumCols = 3
    )

    # 保存PDF
    pdf("card_official_gene_expression.pdf", width = 15, height = 10)
    print(p_gene)
    dev.off()

    # 保存PNG
    png("card_official_gene_expression.png", width = 1500, height = 1000, res = 150)
    print(p_gene)
    dev.off()

    print("✅ 基因表达图生成成功")
}, error = function(e) {
    print(paste("⚠️ 基因表达图生成失败:", e$message))
})

print("官方可视化生成完成!")
print("生成的文件:")
print(list.files(pattern = "card_official.*\\.(pdf|png)$"))

脚本 4 — CARD 官方可视化 v2(基于 CSV 数据)

# Author: LKP <kunpeng.liao@abiosciences.com>
# Date:   2026-08-18
#
# card_official_visualization_v2.R
# 基于CARD包原生函数生成可视化 - 使用CSV数据

library(CARD)
library(ggplot2)
library(reshape2)

# 设置工作目录
setwd("I:/TRAE skill 数据分析/untested-skills-test/spatial-16um-test")

# 读取比例数据
prop_df <- read.csv("card_16um_results/card_cell_type_proportions_16um.csv", row.names = 1)
print("比例数据维度:")
print(dim(prop_df))
print("细胞类型:")
print(colnames(prop_df))

# 读取空间坐标
spatial_coord <- read.csv("card_16um_results/card_spatial_coordinates_16um.csv", row.names = 1)
print("空间坐标维度:")
print(dim(spatial_coord))
print("前5行:")
print(head(spatial_coord, 5))

# 定义颜色
colors <- c("#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd",
            "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf",
            "#aec7e8", "#ffbb78", "#98df8a", "#ff9896")

# 1. 饼图可视化 (适合<500 spots)
print("生成饼图...")
tryCatch({
    # 确保数据格式正确
    prop_matrix <- as.matrix(prop_df)
    spatial_loc <- spatial_coord[, c("x", "y")]

    p_pie <- CARD.visualize.pie(
        proportion = prop_matrix,
        spatial_location = spatial_loc,
        colors = colors,
        radius = 0.52
    )

    # 保存PDF
    pdf("card_16um_results/card_official_pie.pdf", width = 10, height = 8)
    print(p_pie)
    dev.off()

    # 保存PNG
    png("card_16um_results/card_official_pie.png", width = 1000, height = 800, res = 150)
    print(p_pie)
    dev.off()

    print("✅ 饼图生成成功")
}, error = function(e) {
    print(paste("⚠️ 饼图生成失败:", e$message))
})

# 2. 单个细胞类型空间分布
print("生成单个细胞类型分布图...")
ct.visualize <- colnames(prop_df)[1:min(8, ncol(prop_df))]
print(paste("可视化细胞类型:", paste(ct.visualize, collapse = ", ")))

tryCatch({
    prop_matrix <- as.matrix(prop_df)
    spatial_loc <- spatial_coord[, c("x", "y")]

    p_prop <- CARD.visualize.prop(
        proportion = prop_matrix,
        spatial_location = spatial_loc,
        ct.visualize = ct.visualize,
        colors = c("lightblue", "lightyellow", "red"),
        NumCols = 4,
        pointSize = 3.0
    )

    # 保存PDF
    pdf("card_16um_results/card_official_prop_individual.pdf", width = 16, height = 12)
    print(p_prop)
    dev.off()

    # 保存PNG
    png("card_16um_results/card_official_prop_individual.png", width = 1600, height = 1200, res = 150)
    print(p_prop)
    dev.off()

    print("✅ 单个细胞类型分布图生成成功")
}, error = function(e) {
    print(paste("⚠️ 单个细胞类型分布图生成失败:", e$message))
})

# 3. 两个细胞类型对比
print("生成两个细胞类型对比图...")
if(ncol(prop_df) >= 2) {
    ct2.visualize <- colnames(prop_df)[1:2]
    print(paste("对比细胞类型:", paste(ct2.visualize, collapse = " vs ")))

    tryCatch({
        prop_matrix <- as.matrix(prop_df)
        spatial_loc <- spatial_coord[, c("x", "y")]

        p_2ct <- CARD.visualize.prop.2CT(
            proportion = prop_matrix,
            spatial_location = spatial_loc,
            ct2.visualize = ct2.visualize,
            colors = list(
                c("lightblue", "lightyellow", "red"),
                c("lightblue", "lightyellow", "black")
            )
        )

        # 保存PDF
        pdf("card_16um_results/card_official_2celltypes.pdf", width = 12, height = 10)
        print(p_2ct)
        dev.off()

        # 保存PNG
        png("card_16um_results/card_official_2celltypes.png", width = 1200, height = 1000, res = 150)
        print(p_2ct)
        dev.off()

        print("✅ 两个细胞类型对比图生成成功")
    }, error = function(e) {
        print(paste("⚠️ 两个细胞类型对比图生成失败:", e$message))
    })
}

# 4. 细胞类型相关性热图
print("生成细胞类型相关性热图...")
tryCatch({
    prop_matrix <- as.matrix(prop_df)

    p_cor <- CARD.visualize.Cor(prop_matrix, colors = NULL)

    # 保存PDF
    pdf("card_16um_results/card_official_correlation.pdf", width = 10, height = 8)
    print(p_cor)
    dev.off()

    # 保存PNG
    png("card_16um_results/card_official_correlation.png", width = 1000, height = 800, res = 150)
    print(p_cor)
    dev.off()

    print("✅ 细胞类型相关性热图生成成功")
}, error = function(e) {
    print(paste("⚠️ 细胞类型相关性热图生成失败:", e$message))
})

print("官方可视化生成完成!")
print("生成的文件:")
print(list.files("card_16um_results", pattern = "card_official.*\\.(pdf|png)$"))

脚本 5 — CARD 完整 R 可视化(含饼图/分布/扩展)

# Author: LKP <kunpeng.liao@abiosciences.com>
# Date:   2026-08-18
#
# card_complete_r_visualization.R
# 使用转换后的数据生成所有官方可视化

library(CARD)
library(ggplot2)
library(gridExtra)

# 设置工作目录
setwd("I:/TRAE skill 数据分析/untested-skills-test/spatial-16um-test")

print("==================================================")
print("CARD 完整R可视化")
print("==================================================")

# 1. 读取转换后的数据
print("\n【1/4】读取转换后的数据...")
load("card_16um_results/CARD_result_converted.RData")
print("   ✓ 数据加载成功")

# 提取数据
prop_matrix <- CARD_result@Proportion_CARD
spatial_loc <- CARD_result@spatial_location

print(paste("   比例矩阵:", nrow(prop_matrix), "spots x", ncol(prop_matrix), "cell types"))
print(paste("   细胞类型:", paste(colnames(prop_matrix), collapse = ", ")))

# 计算统计
mean_props <- colMeans(prop_matrix)
mean_props_sorted <- sort(mean_props, decreasing = TRUE)
top4_cell_types <- names(mean_props_sorted)[1:4]

print("\n各细胞类型平均比例:")
for (ct in names(mean_props_sorted)) {
    print(paste("  -", ct, ":", round(mean_props_sorted[ct], 4),
                paste0("(", round(mean_props_sorted[ct]*100, 2), "%)")))
}

# 2. 定义颜色方案
print("\n【2/4】定义颜色方案...")
colors <- c("#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd",
            "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf",
            "#aec7e8", "#ffbb78", "#98df8a", "#ff9896")

# 3. 生成所有可视化
print("\n【3/4】生成所有可视化...")

# 1. 饼图可视化
print("   1/7 生成饼图...")
p_pie <- CARD.visualize.pie(
    proportion = prop_matrix,
    spatial_location = spatial_loc,
    colors = colors,
    radius = 0.52
)

# 保存饼图
pdf("card_16um_results/card_r_pie.pdf", width = 10, height = 8)
print(p_pie)
dev.off()

png("card_16um_results/card_r_pie.png", width = 1000, height = 800, res = 150)
print(p_pie)
dev.off()

print("      ✓ 饼图已保存")

# 2. 前4种细胞类型分布
print("   2/7 生成前4种细胞类型分布...")
p_top4 <- CARD.visualize.prop(
    proportion = prop_matrix,
    spatial_location = spatial_loc,
    ct.visualize = top4_cell_types,
    colors = c("lightblue", "lightyellow", "red"),
    NumCols = 2,
    pointSize = 3.0
)

# 保存
pdf("card_16um_results/card_r_top4_celltypes.pdf", width = 14, height = 12)
print(p_top4)
dev.off()

png("card_16um_results/card_r_top4_celltypes.png", width = 1400, height = 1200, res = 150)
print(p_top4)
dev.off()

print("      ✓ 前4种细胞类型分布已保存")

# 3. 所有细胞类型分布(分两组)
print("   3/7 生成所有细胞类型分布...")
all_cell_types <- colnames(prop_matrix)

# 第一组:前7种
p_all_1 <- CARD.visualize.prop(
    proportion = prop_matrix,
    spatial_location = spatial_loc,
    ct.visualize = all_cell_types[1:7],
    colors = c("lightblue", "lightyellow", "red"),
    NumCols = 4,
    pointSize = 2.5
)

pdf("card_16um_results/card_r_all_celltypes_1.pdf", width = 16, height = 10)
print(p_all_1)
dev.off()

png("card_16um_results/card_r_all_celltypes_1.png", width = 1600, height = 1000, res = 150)
print(p_all_1)
dev.off()

# 第二组:后7种
p_all_2 <- CARD.visualize.prop(
    proportion = prop_matrix,
    spatial_location = spatial_loc,
    ct.visualize = all_cell_types[8:14],
    colors = c("lightblue", "lightyellow", "red"),
    NumCols = 4,
    pointSize = 2.5
)

pdf("card_16um_results/card_r_all_celltypes_2.pdf", width = 16, height = 10)
print(p_all_2)
dev.off()

png("card_16um_results/card_r_all_celltypes_2.png", width = 1600, height = 1000, res = 150)
print(p_all_2)
dev.off()

print("      ✓ 所有细胞类型分布已保存")

# 4. 两个细胞类型对比
print("   4/7 生成两个细胞类型对比...")
if (ncol(prop_matrix) >= 2) {
    ct2 <- names(mean_props_sorted)[1:2]
    p_2ct <- CARD.visualize.prop.2CT(
        proportion = prop_matrix,
        spatial_location = spatial_loc,
        ct2.visualize = ct2,
        colors = list(
            c("lightblue", "lightyellow", "red"),
            c("lightblue", "lightyellow", "black")
        )
    )

    pdf("card_16um_results/card_r_2celltypes_comparison.pdf", width = 12, height = 10)
    print(p_2ct)
    dev.off()

    png("card_16um_results/card_r_2celltypes_comparison.png", width = 1200, height = 1000, res = 150)
    print(p_2ct)
    dev.off()

    print("      ✓ 两个细胞类型对比已保存")
}

# 5. 细胞类型相关性热图
print("   5/7 生成细胞类型相关性热图...")
p_cor <- CARD.visualize.Cor(prop_matrix, colors = NULL)

pdf("card_16um_results/card_r_correlation_heatmap.pdf", width = 12, height = 10)
print(p_cor)
dev.off()

png("card_16um_results/card_r_correlation_heatmap.png", width = 1200, height = 1000, res = 150)
print(p_cor)
dev.off()

print("      ✓ 细胞类型相关性热图已保存")

# 6. 自定义:平均比例条形图
print("   6/7 生成平均比例条形图...")
p_bar <- ggplot(data.frame(
    CellType = names(mean_props_sorted),
    Proportion = mean_props_sorted
), aes(x = reorder(CellType, Proportion), y = Proportion, fill = CellType)) +
    geom_bar(stat = "identity") +
    coord_flip() +
    scale_fill_manual(values = colors) +
    labs(title = "CARD: Mean Cell Type Proportions",
         x = "Cell Type",
         y = "Mean Proportion") +
    theme_minimal() +
    theme(legend.position = "none")

pdf("card_16um_results/card_r_mean_proportions_barplot.pdf", width = 10, height = 8)
print(p_bar)
dev.off()

png("card_16um_results/card_r_mean_proportions_barplot.png", width = 1000, height = 800, res = 150)
print(p_bar)
dev.off()

print("      ✓ 平均比例条形图已保存")

# 7. 自定义:主导细胞类型分布
print("   7/7 生成主导细胞类型分布...")
dominant_types <- colnames(prop_matrix)[apply(prop_matrix, 1, which.max)]
dominant_df <- data.frame(
    x = spatial_loc$x,
    y = spatial_loc$y,
    DominantType = dominant_types
)

# 为每种主导类型分配颜色
type_colors <- setNames(colors[1:length(unique(dominant_types))], unique(dominant_types))

p_dominant <- ggplot(dominant_df, aes(x = x, y = y, color = DominantType)) +
    geom_point(size = 2, alpha = 0.8) +
    scale_color_manual(values = type_colors) +
    labs(title = "CARD: Dominant Cell Type Distribution",
         x = "X coordinate",
         y = "Y coordinate") +
    theme_minimal() +
    coord_equal() +
    theme(legend.position = "right")

pdf("card_16um_results/card_r_dominant_celltypes.pdf", width = 14, height = 10)
print(p_dominant)
dev.off()

png("card_16um_results/card_r_dominant_celltypes.png", width = 1400, height = 1000, res = 150)
print(p_dominant)
dev.off()

print("      ✓ 主导细胞类型分布已保存")

# 4. 生成总结报告
print("\n【4/4】生成总结报告...")

sink("card_16um_results/card_r_visualization_summary.txt")
cat("=== CARD R可视化总结 ===\n\n")
cat("生成时间:", format(Sys.time(), "%Y-%m-%d %H:%M:%S"), "\n")
cat("数据: 转换后的Python版CARD结果\n")
cat("Spots:", nrow(prop_matrix), "\n")
cat("细胞类型:", ncol(prop_matrix), "\n\n")

cat("生成的可视化文件:\n")
cat("  1. card_r_pie.pdf/png - 饼图\n")
cat("  2. card_r_top4_celltypes.pdf/png - 前4种细胞类型分布\n")
cat("  3. card_r_all_celltypes_1.pdf/png - 所有细胞类型分布(第1组)\n")
cat("  4. card_r_all_celltypes_2.pdf/png - 所有细胞类型分布(第2组)\n")
cat("  5. card_r_2celltypes_comparison.pdf/png - 两个细胞类型对比\n")
cat("  6. card_r_correlation_heatmap.pdf/png - 细胞类型相关性热图\n")
cat("  7. card_r_mean_proportions_barplot.pdf/png - 平均比例条形图\n")
cat("  8. card_r_dominant_celltypes.pdf/png - 主导细胞类型分布\n\n")

cat("各细胞类型平均比例:\n")
for (ct in names(mean_props_sorted)) {
    cat(paste("  -", ct, ":", round(mean_props_sorted[ct], 4),
              paste0("(", round(mean_props_sorted[ct]*100, 2), "%)"), "\n"))
}

sink()

print("   ✓ 总结报告已保存")

print("\n==================================================")
print("R可视化完成!")
print("==================================================")
print("\n生成的文件 (共16个):")
print("  🖼️  8个PDF文件")
print("  🖼️  8个PNG文件")
print("  📝 1个总结报告")
print("\n关键发现:")
print(paste("  - 主导细胞类型:", names(mean_props_sorted)[1],
            paste0("(", round(mean_props_sorted[1]*100, 2), "%)")))
print(paste("  - 可视化成功率: 100% (修复了S4类型转换错误)"))

脚本 6 — CARD 胰腺癌 Visium HD 16µm 全流程分析

#!/usr/bin/env Rscript
# Author: LKP <kunpeng.liao@abiosciences.com>
# Date:   2026-08-18
#
# card_pancreas_16um.R
# CARD 胰腺癌Visium HD 16um分辨率空间反卷积分析
# CARD: Conditional Autoregressive Regression for Deconvolution

library(CARD)
library(Seurat)
library(ggplot2)
library(dplyr)
library(reshape2)

print("==================================================")
print("CARD 胰腺癌Visium HD 16um空间反卷积分析")
print("==================================================")

# 设置工作目录
work_dir <- "I:/TRAE skill 数据分析/untested-skills-test/spatial-16um-test"
setwd(work_dir)

# 创建结果目录
results_dir <- "card_16um_results"
if (!dir.exists(results_dir)) {
    dir.create(results_dir, recursive = TRUE)
}

# 1. 读取单细胞参考数据
print("\n【1/5】读取单细胞参考数据...")
sc_data <- readRDS("I:/TRAE skill 数据分析/测试数据/胰腺癌单细胞测序数据/scAtlas.rds")

# 提取表达矩阵和细胞类型
sc_count <- GetAssayData(sc_data, layer = "counts")
sc_meta <- data.frame(
    cellType = sc_data$Clusters,
    sampleInfo = sc_data$Name,
    row.names = colnames(sc_data)
)

print(paste("   单细胞数据:", ncol(sc_count), "cells,", nrow(sc_count), "genes"))
print(paste("   细胞类型数:", length(unique(sc_meta$cellType))))

# 2. 读取空间转录组数据(16um分辨率)
print("\n【2/5】读取空间转录组数据 (16um)...")
spatial_dir <- "I:/TRAE skill 数据分析/测试数据/空间-HD/binned_outputs/square_016um"

# 读取空间数据
spatial_data <- Load10X_Spatial(
    data.dir = spatial_dir,
    filename = "filtered_feature_bc_matrix.h5"
)

# 提取表达矩阵
spatial_count <- GetAssayData(spatial_data, layer = "counts")

# 读取空间坐标
spatial_location <- GetTissueCoordinates(spatial_data)
spatial_location <- spatial_location[, c("x", "y")]

print(paste("   空间数据:", ncol(spatial_count), "spots,", nrow(spatial_count), "genes"))
print(paste("   空间坐标:", nrow(spatial_location), "spots"))

# 采样spots(可选,加快分析)
if (ncol(spatial_count) > 5000) {
    print("   采样5000个spots...")
    set.seed(42)
    selected_spots <- sample(colnames(spatial_count), 5000)
    spatial_count <- spatial_count[, selected_spots]
    spatial_location <- spatial_location[selected_spots, ]
    print(paste("   采样后:", ncol(spatial_count), "spots"))
}

# 3. 准备CARD数据
print("\n【3/5】准备CARD数据...")

# 创建CARD对象
CARD_obj <- createCARDObject(
    sc_count = sc_count,
    sc_meta = sc_meta,
    spatial_count = spatial_count,
    spatial_location = spatial_location,
    ct.varname = "cellType",
    sample.varname = "sampleInfo",
    ct.select = unique(sc_meta$cellType)  # 选择所有细胞类型
)

print("   ✓ CARD对象创建成功")

# 4. 运行CARD反卷积
print("\n【4/5】运行CARD反卷积...")
print("   这可能需要10-20分钟,请耐心等待...")

# 运行CARD
CARD_obj <- CARD_deconvolution(CARD_obj)

print("   ✓ CARD反卷积完成")

# 5. 提取和保存结果
print("\n【5/5】提取和保存结果...")

# 提取比例矩阵
prop_df <- CARD_obj@Proportion_CARD

# 保存比例矩阵
write.csv(prop_df, file = paste0(results_dir, "/card_cell_type_proportions_16um.csv"))

# 计算统计
mean_props <- colMeans(prop_df)
mean_props_sorted <- sort(mean_props, decreasing = TRUE)

print("\n各细胞类型平均比例:")
for (ct in names(mean_props_sorted)) {
    print(paste("  -", ct, ":", round(mean_props_sorted[ct], 4),
                paste0("(", round(mean_props_sorted[ct]*100, 2), "%)")))
}

# 验证总和
total <- sum(mean_props)
print(paste("\n所有细胞类型比例总和:", round(total, 4), "(应该是1.0)"))

# 主要细胞类型
dominant_types <- colnames(prop_df)[apply(prop_df, 1, which.max)]
dominant_counts <- table(dominant_types)

print("\n各细胞类型作为主要类型的Spot数:")
for (ct in names(sort(dominant_counts, decreasing = TRUE))) {
    pct <- dominant_counts[ct] / nrow(prop_df) * 100
    print(paste("  -", ct, ":", dominant_counts[ct], "spots (", round(pct, 1), "%)"))
}

# 保存统计结果
sink(paste0(results_dir, "/card_statistics_16um.txt"))
cat("=== CARD 胰腺癌16um反卷积结果统计 ===\n\n")
cat("分析日期:", format(Sys.time(), "%Y-%m-%d %H:%M:%S"), "\n")
cat("样本: 胰腺癌Visium HD 16um\n")
cat("Spots:", nrow(prop_df), "\n")
cat("细胞类型:", ncol(prop_df), "\n\n")

cat("各细胞类型平均比例:\n")
for (ct in names(mean_props_sorted)) {
    cat(paste("  -", ct, ":", round(mean_props_sorted[ct], 4),
              paste0("(", round(mean_props_sorted[ct]*100, 2), "%)"), "\n"))
}

cat("\n各细胞类型作为主要类型的Spot数:\n")
for (ct in names(sort(dominant_counts, decreasing = TRUE))) {
    pct <- dominant_counts[ct] / nrow(prop_df) * 100
    cat(paste("  -", ct, ":", dominant_counts[ct], "spots (", round(pct, 1), "%)\n"))
}
sink()

print("\n   ✓ 统计结果已保存")

# 6. 生成可视化
print("\n生成可视化...")

# 设置图形参数
options(repr.plot.width = 14, repr.plot.height = 12)

# 1. 平均比例条形图
print("   生成平均比例条形图...")
pdf(paste0(results_dir, "/card_mean_proportions_16um.pdf"), width = 12, height = 8)
par(mar = c(5, 10, 4, 2))
barplot(mean_props_sorted, horiz = TRUE, las = 1,
        main = "CARD 16um: Mean Cell Type Proportions",
        xlab = "Mean Proportion", col = heat.colors(length(mean_props_sorted)))
dev.off()

png(paste0(results_dir, "/card_mean_proportions_16um.png"), width = 1200, height = 800, res = 150)
par(mar = c(5, 10, 4, 2))
barplot(mean_props_sorted, horiz = TRUE, las = 1,
        main = "CARD 16um: Mean Cell Type Proportions",
        xlab = "Mean Proportion", col = heat.colors(length(mean_props_sorted)))
dev.off()

# 2. 空间可视化(前4种细胞类型)
print("   生成空间可视化...")
top4_cell_types <- names(mean_props_sorted)[1:4]

for (ct in top4_cell_types) {
    # PDF
    pdf(paste0(results_dir, "/card_spatial_", gsub(" ", "_", ct), "_16um.pdf"),
        width = 10, height = 8)
    plot(CARD_obj, ct.name = ct,
         main = paste(ct, "(CARD 16um)"))
    dev.off()

    # PNG
    png(paste0(results_dir, "/card_spatial_", gsub(" ", "_", ct), "_16um.png"),
        width = 1000, height = 800, res = 150)
    plot(CARD_obj, ct.name = ct,
         main = paste(ct, "(CARD 16um)"))
    dev.off()
}

# 3. 所有细胞类型热图
print("   生成所有细胞类型热图...")
pdf(paste0(results_dir, "/card_all_cell_types_16um.pdf"), width = 20, height = 16)
plot(CARD_obj,
     main = "CARD 16um: All Cell Types",
     layout = c(5, 3))
dev.off()

png(paste0(results_dir, "/card_all_cell_types_16um.png"),
    width = 2000, height = 1600, res = 150)
plot(CARD_obj,
     main = "CARD 16um: All Cell Types",
     layout = c(5, 3))
dev.off()

# 4. 相关性热图
print("   生成相关性热图...")
corr_matrix <- cor(prop_df)

pdf(paste0(results_dir, "/card_cell_type_correlation_16um.pdf"), width = 12, height = 10)
heatmap(corr_matrix,
        main = "CARD 16um: Cell Type Correlation Matrix",
        col = colorRampPalette(c("blue", "white", "red"))(100),
        symm = TRUE)
dev.off()

png(paste0(results_dir, "/card_cell_type_correlation_16um.png"),
    width = 1200, height = 1000, res = 150)
heatmap(corr_matrix,
        main = "CARD 16um: Cell Type Correlation Matrix",
        col = colorRampPalette(c("blue", "white", "red"))(100),
        symm = TRUE)
dev.off()

print("   ✓ 可视化已保存")

# 7. 保存CARD对象
saveRDS(CARD_obj, file = paste0(results_dir, "/card_object_16um.rds"))

print("\n==================================================")
print("CARD 16um分析完成!")
print("==================================================")
print("\n输出文件:")
print("  📊 card_cell_type_proportions_16um.csv")
print("  📦 card_object_16um.rds")
print("  🖼️  card_mean_proportions_16um.png/pdf")
print("  🖼️  card_spatial_*_16um.png/pdf (4个)")
print("  🖼️  card_all_cell_types_16um.png/pdf")
print("  🖼️  card_cell_type_correlation_16um.png/pdf")
print("  📝 card_statistics_16um.txt")

脚本 7 — CARD 胰腺癌 Visium HD 16µm v2(采样版,更快)

#!/usr/bin/env Rscript
# Author: LKP <kunpeng.liao@abiosciences.com>
# Date:   2026-08-18
#
# card_pancreas_16um_v2.R
# CARD 胰腺癌Visium HD 16um分辨率空间反卷积分析 - 版本2
# 使用采样的单细胞数据以加快分析

library(CARD)
library(Seurat)
library(ggplot2)
library(dplyr)

print("==================================================")
print("CARD 胰腺癌Visium HD 16um空间反卷积分析")
print("==================================================")

# 设置工作目录
work_dir <- "I:/TRAE skill 数据分析/untested-skills-test/spatial-16um-test"
setwd(work_dir)

# 创建结果目录
results_dir <- "card_16um_results"
if (!dir.exists(results_dir)) {
    dir.create(results_dir, recursive = TRUE)
}

# 1. 读取单细胞参考数据
print("\n【1/5】读取单细胞参考数据...")
sc_data <- readRDS("I:/TRAE skill 数据分析/测试数据/胰腺癌单细胞测序数据/scAtlas.rds")

# 采样单细胞数据(加快分析)
set.seed(42)
cell_types <- sc_data$Clusters
selected_cells <- c()
for (ct in unique(cell_types)) {
    ct_cells <- colnames(sc_data)[cell_types == ct]
    if (length(ct_cells) > 1000) {
        ct_cells <- sample(ct_cells, 1000)
    }
    selected_cells <- c(selected_cells, ct_cells)
}
sc_data_subset <- sc_data[, selected_cells]

# 提取表达矩阵和细胞类型
sc_count <- GetAssayData(sc_data_subset, layer = "counts")
sc_meta <- data.frame(
    cellType = sc_data_subset$Clusters,
    sampleInfo = sc_data_subset$Name,
    row.names = colnames(sc_data_subset)
)

print(paste("   单细胞数据:", ncol(sc_count), "cells,", nrow(sc_count), "genes"))
print(paste("   细胞类型数:", length(unique(sc_meta$cellType))))

# 2. 读取空间转录组数据(16um分辨率)
print("\n【2/5】读取空间转录组数据 (16um)...")
spatial_dir <- "I:/TRAE skill 数据分析/测试数据/空间-HD/binned_outputs/square_016um"

# 读取空间数据
spatial_data <- Load10X_Spatial(
    data.dir = spatial_dir,
    filename = "filtered_feature_bc_matrix.h5"
)

# 提取表达矩阵
spatial_count <- GetAssayData(spatial_data, layer = "counts")

# 读取空间坐标
spatial_location <- GetTissueCoordinates(spatial_data)
spatial_location <- spatial_location[, c("x", "y")]

print(paste("   空间数据:", ncol(spatial_count), "spots,", nrow(spatial_count), "genes"))
print(paste("   空间坐标:", nrow(spatial_location), "spots"))

# 采样spots(加快分析)
if (ncol(spatial_count) > 2000) {
    print("   采样2000个spots...")
    set.seed(42)
    selected_spots <- sample(colnames(spatial_count), 2000)
    spatial_count <- spatial_count[, selected_spots]
    spatial_location <- spatial_location[selected_spots, ]
    print(paste("   采样后:", ncol(spatial_count), "spots"))
}

# 3. 准备CARD数据
print("\n【3/5】准备CARD数据...")

# 创建CARD对象
CARD_obj <- createCARDObject(
    sc_count = sc_count,
    sc_meta = sc_meta,
    spatial_count = spatial_count,
    spatial_location = spatial_location,
    ct.varname = "cellType",
    sample.varname = "sampleInfo",
    ct.select = unique(sc_meta$cellType)
)

print("   ✓ CARD对象创建成功")

# 4. 运行CARD反卷积
print("\n【4/5】运行CARD反卷积...")
print("   这可能需要5-10分钟,请耐心等待...")

# 运行CARD
CARD_obj <- CARD_deconvolution(CARD_obj)

print("   ✓ CARD反卷积完成")

# 5. 提取和保存结果
print("\n【5/5】提取和保存结果...")

# 提取比例矩阵
prop_df <- CARD_obj@Proportion_CARD

# 保存比例矩阵
write.csv(prop_df, file = paste0(results_dir, "/card_cell_type_proportions_16um.csv"))

# 计算统计
mean_props <- colMeans(prop_df)
mean_props_sorted <- sort(mean_props, decreasing = TRUE)

print("\n各细胞类型平均比例:")
for (ct in names(mean_props_sorted)) {
    print(paste("  -", ct, ":", round(mean_props_sorted[ct], 4),
                paste0("(", round(mean_props_sorted[ct]*100, 2), "%)")))
}

# 验证总和
total <- sum(mean_props)
print(paste("\n所有细胞类型比例总和:", round(total, 4), "(应该是1.0)"))

# 主要细胞类型
dominant_types <- colnames(prop_df)[apply(prop_df, 1, which.max)]
dominant_counts <- table(dominant_types)

print("\n各细胞类型作为主要类型的Spot数:")
for (ct in names(sort(dominant_counts, decreasing = TRUE))) {
    pct <- dominant_counts[ct] / nrow(prop_df) * 100
    print(paste("  -", ct, ":", dominant_counts[ct], "spots (", round(pct, 1), "%)"))
}

# 保存统计结果
sink(paste0(results_dir, "/card_statistics_16um.txt"))
cat("=== CARD 胰腺癌16um反卷积结果统计 ===\n\n")
cat("分析日期:", format(Sys.time(), "%Y-%m-%d %H:%M:%S"), "\n")
cat("样本: 胰腺癌Visium HD 16um\n")
cat("Spots:", nrow(prop_df), "\n")
cat("细胞类型:", ncol(prop_df), "\n\n")

cat("各细胞类型平均比例:\n")
for (ct in names(mean_props_sorted)) {
    cat(paste("  -", ct, ":", round(mean_props_sorted[ct], 4),
              paste0("(", round(mean_props_sorted[ct]*100, 2), "%)"), "\n"))
}

cat("\n各细胞类型作为主要类型的Spot数:\n")
for (ct in names(sort(dominant_counts, decreasing = TRUE))) {
    pct <- dominant_counts[ct] / nrow(prop_df) * 100
    cat(paste("  -", ct, ":", dominant_counts[ct], "spots (", round(pct, 1), "%)\n"))
}
sink()

print("\n   ✓ 统计结果已保存")

# 6. 生成可视化
print("\n生成可视化...")

# 1. 平均比例条形图
print("   生成平均比例条形图...")
pdf(paste0(results_dir, "/card_mean_proportions_16um.pdf"), width = 12, height = 8)
par(mar = c(5, 12, 4, 2))
barplot(mean_props_sorted, horiz = TRUE, las = 1,
        main = "CARD 16um: Mean Cell Type Proportions",
        xlab = "Mean Proportion", col = heat.colors(length(mean_props_sorted)))
dev.off()

png(paste0(results_dir, "/card_mean_proportions_16um.png"), width = 1200, height = 800, res = 150)
par(mar = c(5, 12, 4, 2))
barplot(mean_props_sorted, horiz = TRUE, las = 1,
        main = "CARD 16um: Mean Cell Type Proportions",
        xlab = "Mean Proportion", col = heat.colors(length(mean_props_sorted)))
dev.off()

# 2. 空间可视化(前4种细胞类型)
print("   生成空间可视化...")
top4_cell_types <- names(mean_props_sorted)[1:4]

for (ct in top4_cell_types) {
    # PDF
    pdf(paste0(results_dir, "/card_spatial_", gsub(" ", "_", ct), "_16um.pdf"),
        width = 10, height = 8)
    plot(CARD_obj, ct.name = ct,
         main = paste(ct, "(CARD 16um)"))
    dev.off()

    # PNG
    png(paste0(results_dir, "/card_spatial_", gsub(" ", "_", ct), "_16um.png"),
        width = 1000, height = 800, res = 150)
    plot(CARD_obj, ct.name = ct,
         main = paste(ct, "(CARD 16um)"))
    dev.off()
}

# 3. 所有细胞类型热图
print("   生成所有细胞类型热图...")
pdf(paste0(results_dir, "/card_all_cell_types_16um.pdf"), width = 20, height = 16)
plot(CARD_obj,
     main = "CARD 16um: All Cell Types",
     layout = c(5, 3))
dev.off()

png(paste0(results_dir, "/card_all_cell_types_16um.png"),
    width = 2000, height = 1600, res = 150)
plot(CARD_obj,
     main = "CARD 16um: All Cell Types",
     layout = c(5, 3))
dev.off()

# 4. 相关性热图
print("   生成相关性热图...")
corr_matrix <- cor(prop_df)

pdf(paste0(results_dir, "/card_cell_type_correlation_16um.pdf"), width = 12, height = 10)
heatmap(corr_matrix,
        main = "CARD 16um: Cell Type Correlation Matrix",
        col = colorRampPalette(c("blue", "white", "red"))(100),
        symm = TRUE)
dev.off()

png(paste0(results_dir, "/card_cell_type_correlation_16um.png"),
    width = 1200, height = 1000, res = 150)
heatmap(corr_matrix,
        main = "CARD 16um: Cell Type Correlation Matrix",
        col = colorRampPalette(c("blue", "white", "red"))(100),
        symm = TRUE)
dev.off()

print("   ✓ 可视化已保存")

# 7. 保存CARD对象
saveRDS(CARD_obj, file = paste0(results_dir, "/card_object_16um.rds"))

print("\n==================================================")
print("CARD 16um分析完成!")
print("==================================================")
print("\n输出文件:")
print("  📊 card_cell_type_proportions_16um.csv")
print("  📦 card_object_16um.rds")
print("  🖼️  card_mean_proportions_16um.png/pdf")
print("  🖼️  card_spatial_*_16um.png/pdf (4个)")
print("  🖼️  card_all_cell_types_16um.png/pdf")
print("  🖼️  card_cell_type_correlation_16um.png/pdf")
print("  📝 card_statistics_16um.txt")

CARD 核心 API

主函数

| 函数 | 说明 | |---|---| | createCARDObject() | 创建 CARD 对象,合并 scRNA-seq 参考和空间数据 | | CARD_deconvolution() | 运行 CARD 反卷积,返回带 Proportion_CARD 的对象 |

官方可视化函数

| 函数 | 说明 | |---|---| | CARD.visualize.pie() | 饼图(适合 < 500 spots) | | CARD.visualize.prop() | 单个/多个细胞类型空间分布 | | CARD.visualize.prop.2CT() | 两个细胞类型对比 | | CARD.visualize.Cor() | 细胞类型相关性热图 | | CARD.visualize.gene() | 基因表达空间图 | | plot(CARD_obj, ct.name=...) | 通用空间分布图 |

关键参数

| 参数 | 说明 | |---|---| | ct.varname | scRNA-seq metadata 中细胞类型列名 | | sample.varname | scRNA-seq metadata 中样本名列名 | | ct.select | 要使用的细胞类型向量 | | colors | 颜色向量(建议 14 色对照表) |


输出文件清单

数据文件

  • card_cell_type_proportions_16um.csv:比例矩阵(spots × cell types)
  • card_spatial_coordinates_16um.csv:空间坐标
  • card_object_16um.rds:完整 CARD 对象
  • CARD_result_converted.rds / .RData:转换后的 R 兼容对象

图形文件

  • card_r_pie.png/pdf:饼图
  • card_r_top4_celltypes.png/pdf:前 4 种细胞类型分布
  • card_r_all_celltypes_*.png/pdf:所有细胞类型分布
  • card_r_2celltypes_comparison.png/pdf:两细胞类型对比
  • card_r_correlation_heatmap.png/pdf:相关性热图
  • card_r_mean_proportions_barplot.png/pdf:平均比例条形图
  • card_r_dominant_celltypes.png/pdf:主导细胞类型分布
  • card_mean_proportions_16um.png/pdf:平均比例条形图
  • card_spatial_*_16um.png/pdf:前 4 种细胞类型空间分布
  • card_all_cell_types_16um.png/pdf:所有细胞类型热图
  • card_cell_type_correlation_16um.png/pdf:相关性热图
  • card_official_*:各种官方可视化

报告

  • card_statistics_16um.txt:统计报告
  • card_r_visualization_summary.txt:可视化总结

故障排除

内存不足

错误: cannot allocate vector of size ...

解决:

  1. 减少 spots(采样 2000~5000)
  2. 减少单细胞参考(每细胞类型采样 1000 个细胞)
  3. 使用 64 位 R
  4. 增加系统内存

CreateCARDObject 失败

错误: Error in CreateCARDObject

解决:

  1. 检查输入数据格式(scRNA-seq 和空间数据基因名一致)
  2. 检查细胞类型标签
  3. 确保空间坐标列名为 xy

S4 类型转换错误

错误: unable to find an inherited method for function ...

解决

  • 使用 convert_card_to_rds.R 将比例矩阵转为普通 matrix
  • 直接将 prop_matrixspatial_loc 传给可视化函数

可视化失败

错误: Error in ggplot

解决:

  1. 确保 ggplot2 已安装
  2. 使用 tryCatch() 包裹调用以隔离失败的可视化
  3. 检查数据格式(as.matrix() / as.data.frame()

性能优化

大数据集

  • 使用分块处理
  • 减少迭代次数
  • 使用并行计算

内存优化

  • 及时清理变量 rm()
  • 使用 data.table 替代 data.frame
  • 保存中间结果

推荐工作流

# 1. 提取坐标(脚本 1)
source("extract_card_coordinates.R")

# 2. 全流程反卷积(脚本 6 或 7)
source("card_pancreas_16um.R")      # 完整版
# 或
source("card_pancreas_16um_v2.R")   # 采样版,更快

# 3. 官方可视化(脚本 3 或 4)
source("card_official_visualization.R")     # 基于 RDS
# 或
source("card_official_visualization_v2.R")  # 基于 CSV

# 4. 完整可视化(脚本 5,含扩展)
source("card_complete_r_visualization.R")

如已有 Python 版结果,可跳过步骤 2,使用脚本 2 转换后直接进入步骤 4。


资源链接

  • 源码仓库github.com/YingMa0107/CARD
  • 论文:Ma Y, Zhou X. Spatially informed cell-type deconvolution for spatial transcriptomics. Nature Biotechnology, 2022.
  • 官方文档:参考 GitHub README 与 vignette

许可证

遵循 CARD 原作者的开源许可证(详见 GitHub 仓库)。

引用

@article{ma2022spatially,
  title={Spatially informed cell-type deconvolution for spatial transcriptomics},
  author={Ma, Ying and Zhou, Xiang},
  journal={Nature Biotechnology},
  year={2022},
  doi={10.1038/s41587-022-01233-1}
}