You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

R语言镜像条形图优化:差异标签居中与%H:%M格式显示

镜像条形图优化方案

一、核心优化方向及实现

1. 大幅简化代码

通过统一数据预处理逻辑,避免重复编写分类型的绘图代码,同时优化排序逻辑:

  • 给tv类型数值添加负号,实现上下镜像布局,无需分别绘制正负轴图层
  • 将月份转换为反转因子,直接实现最早月份置顶,移除冗余的rev()调用
  • 转长格式数据,用单个geom层绘制所有用户条形

2. 差异标签精准居中

重新计算标签位置,确保标签位于两个条形的水平中点:

  • 标签位置公式:(user1_scaled + user2_scaled)/2/60,其中user1_scaled和user2_scaled已处理正负值
  • 移除手动调整vjust和position_dodge的逻辑,直接用计算好的位置实现居中

3. 分钟转%H:%M格式

自定义转换函数,统一处理轴刻度和差异标签的格式,支持超过24小时的时长显示:

min_to_hhmm <- function(minutes) {
  hours <- floor(minutes / 60)
  mins <- minutes %% 60
  sprintf("%02d:%02d", hours, mins)
}

二、完整优化代码

set.seed(123)
library(tidyverse)
library(ggplot2)
library(zoo)
library(ggnewscale)

# 自定义分钟转HH:MM格式函数
min_to_hhmm <- function(minutes) {
  hours <- floor(minutes / 60)
  mins <- minutes %% 60
  sprintf("%02d:%02d", hours, mins)
}

# 原始数据
dat <- tibble(
  user = rep(c("user1", "user2"), each = 6),
  type = rep(c("tv", "movie"), each = 6),
  yearmonth = as.yearmon(rep(seq.Date(as.Date("2024-12-01"), as.Date("2025-02-01"), "month"), 4)),
  cumulative_minutes = sample(200:900, 12, replace = TRUE)
)

# 统一数据预处理
processed_dat <- dat %>%
  pivot_wider(names_from = user, values_from = cumulative_minutes) %>%
  mutate(
    # 给tv类型数值加负号,实现镜像
    value_multiplier = ifelse(type == "tv", -1, 1),
    user1_scaled = user1 * value_multiplier,
    user2_scaled = user2 * value_multiplier,
    diff = user1 - user2,
    # 计算标签居中位置
    label_pos = (user1_scaled + user2_scaled) / 2 / 60,
    # 月份反转因子,实现最早月份置顶
    yearmonth_fct = fct_rev(factor(yearmonth))
  ) %>%
  # 转长格式,方便统一绘图
  pivot_longer(cols = c(user1_scaled, user2_scaled), names_to = "user", values_to = "scaled_minutes") %>%
  mutate(user = str_remove(user, "_scaled"))

# 绘图
ggplot(processed_dat, aes(x = yearmonth_fct)) +
  # 差异背景条
  geom_col(
    data = processed_dat %>% distinct(type, yearmonth_fct, user1, user2, value_multiplier),
    aes(y = max(user1, user2) * value_multiplier / 60, 
        fill = ifelse(user1 > user2, "#C7E9C0", "#FCBBA1")),
    alpha = 0.7
  ) +
  # 用户条形
  geom_col(aes(y = scaled_minutes / 60, fill = user), position = position_dodge(width = 0.8), width = 0.7) +
  # 差异标签(HH:MM格式)
  geom_text(
    data = processed_dat %>% distinct(type, yearmonth_fct, diff, label_pos),
    aes(y = label_pos, label = min_to_hhmm(abs(diff))),
    size = 3, color = "black"
  ) +
  # Movies填充色设置
  scale_fill_manual(
    values = c("user1" = "#1B9E77", "user2" = "#D95F02", "#C7E9C0" = "#C7E9C0", "#FCBBA1" = "#FCBBA1"),
    breaks = c("user1", "user2"),
    guide = guide_legend("Movies")
  ) +
  new_scale_fill() +
  # TV填充色设置
  scale_fill_manual(
    values = c("user1" = "#E41A1C", "user2" = "#377EB8", "#C7E9C0" = "#C7E9C0", "#FCBBA1" = "#FCBBA1"),
    breaks = c("user1", "user2"),
    guide = guide_legend("TV")
  ) +
  # Y轴刻度转为HH:MM格式
  scale_y_continuous(
    labels = function(y) min_to_hhmm(abs(y)*60),
    name = "时长"
  ) +
  # X轴标签格式化
  scale_x_discrete(labels = function(x) as.yearmon(x)) +
  labs(x = "月份") +
  theme_light() +
  coord_flip()

内容的提问来源于stack exchange,提问作者devster

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 07:17:09