自定义ggplot2 geom:子绘图配色规则、颜色统一及图例添加
我明白你正在用ggplot2自定义一个geom来实现绘图中的迷你条形图小部件,用来展示不同年份气缸数车型的占比,这个需求挺实用的!针对你提出的四个技术疑问,我结合ggplot2的底层逻辑和你的代码来逐一解答:
问题1:ggplot2如何为条形选择颜色?未定义颜色的情况下,为何调用coord$transform后颜色自动生成?
ggplot2的美学映射(aes)系统会自动处理离散变量的颜色分配:当你在aes()里把fill映射到一个离散变量(比如你代码里的factor(n)),ggplot2会默认使用内置的离散色板(比如scale_fill_hue())来给每个不同的离散值分配颜色。
而coord$transform()的作用是把原始数据坐标系的点转换为面板绘图坐标系的点,这个过程不仅处理x/y坐标,也会处理所有绑定到数据的美学属性(包括fill颜色)——也就是说,在调用coord$transform()之前,ggplot2已经根据你的fill映射生成了对应的颜色值,coord$transform()只是把这些颜色值和转换后的坐标一起传递给绘图函数,所以你会看到颜色自动生成了。
问题2:相同气缸数在绘图不同位置的颜色为何不一致?
看你的代码里,geom_bar_widget的aes中写的是fill=factor(n)——这里的n是每个年份下对应气缸数的车型数量,而不是气缸数cyl本身!
举个例子:1999年4缸车型的数量n可能是10,2008年4缸车型的数量n可能是15,这两个factor(n)的取值不同,所以ggplot2会分配不同的颜色。这就是为什么相同气缸数在不同年份颜色不一致的核心原因——你把fill映射到了错误的变量上!
问题3:如何实现各迷你条形图间的颜色匹配?
解决方法很直接:把fill的映射对象从n改成cyl(气缸数),这样同一个气缸数在所有年份都会被分配到同一个颜色。
修改你的geom_bar_widget调用代码:
geom_bar_widget(data=bar.data, aes(x=x, y=y, width=.2, height=.15, group=year, value=n, fill=factor(cyl)))
这样,无论某个气缸数在不同年份的车型数量是多少,它的颜色都会保持一致,实现跨迷你条形图的颜色匹配。
问题4:如何为条形添加图例?
你推测的方向是对的,图例的生成需要正确的美学映射和geom的配置,具体需要做这几步修改:
- 修正fill映射:如问题3所说,把fill映射到
factor(cyl),这样有明确的离散分类作为图例的项。 - 开启图例显示:在
geom_bar_widget()里把show.legend设为TRUE(你之前设的是FALSE,所以图例被隐藏了)。 - 修改draw_key:你的自定义Geom里
draw_key=draw_key_blank(),这会导致图例项不显示,改成draw_key=draw_key_rect(),这样图例里会显示对应颜色的矩形块。 - 调整ggproto的默认美学:确保
default_aes里的fill不是NULL,可以设置一个默认值,但更重要的是让映射的fill生效。
修改后的自定义Geom代码片段:
GeomBarWidget <- ggproto("GeomBarWidget", Geom, required_aes=c("x", "y", "width", "height", "group", "value"), default_aes=aes(shape=19, colour="black", fill=NA, labels=NULL), draw_key=draw_key_rect(), # 改成矩形图例键 draw_group=.bar_widget_draw_group, extra_params=c("na.rm") ) # 调用geom时开启图例 geom_bar_widget(data=bar.data, aes(x=x, y=y, width=.2, height=.15, group=year, value=n, fill=factor(cyl)), show.legend=TRUE)
另外,如果你想自定义图例的标题或者颜色,可以添加scale_fill_discrete(name="气缸数")这样的颜色标度函数来调整。
内容的提问来源于stack exchange,提问作者January

