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

如何在ggplot2中绘制带指定颜色的连续渐变色轮

实现带渐变切片的12色钟表式色轮(认知科学实验用)

目标是复刻一个用颜色替代数字的钟表样式渐变色轮,每个切片中心保留纯色,向两侧分别渐变为相邻切片的颜色,预期效果如下:
Desired output

已完成的离散色轮代码

我已经成功创建了离散版色轮,代码如下:

library(ggplot2)

data <- data.frame(
  number=c(1:12),
  count=rep(10, 12),
  cs=c('#FE2712', '#FC600A', '#FB9902', '#FCCC1A', '#FEFE33', '#B2D732', 
       '#66B032', '#347C98', '#0247FE', '#4424D6', '#8601AF', '#C21460'))

data$fraction = data$count / sum(data$count)

# 计算每个切片的顶部累积百分比
data$ymax = cumsum(data$fraction)

# 计算每个切片的底部位置
data$ymin = c(0, head(data$ymax, n=-1))

# 15度转换为弧度
deg15 <- 0.26179999

(basicwheel <- 
  ggplot(data, aes(ymax=ymax, ymin=ymin, xmax=1, xmin=0, fill= as.factor(number), color='black')) +
  geom_rect(inherit.aes = T) +
  coord_polar(theta="y", start=deg15) +
  xlim(c(0, 1))+
  scale_color_manual(values = 'black')+
  scale_fill_manual(values= cs)+
  theme_void()+
  theme(legend.position = '')
)

Discrete color wheel

尝试实现切片内渐变的过程

我尝试让每个切片中心保留纯色,向一侧渐变为前一个切片的颜色,另一侧渐变为后一个切片的颜色,首先创建了包含自身、前后切片颜色的矩阵:

colmat <- data.frame(number = numeric(length(unique(data$number))),
                     ymax = numeric(length(unique(data$number))),
                     ymin = numeric(length(unique(data$number))),
                     previous_color = numeric(length(unique(data$number))),
                     next_color = numeric(length(unique(data$number))),
                     own_color = numeric(length(unique(data$number))))

for (i in 1:length(unique(data$number))) {
  d <- data %>% subset(as.numeric(number) == i)
  iminusone <- as.numeric(i) - 1
  iplusone <- as.numeric(i) + 1
  previousnr <- ifelse(iminusone > 0, data[which(as.numeric(data$number) == iminusone),]$number, 12)
  nxtnr <- ifelse(iplusone < 13, data[which(as.numeric(data$number) == iplusone),]$number, 1)
  colmat[i, 'number'] <- i
  
  previous_color <- colmat[i, 'previous_color'] <- data[which(as.numeric(data$number)  == previousnr),]$cs
  next_color <- colmat[i, 'next_color'] <- data[which(as.numeric(data$number) == nxtnr),]$cs
  owncolor <- colmat[i, 'own_color'] <- data[which(as.numeric(data$number) == i),]$cs
  ymax <- colmat[i, 'ymax'] <- data[which(as.numeric(data$number) == i),]$ymax
  ymin <- colmat[i, 'ymin'] <- data[which(as.numeric(data$number) == i),]$ymin
  
}

head(colmat)

但后续代码无法实现预期渐变效果:

(gwheel <- 
    ggplot(colmat, aes(ymax=ymax, ymin=ymin, xmax=1, xmin=0, fill=number)) +
    geom_rect() +
    coord_polar(theta="y", start=deg15) +
    xlim(c(0, 1))+
    scale_fill_gradient2(high = colmat$previous_color, mid = colmat$own_color, low = colmat$next_color)+
    theme_void()+
    theme(legend.position = '')
)

Failed gradient attempt

解决方案:拆分切片为渐变条带

scale_fill_gradient2无法为每个切片单独设置渐变规则,正确的做法是将每个30度的切片拆分成多个小分段,通过分段设置过渡色来实现双向渐变:

步骤1:准备基础数据与依赖包

library(ggplot2)
library(dplyr)
library(colorspace)

# 原始颜色数据
data <- data.frame(
  number = 1:12,
  cs = c('#FE2712', '#FC600A', '#FB9902', '#FCCC1A', '#FEFE33', '#B2D732', 
         '#66B032', '#347C98', '#0247FE', '#4424D6', '#8601AF', '#C21460')
)

# 补充前后切片的颜色(循环处理首尾)
data <- data %>%
  mutate(
    prev_cs = lag(cs, default = last(cs)),
    next_cs = lead(cs, default = first(cs))
  )

步骤2:拆分切片为渐变分段

将每个切片拆分为20个小分段,前10段从左侧相邻色过渡到当前切片纯色,后10段从当前纯色过渡到右侧相邻色:

n_segments <- 20  # 每个切片拆分的分段数
half_seg <- n_segments %/% 2
slice_angle <- 2 * pi / 12  # 每个切片的弧度范围

# 生成渐变分段数据
grad_data <- pmap_dfr(data, function(number, cs, prev_cs, next_cs) {
  # 计算当前切片的起始/结束弧度(包含15度偏移)
  start_angle <- (number - 1) * slice_angle + 0.26179999
  end_angle <- start_angle + slice_angle
  
  # 生成左侧渐变段:从相邻色到当前色
  prev_to_own <- data.frame(
    number = number,
    angle = seq(start_angle, start_angle + slice_angle/2, length.out = half_seg),
    color = hex(colorspace::colorRamp(c(prev_cs, cs))(seq(0, 1, length.out = half_seg)))
  )
  
  # 生成右侧渐变段:从当前色到相邻色
  own_to_next <- data.frame(
    number = number,
    angle = seq(start_angle + slice_angle/2, end_angle, length.out = half_seg),
    color = hex(colorspace::colorRamp(c(cs, next_cs))(seq(0, 1, length.out = half_seg)))
  )
  
  bind_rows(prev_to_own, own_to_next)
})

# 转换为极坐标所需的ymin/ymax
grad_data <- grad_data %>%
  arrange(number, angle) %>%
  group_by(number) %>%
  mutate(
    ymin = angle - slice_angle/n_segments,
    ymax = angle
  ) %>%
  ungroup()

步骤3:绘制渐变色轮

ggplot(grad_data, aes(ymin = ymin, ymax = ymax, xmin = 0, xmax = 1, fill = color)) +
  geom_rect(color = "black") +
  coord_polar(theta = "y", start = 0) +
  scale_fill_identity() +
  xlim(c(0, 1)) +
  theme_void() +
  theme(legend.position = "none")

该方法通过拆分切片为多个过渡色小条带,完美实现了每个切片内从相邻色到自身色再到下一个相邻色的平滑渐变,符合预期效果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 15:47:34