如何用R ggplot2或其他工具绘制按行着色的堆叠直方图式条码图
解决方案:保持条码图布局并按行着色(R + Python)
我来帮你搞定这个需求——既要保留plot(test.matrix)那种“条码图”的布局(柱宽对应行总和,柱高对应行内列差异),又要按指定颜色向量给每行着色。下面分别提供R(用ggplot2)和Python的实现方案:
R 实现(ggplot2)
首先我们需要把表格数据转换成适合ggplot2处理的格式,同时计算出控制柱宽的行总和、控制柱高的列差异,再用矩形来模拟原生plot的布局:
# 测试数据初始化 test.matrix <- matrix(c(70, 120, 65, 140, 13, 68, 46, 294, 52, 410), ncol=2, byrow=TRUE) rownames(test.matrix) <- c("BC.1", "BC.2", "GC", "MO", "EB") colnames(test.matrix) <- c("12m","3m") test.matrix <- as.table(test.matrix) # 自定义颜色向量 color.ct <- c("gold","yellowgreen","navy","royalblue","orangered") names(color.ct) <- rownames(test.matrix) # 转换为数据框并计算关键参数 df <- as.data.frame(test.matrix) # 计算每行总和(柱宽) df$row_sum <- ave(df$Freq, df$Var1, FUN = sum) # 计算行内列差异(柱高,这里用12m - 3m,要绝对值的话改成abs(x[1]-x[2])) df$diff <- ave(df$Freq, df$Var1, FUN = function(x) x[1] - x[2]) # 保留每个行的唯一记录(因为同一行的sum和diff是重复的) df_unique <- df[!duplicated(df$Var1), ] # 计算每个条形的x轴起止位置(累积宽度定位) df_unique$x_start <- c(0, cumsum(df_unique$row_sum)[-nrow(df_unique)]) df_unique$x_end <- df_unique$x_start + df_unique$row_sum # 计算y轴起止位置(以0为基准,差异为高度) df_unique$y_bottom <- pmin(0, df_unique$diff) df_unique$y_top <- pmax(0, df_unique$diff) # 用ggplot2绘制 library(ggplot2) ggplot(df_unique) + geom_rect(aes(xmin = x_start, xmax = x_end, ymin = y_bottom, ymax = y_top, fill = Var1)) + scale_fill_manual(values = color.ct) + labs(x = "行总和", y = "12m - 3m 差异", fill = "类别") + theme_minimal() + theme(axis.text.x = element_text(angle = 45, hjust = 1))
这段代码会生成和原生plot(test.matrix)布局一致的图形,同时每个行的条形会使用你指定的颜色。如果需要柱高是差异的绝对值,只需要修改diff的计算逻辑即可。
Python 实现(matplotlib)
在Python中,我们可以直接用matplotlib绘制矩形来实现相同的效果,步骤和R类似:
import matplotlib.pyplot as plt import numpy as np # 测试数据初始化 test_matrix = np.array([[70, 120], [65, 140], [13, 68], [46, 294], [52, 410]]) rownames = ["BC.1", "BC.2", "GC", "MO", "EB"] colnames = ["12m", "3m"] # 自定义颜色向量 color_ct = ["gold", "yellowgreen", "navy", "royalblue", "orangered"] color_dict = dict(zip(rownames, color_ct)) # 计算关键参数:行总和(柱宽)、列差异(柱高) row_sums = test_matrix.sum(axis=1) diffs = test_matrix[:, 0] - test_matrix[:, 1] # 要绝对值的话用np.abs() # 计算每个矩形的x轴起止位置 x_starts = np.cumsum(row_sums) - row_sums x_ends = x_starts + row_sums # 创建绘图对象 fig, ax = plt.subplots(figsize=(10, 6)) # 逐个绘制每个行对应的条形(矩形) for idx in range(len(rownames)): x_start = x_starts[idx] x_end = x_ends[idx] y_bottom = min(0, diffs[idx]) y_top = max(0, diffs[idx]) # 添加矩形到画布 rect = plt.Rectangle((x_start, y_bottom), width=x_end - x_start, height=y_top - y_bottom, color=color_dict[rownames[idx]]) ax.add_patch(rect) # 设置坐标轴范围和标签 ax.set_xlim(0, x_ends[-1]) ax.set_ylim(min(diffs) - 10, max(diffs) + 10) # 留一点边距让图形更美观 ax.set_xlabel("行总和") ax.set_ylabel("12m - 3m 差异") # 添加图例 from matplotlib.patches import Patch legend_elements = [Patch(facecolor=color_ct[i], label=rownames[i]) for i in range(len(rownames))] ax.legend(handles=legend_elements, loc="upper right") # 调整布局并显示 plt.tight_layout() plt.show()
这个Python版本同样完美匹配你的需求,你可以根据实际需要调整柱高的计算逻辑(比如用绝对值)。
内容的提问来源于stack exchange,提问作者fuchsdeluxe




