如何在ggplot2中可视化营收与利润随时间的变化关系?
针对你的需求,这里有几个实用的可视化方案,既能清晰展示营收与利润随时间的变化趋势,又能直观体现两者的关联或背离关系:
先把原始数据整理成ggplot偏好的格式:
library(tidyverse) # 构建数据集 df <- tibble( quarter = c("2020_Q1", "2020_Q2", "2020_Q3", "2020_Q4", "2021_Q1", "2021_Q2", "2021_Q3", "2021_Q4", "2022_Q1", "2022_Q2", "2022_Q3"), revenue = c(23700, 24400, 24000, 23300, 23800, 23600, 23100, 21800, 21600, 20700, 20100), profit = c(1426, 1447, 1245, 1599, 896, 813, 706, 510, 649, 789, 947) ) %>% # 转成长格式(部分方案需要) pivot_longer(cols = c(revenue, profit), names_to = "metric", values_to = "value")
1. 正确配置双Y轴(sec.axis 可正常生效)
你之前用sec.axis没生效,大概率是没对利润做缩放转换——因为营收和利润量级差太大,直接用次轴会导致利润线几乎贴在底部。解决方法是先把利润按比例缩放至营收的量级范围,再通过次轴反向转换显示原始利润值:
# 计算缩放系数:用营收最大值除以利润最大值 scale_factor <- max(df$revenue) / max(df$profit) ggplot(df %>% filter(metric == "revenue"), aes(x = quarter, y = value, group = 1)) + geom_line(color = "#2c3e50", size = 1.2) + # 添加缩放后的利润线 geom_line(data = df %>% filter(metric == "profit"), aes(y = value * scale_factor), color = "#e74c3c", size = 1.2) + # 设置主Y轴(营收) scale_y_continuous( name = "营收", # 设置次Y轴,反向缩放显示原始利润值 sec.axis = sec_axis(~ . / scale_factor, name = "利润") ) + labs(x = "季度") + theme_minimal() + theme(axis.text.x = element_text(angle = 45, hjust = 1))
优势:在同一图中同时展示两个指标的绝对值趋势,适合需要对比具体数值的场景;注意:双Y轴容易误导读者,需确保缩放逻辑清晰,标注明确。
2. 分面独立展示
把营收和利润分成上下两个子图,共享X轴,既能清晰看到各自趋势,又能直观对比两者的变化节奏:
ggplot(df, aes(x = quarter, y = value, group = 1)) + geom_line(color = "#2c3e50", size = 1.2) + facet_wrap(~ metric, ncol = 1, scales = "free_y") + labs(x = "季度", y = "") + theme_minimal() + theme(axis.text.x = element_text(angle = 45, hjust = 1))
优势:完全避免双Y轴的误导性,每个指标的刻度独立,趋势展示更准确;适合侧重观察各自变化细节的场景。
3. 折线+柱状组合图
用折线展示营收,柱状展示利润(反之亦可),通过图形类型区分两个指标,无需次轴也能在同一图中对比:
# 转回宽格式数据做组合图 df_wide <- df %>% pivot_wider(names_from = metric, values_from = value) ggplot(df_wide, aes(x = quarter)) + geom_line(aes(y = revenue, group = 1), color = "#2c3e50", size = 1.2) + geom_col(aes(y = profit * scale_factor), fill = "#e74c3c", alpha = 0.6) + scale_y_continuous( name = "营收", sec.axis = sec_axis(~ . / scale_factor, name = "利润") ) + labs(x = "季度") + theme_minimal() + theme(axis.text.x = element_text(angle = 45, hjust = 1))
优势:图形类型的差异比颜色更直观,能快速区分两个指标;适合需要同时看绝对值和趋势的场景。
4. 标准化后同轴展示
将营收和利润都标准化到[0,1]范围,这样可以在同一Y轴上展示两者的相对变化趋势,重点看是否同步或背离:
df_normalized <- df %>% group_by(metric) %>% mutate(norm_value = (value - min(value)) / (max(value) - min(value))) %>% ungroup() ggplot(df_normalized, aes(x = quarter, y = norm_value, color = metric, group = metric)) + geom_line(size = 1.2) + labs(x = "季度", y = "标准化值(0-1)", color = "指标") + theme_minimal() + theme(axis.text.x = element_text(angle = 45, hjust = 1))
优势:忽略绝对值差异,聚焦趋势的同步性——比如营收持续下降,但利润在2022年开始回升,这个背离会非常明显;适合分析两者变化节奏的关联。
5. 散点图+趋势线(聚焦关联关系)
如果核心需求是看营收与利润的关联程度,可以用散点图展示每个季度的营收-利润对应关系,再添加趋势线看整体相关性:
ggplot(df_wide, aes(x = revenue, y = profit)) + geom_point(size = 3, color = "#2c3e50") + # 添加每个点的季度标签 geom_text(aes(label = quarter), hjust = 1.2, vjust = 0.5) + # 拟合线性趋势线 geom_smooth(method = "lm", se = FALSE, color = "#e74c3c") + labs(x = "营收", y = "利润") + theme_minimal()
优势:直接展示两个指标的相关性,同时通过标签看到时间维度的变化;适合分析“营收变化如何影响利润”的场景。
内容的提问来源于stack exchange,提问作者Florian

