1. 列联表与ggplot2基础概念
在数据分析工作中,列联表(Contingency Table)是我们经常需要处理的一种数据结构。它本质上是一种交叉分类的频数表,用于展示两个或多个分类变量之间的关系。典型的2×2列联表包含两个分类变量,每个变量有两个水平,形成四个单元格的频数分布。
R语言中的ggplot2包是数据可视化领域的标杆工具,它基于图形语法理论构建,允许用户通过图层叠加的方式构建复杂的统计图形。虽然ggplot2没有直接提供绘制列联表的专用函数,但我们可以通过巧妙的数据处理和几何对象组合来实现专业的列联表可视化。
提示:在开始前,请确保已安装ggplot2包。如果尚未安装,可以通过命令
install.packages("ggplot2")完成安装。
1.1 列联表的数据结构特点
典型的2×2列联表数据结构如下所示:
| 变量B水平1 | 变量B水平2 | 总计 | |
|---|---|---|---|
| 变量A水平1 | n11 | n12 | n1. |
| 变量A水平2 | n21 | n22 | n2. |
| 总计 | n.1 | n.2 | n.. |
在R中,我们通常使用table()函数或xtabs()函数来创建这样的列联表。例如:
r复制# 创建示例数据
data <- data.frame(
VarA = rep(c("A1", "A2"), each = 50),
VarB = rep(c("B1", "B2"), times = 50)
)
# 生成列联表
cont_table <- table(data$VarA, data$VarB)
1.2 ggplot2可视化基本原理
ggplot2的核心思想是将图形分解为以下几个组成部分:
- 数据(Data):要可视化的数据集
- 美学映射(Aesthetics):将数据变量映射到图形属性(如x轴、y轴、颜色等)
- 几何对象(Geoms):实际绘制的图形元素(如点、线、条形等)
- 统计变换(Stats):对数据进行统计处理(如计数、汇总等)
- 坐标系(Coordinate):定义图形的坐标系统
- 分面(Facet):创建多个子图
对于列联表可视化,我们主要会用到:
geom_tile()或geom_rect():绘制矩形单元格geom_text():添加频数标签scale_fill_gradient():设置颜色渐变theme():调整图形主题和细节
2. 数据准备与预处理
2.1 创建示例数据集
让我们首先创建一个适合演示的示例数据集。假设我们研究吸烟与肺癌之间的关系:
r复制set.seed(123)
smoking_data <- data.frame(
Smoking = sample(c("Smoker", "Non-smoker"), 200, replace = TRUE, prob = c(0.4, 0.6)),
Cancer = sample(c("Yes", "No"), 200, replace = TRUE, prob = c(0.3, 0.7))
)
2.2 生成列联表
我们可以使用多种方法生成列联表:
r复制# 方法1:使用table函数
cont_table <- table(smoking_data$Smoking, smoking_data$Cancer)
# 方法2:使用xtabs函数
cont_table_xtabs <- xtabs(~ Smoking + Cancer, data = smoking_data)
# 方法3:使用dplyr和tidyr组合
library(dplyr)
library(tidyr)
cont_table_tidy <- smoking_data %>%
count(Smoking, Cancer) %>%
pivot_wider(names_from = Cancer, values_from = n)
2.3 数据格式转换
ggplot2更擅长处理"长格式"数据,因此我们需要将列联表转换为适合绘图的数据框格式:
r复制library(reshape2)
plot_data <- as.data.frame(cont_table)
colnames(plot_data) <- c("Smoking", "Cancer", "Freq")
# 或者使用tidyverse方式
plot_data_tidy <- as_tibble(cont_table) %>%
rename(Freq = n)
3. 基础列联表可视化
3.1 热图风格列联表
热图是展示列联表最直观的方式之一,通过颜色深浅表示频数大小:
r复制library(ggplot2)
ggplot(plot_data, aes(x = Cancer, y = Smoking, fill = Freq)) +
geom_tile(color = "white") +
geom_text(aes(label = Freq), color = "black", size = 6) +
scale_fill_gradient(low = "lightblue", high = "darkblue") +
labs(title = "Smoking and Lung Cancer Contingency Table",
x = "Cancer Status",
y = "Smoking Status",
fill = "Frequency") +
theme_minimal() +
theme(panel.grid = element_blank(),
axis.text = element_text(size = 12),
axis.title = element_text(size = 14),
legend.position = "right")
3.2 马赛克图实现
马赛克图是另一种展示列联表的有效方式,它通过矩形面积表示频数比例:
r复制library(ggmosaic)
ggplot(data = smoking_data) +
geom_mosaic(aes(x = product(Smoking, Cancer), fill = Smoking)) +
labs(title = "Mosaic Plot of Smoking and Cancer",
x = "Smoking Status",
y = "Cancer Status") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
注意:使用ggmosaic包前需要先安装,可以通过
install.packages("ggmosaic")安装。
3.3 条形图变体
我们也可以通过堆叠条形图或分组条形图来展示列联表数据:
r复制# 堆叠条形图
ggplot(plot_data, aes(x = Smoking, y = Freq, fill = Cancer)) +
geom_bar(stat = "identity") +
labs(title = "Stacked Bar Chart of Smoking and Cancer",
x = "Smoking Status",
y = "Frequency") +
theme_minimal()
# 分组条形图
ggplot(plot_data, aes(x = Smoking, y = Freq, fill = Cancer)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Grouped Bar Chart of Smoking and Cancer",
x = "Smoking Status",
y = "Frequency") +
theme_minimal()
4. 高级定制与统计增强
4.1 添加统计检验结果
在展示列联表时,通常需要包含卡方检验等统计检验结果:
r复制# 执行卡方检验
chi_test <- chisq.test(cont_table)
# 提取关键统计量
p_value <- format.pval(chi_test$p.value, digits = 3)
stat_label <- paste0("Chi-sq = ", round(chi_test$statistic, 2),
", df = ", chi_test$parameter,
", p = ", p_value)
# 在图形中添加统计结果
ggplot(plot_data, aes(x = Cancer, y = Smoking, fill = Freq)) +
geom_tile(color = "white") +
geom_text(aes(label = Freq), color = "black", size = 6) +
scale_fill_gradient(low = "lightblue", high = "darkblue") +
labs(title = "Smoking and Lung Cancer Contingency Table",
subtitle = stat_label,
x = "Cancer Status",
y = "Smoking Status",
fill = "Frequency") +
theme_minimal()
4.2 标准化显示
有时我们需要展示比例而非绝对频数,可以通过以下方式实现:
r复制# 计算行比例
row_prop <- prop.table(cont_table, margin = 1)
# 转换为绘图数据
plot_data_prop <- as.data.frame(row_prop)
colnames(plot_data_prop) <- c("Smoking", "Cancer", "Prop")
ggplot(plot_data_prop, aes(x = Cancer, y = Smoking, fill = Prop)) +
geom_tile(color = "white") +
geom_text(aes(label = scales::percent(Prop, accuracy = 0.1)),
color = "black", size = 6) +
scale_fill_gradient(low = "lightblue", high = "darkblue",
labels = scales::percent) +
labs(title = "Row Proportions of Smoking and Cancer",
x = "Cancer Status",
y = "Smoking Status",
fill = "Proportion") +
theme_minimal()
4.3 残差图展示
残差图可以帮助我们理解观察值与期望值之间的差异:
r复制# 计算标准化残差
residuals <- chisq.test(cont_table)$stdres
# 准备绘图数据
residual_data <- as.data.frame(residuals)
colnames(residual_data) <- c("Smoking", "Cancer", "Residual")
ggplot(residual_data, aes(x = Cancer, y = Smoking, fill = Residual)) +
geom_tile(color = "white") +
geom_text(aes(label = round(Residual, 2)), color = "black", size = 6) +
scale_fill_gradient2(low = "red", mid = "white", high = "blue",
midpoint = 0, limits = c(-3, 3)) +
labs(title = "Standardized Residuals of Smoking and Cancer",
x = "Cancer Status",
y = "Smoking Status",
fill = "Std. Residual") +
theme_minimal()
5. 实战技巧与常见问题
5.1 颜色方案选择
选择合适的颜色方案对于列联表可视化至关重要:
- 频数热图:建议使用单色渐变,如浅蓝到深蓝
- 残差图:建议使用双色渐变,红色(负)到蓝色(正),中间为白色
- 分类展示:使用明显区分的分类颜色,如红/蓝、绿/紫等
r复制# 自定义颜色方案示例
ggplot(plot_data, aes(x = Cancer, y = Smoking, fill = Freq)) +
geom_tile(color = "white") +
scale_fill_gradientn(colours = c("#f7fbff", "#6baed6", "#08306b")) +
theme_minimal()
5.2 标签位置优化
当单元格频数差异较大时,标签位置可能需要特别调整:
r复制ggplot(plot_data, aes(x = Cancer, y = Smoking, fill = Freq)) +
geom_tile(color = "white") +
geom_text(aes(label = Freq),
color = ifelse(plot_data$Freq > median(plot_data$Freq), "white", "black"),
size = 6) +
scale_fill_gradient(low = "lightblue", high = "darkblue") +
theme_minimal()
5.3 处理零频数单元格
当列联表中存在零频数单元格时,需要特别注意:
r复制# 示例数据包含零频数
zero_data <- data.frame(
Group = rep(c("A", "B", "C"), each = 2),
Outcome = rep(c("Yes", "No"), 3),
Count = c(10, 5, 8, 0, 12, 3)
)
ggplot(zero_data, aes(x = Outcome, y = Group, fill = Count)) +
geom_tile(color = "white") +
geom_text(aes(label = ifelse(Count == 0, "0", Count)),
color = "black", size = 6) +
scale_fill_gradient(low = "lightblue", high = "darkblue", na.value = "gray90") +
theme_minimal()
5.4 大型列联表处理
对于超过2×2的大型列联表,可视化时需要额外考虑:
- 使用分面(facet)来分解复杂表格
- 增加交互性(如plotly)方便探索
- 考虑使用树状图或桑基图等替代可视化
r复制# 示例:3×3列联表
large_data <- data.frame(
Var1 = rep(c("A", "B", "C"), each = 3),
Var2 = rep(c("X", "Y", "Z"), 3),
Freq = c(10, 5, 8, 7, 12, 3, 9, 6, 11)
)
ggplot(large_data, aes(x = Var2, y = Var1, fill = Freq)) +
geom_tile(color = "white") +
geom_text(aes(label = Freq), color = "black", size = 4) +
scale_fill_gradient(low = "lightblue", high = "darkblue") +
labs(title = "3×3 Contingency Table") +
theme_minimal()
6. 交互式列联表可视化
6.1 使用plotly创建交互式热图
r复制library(plotly)
plotly_plot <- ggplot(plot_data, aes(x = Cancer, y = Smoking, fill = Freq,
text = paste("Smoking:", Smoking, "<br>",
"Cancer:", Cancer, "<br>",
"Count:", Freq))) +
geom_tile(color = "white") +
scale_fill_gradient(low = "lightblue", high = "darkblue") +
theme_minimal()
ggplotly(plotly_plot, tooltip = "text")
6.2 交互式马赛克图
r复制library(ggiraph)
mosaic_plot <- ggplot(data = smoking_data) +
geom_mosaic(aes(x = product(Smoking, Cancer), fill = Smoking)) +
labs(title = "Interactive Mosaic Plot") +
theme_minimal()
girafe(ggobj = mosaic_plot)
6.3 添加交互式统计信息
r复制library(htmlwidgets)
library(DT)
# 创建统计信息表格
stats_table <- data.frame(
Test = c("Chi-squared", "Fisher's Exact", "Odds Ratio"),
Value = c(
round(chisq.test(cont_table)$statistic, 2),
round(fisher.test(cont_table)$p.value, 4),
round(fisher.test(cont_table)$estimate, 2)
)
)
# 显示交互式表格
datatable(stats_table, rownames = FALSE,
options = list(dom = 't', pageLength = 3))
7. 导出与分享可视化结果
7.1 导出高质量图片
r复制final_plot <- ggplot(plot_data, aes(x = Cancer, y = Smoking, fill = Freq)) +
geom_tile(color = "white") +
geom_text(aes(label = Freq), color = "black", size = 6) +
scale_fill_gradient(low = "lightblue", high = "darkblue") +
theme_minimal()
# 保存为PNG
ggsave("contingency_table.png", final_plot,
width = 8, height = 6, dpi = 300)
# 保存为PDF
ggsave("contingency_table.pdf", final_plot,
width = 8, height = 6, device = cairo_pdf)
7.2 嵌入R Markdown报告
在R Markdown文档中使用:
markdown复制```{r contingency-table, fig.width=8, fig.height=6}
ggplot(plot_data, aes(x = Cancer, y = Smoking, fill = Freq)) +
geom_tile(color = "white") +
geom_text(aes(label = Freq), color = "black", size = 6) +
scale_fill_gradient(low = "lightblue", high = "darkblue") +
theme_minimal()
```
7.3 创建可重复使用的函数
为了提高效率,可以创建自定义函数来生成列联表可视化:
r复制plot_contingency <- function(data, var1, var2,
title = "Contingency Table",
xlab = deparse(substitute(var2)),
ylab = deparse(substitute(var1))) {
# 创建列联表数据
plot_data <- as.data.frame(table(data[[var1]], data[[var2]]))
colnames(plot_data) <- c("Var1", "Var2", "Freq")
# 绘制图形
ggplot(plot_data, aes(x = Var2, y = Var1, fill = Freq)) +
geom_tile(color = "white") +
geom_text(aes(label = Freq), color = "black", size = 6) +
scale_fill_gradient(low = "lightblue", high = "darkblue") +
labs(title = title, x = xlab, y = ylab) +
theme_minimal()
}
# 使用示例
plot_contingency(smoking_data, "Smoking", "Cancer",
title = "Smoking and Cancer Association")
在实际项目中,我发现将常用的可视化模式封装成函数可以显著提高工作效率,特别是在需要创建多个类似图表时。这个自定义函数可以根据需要进一步扩展,比如添加统计检验结果、调整颜色方案等参数。
