如何为geom_hex绘制的单细胞UMAP六边形分配特定颜色?
单细胞UMAP六边形分箱按簇比例混色的问题解决
问题根源拆解
- 颜色对应错误:
geom_hex默认会对输入数据做分箱统计(统计每个箱内的细胞数),此时直接传递原细胞级的颜色向量,会被ggplot按分箱后的统计逻辑重新映射,而非直接对应每个六边形。 - 六边形大小异常:如果在
geom_hex中绑定原细胞的cell或簇字段作为分组/映射变量,会强制ggplot为每个细胞单独生成六边形,导致每个六边形尺寸极小,完全偏离预期的分箱效果。
正确实现步骤与代码
核心思路:先对UMAP坐标做六边形分箱,统计每个箱内各细胞簇的数量比例,计算出混合色后,再基于分箱后的汇总数据绘制统一大小的六边形。
1. 依赖包加载
library(ggplot2) library(hexbin) library(dplyr) library(tidyr)
2. 数据准备(示例)
假设你的单细胞数据框sc_data包含UMAP_1、UMAP_2(UMAP坐标)和cluster(细胞簇标签),先定义各簇的对应颜色:
# 自定义簇颜色(替换为你的实际颜色映射) cluster_colors <- c( "Cluster0" = "#E41A1C", "Cluster1" = "#377EB8", "Cluster2" = "#4DAF4A", "Cluster3" = "#984EA3" )
3. 分箱统计与混合色计算
# 第一步:生成六边形分箱,获取每个箱的坐标与基础计数 xbins <- 30 # 分箱数量,可根据数据密度调整 hb <- hexbin(sc_data$UMAP_1, sc_data$UMAP_2, xbins = xbins) hex_df <- data.frame(hcell2xy(hb), hex_id = seq_len(nrow(hcell2xy(hb))), count = hb@count) # 第二步:统计每个六边形内各簇的细胞数量 cluster_hex_counts <- sc_data %>% # 为每个细胞分配所属的六边形ID mutate(hex_id = hexbin(UMAP_1, UMAP_2, xbins = xbins, IDs = TRUE)) %>% group_by(hex_id, cluster) %>% summarise(n = n(), .groups = "drop") %>% # 转换为宽格式,方便后续计算 pivot_wider(names_from = cluster, values_from = n, values_fill = 0) # 第三步:计算每个六边形的混合颜色 hex_df <- hex_df %>% left_join(cluster_hex_counts, by = "hex_id") %>% rowwise() %>% mutate( # 提取当前箱内所有簇的细胞数 cluster_ns = c_across(all_of(names(cluster_colors))), total_cells = sum(cluster_ns), # 计算加权混合RGB颜色 fill_color = if (total_cells == 0) { "#F0F0F0" # 无细胞的箱设为浅灰色 } else { # 提取各簇颜色的RGB值 cluster_rgb_list <- lapply(names(cluster_ns), function(c) col2rgb(cluster_colors[c])) # 按细胞数加权求和 mixed_rgb <- Reduce("+", mapply(function(rgb, n) rgb * n, cluster_rgb_list, cluster_ns, SIMPLIFY = FALSE)) / total_cells # 转换为十六进制颜色 rgb(mixed_rgb[1], mixed_rgb[2], mixed_rgb[3], maxColorValue = 255) } ) %>% ungroup()
4. 绘制正确的六边形图
使用geom_hex配合stat = "identity",直接基于分箱后的汇总数据绘制,避免触发默认的分箱统计逻辑:
ggplot() + geom_hex( data = hex_df, aes(x = x, y = y, fill = fill_color), stat = "identity", # 设置六边形宽度,与分箱数量匹配 binwidth = c(diff(range(sc_data$UMAP_1))/xbins, diff(range(sc_data$UMAP_2))/xbins) ) + scale_fill_identity() + theme_bw() + labs(x = "UMAP_1", y = "UMAP_2", title = "Hex-binned UMAP with cluster proportion colors")
或者使用hexbin包的hexPolygons函数绘制更精准的六边形:
hex_polys <- hexPolygons(hb, hex_df$fill_color) ggplot() + geom_polygon(data = hex_polys, aes(x = x, y = y, group = id, fill = fill)) + scale_fill_identity() + theme_bw() + labs(x = "UMAP_1", y = "UMAP_2")
关键注意事项
- 绝对不要在
geom_hex中引入原细胞级的字段(如cell、cluster)作为映射变量,否则会破坏分箱逻辑,导致大小异常。 - 混合色的计算必须基于分箱后的簇数量比例,而非原细胞的颜色,才能保证每个六边形的颜色准确反映箱内细胞组成。
xbins参数控制分箱密度,可根据你的UMAP图的细胞密度调整,数值越大分箱越细。
内容的提问来源于stack exchange,提问作者YumTum
相关产品推荐
相关产品推荐

