赞
踩
自新冠疫情在全球扩散以后,人民日报有段时间每天都会在微信公众号更新全球疫情形势,用南丁格尔玫瑰图(点击查看人民日报原图链接)来统计各国确诊和死亡病例。此后国内似乎突然开始喜欢上了用玫瑰图来展示数据,也出现了很多仿制人民日报的玫瑰图的教程。前几天刷微博的时候偶然看到网易数读在8月23日发布的一条“人类社畜图鉴”统计对社畜最友好的城市,列举了各个城市之间薪酬、加班时长以及生活方面的各类对比,其中关于中国一线和新一线城市居住自由指数的那张玫瑰图引起了我的注意。
仔细来看的话,这张玫瑰图比之前的人民日报的玫瑰图多了两个不连续的同心圆,把玫瑰包在了中间。之前关于人民日报的玫瑰图的教程已经很多了,今天我就来仿制一下这个难度稍微更大一点的。
玫瑰图是通过改变柱形图的横坐标实现的,所以绘制玫瑰图我们首先需要绘制出对应的柱形图。绘图的工具包依旧是ggplot2以及ggimage扩展包。再次观察网易的这张玫瑰图,我们依照ggplot2的语法特点,将原图划分为不同的结构层次依次绘制,然后将其叠加在一起。首先是玫瑰图的最底层,即主柱形图下面的那层浅灰色的底图,我们需要先绘制这层底图,然后再叠加主柱形图以及外围的不连续的两个同心圆。外围的两个同心圆的绘制思路是,在柱形图的上方绘制一排等间距的长方形,通过坐标轴转换将柱形图变成玫瑰图以后,这一排长方形也会随着坐标轴闭合变成同心圆。我有试过其他方法,但是效果都没有这个好。最后圆心部分的图标,我们使用ggimage扩展包将从网上找到的类似logo添加进去。需要注意的是柱形图顶部的文字标签,其中佛山、郑州、武汉、合肥、广州、南京、杭州以及深圳都是分成两行显示的,所以在输入数据的时候要注意一下。同时因为涉及中文字符,所以在开始前最好先设定R的语言环境为中文。第一步我们来手动输入数据表格,原图总共有19个柱子,所以我设置x坐标轴1到19,观察原图可以发现底图的柱子高度约为60,所以设置y为60,变量y1为各个城市对应的数据:
### 加载ggplot2和ggimagelibrary(tidyverse)library(ggimage)### 设定R语言环境Sys.setlocale(category = "LC_ALL", locale = "Chinese")### 输入表格g 1: y = 60, y1 = c(95.70, 90.30, 86.60, 85.10, 82.10, 80.20, 78.10, 76.40, 75.60, 75.60, 73.70, 73.10, 71.50, 62.60, 62.40, 49.36, 22.48, 12.11, 8.09), ### 使用正则表达式"\n"对文字分行 place = c("长沙", "沈阳", "重庆", "佛\n山", "郑\n州", "武\n汉", "合\n肥", "西安", "青岛", "成都", "苏州", "东莞", "天津", "广\n州", "南\n京", "杭\n州", "上海", "北京", "深\n圳"))
下面我们绘制浅灰色的柱形底图,注意这里我把横坐标轴从数字转换成了分类变量,因为我在输入数据的时候输入的是数字,如果不进行转换,那么R会将其识别为连续变量,那么画出来的就不是柱形图 (bar chart),而是直方图 (histogram),所以这一步转换是不能省掉的,或者我们可以在之前输入数据的时候就将其设定为文本。我通过颜色拾取器找到的和原图近似的颜色,和原图有色差,对颜色敏感的朋友可以试一下找到更合适的颜色,下面是使用hex编码输入颜色的底图代码:
### 绘制底图ggplot(data = g, aes(x = as.factor(x))) + ### 浅灰色的hex编码为 #e0dee0 geom_col(aes(y = y), fill = "#e0dee0", color = "#e0dee0")
下一步我们需要绘制同心圆的部分,开篇的绘图思路中已经提到,我是在柱形图上方添加一排等间距排列的长方形来实现同心圆的。原图有两个同心圆,所以我需要添加两排。观察原图,里面的那个同心圆在纵坐标轴上的高度约为90,外围的同心圆的距离可以设置较远一点,我设置为120左右。下面我们使用geom_rect()命令符添加这两排长方形,其中四个参数设定了每个长方形的长和宽,长设定为0.9,基本和柱形图的宽度一样。如果设定为1,那么长方形就会连在一起,我们得到的同心圆之间就没有等距间隔了。同心圆的颜色与柱形图中最浅的蓝色相同,我通过颜色拾取器拾取的值为"#eae9f0"。最后采用R语言自带的空白主题,去掉不需要的坐标轴,同时改变横坐标查看初步的效果:
ggplot(data = g, aes(x = as.factor(x))) + geom_col(aes(y = y), fill = "#e0dee0", color = "#e0dee0") + ### 添加第一层同心圆 geom_rect(aes(xmin = 0.55 + 1*c(0:18), xmax = 1.45 + 1*c(0:18), ymin = 92, ymax = 93.5), fill = "#eae9f0") + ### 添加第二层同心圆 geom_rect(aes(xmin = 0.55 + 1*c(0:18), xmax = 1.45 + 1*c(0:18), ymin = 120, ymax = 123), fill = "#eae9f0") + ### 设置空白主题 theme_void() + ### 将横坐标轴极化 查看玫瑰图 coord_polar()
上面第一张图没有极化坐标轴,下一张图是极化过后得到的玫瑰图。和原图对比,我们的玫瑰图圆点部分应该也有一个同心圆,之后在里面添加图片,这里我们需要改变柱形图的纵坐标轴。原始的柱形图是从(0, 0)开始的,所以极化的坐标轴就是以(0, 0)为原点,所以中间是实心的。我们将纵坐标轴的范围改为(-15, 125),就可以将坐标轴原点改为(-15, 0),125的取值是由刚才的同心圆决定的,我们绘制同心圆所用的长方形纵坐标最高取值为123,所以我们的y轴最少应为123,否则同心圆就无法显示。
ggplot(data = g, aes(x = as.factor(x))) + geom_col(aes(y = y), fill = "#e0dee0", color = "#e0dee0") + geom_rect(aes(xmin = 0.55 + 1*c(0:18), xmax = 1.45 + 1*c(0:18), ymin = 92, ymax = 93.5), fill = "#eae9f0") + geom_rect(aes(xmin = 0.55 + 1*c(0:18), xmax = 1.45 + 1*c(0:18), ymin = 120, ymax = 123), fill = "#eae9f0") + theme_void() + coord_polar() + ### 设定纵坐标轴范围 ylim(-15, 125)
到这一步,我们的底图已经绘制好了,下面我们就需要把主体的柱形图给添加上来:
ggplot(data = g, aes(x = as.factor(x))) + geom_col(aes(y = y), fill = "#e0dee0", color = "#e0dee0") + geom_rect(aes(xmin = 0.55 + 1*c(0:18), xmax = 1.45 + 1*c(0:18), ymin = 92, ymax = 93.5), fill = "#eae9f0") + geom_rect(aes(xmin = 0.55 + 1*c(0:18), xmax = 1.45 + 1*c(0:18), ymin = 120, ymax = 123), fill = "#eae9f0") + theme_void() + coord_polar() + ylim(-15, 125) + ### 添加主柱形图 进行颜色填充 去掉图示 geom_col(aes(y = y1, fill = as.factor(x)), show.legend = F)
上面两张图,第一张是通过coord_polar()对坐标轴进行极化之前我们的原始柱形图。下面那张为极化之后得到的玫瑰图。继续和原图比较,这里我们的柱形图的填充颜色和原图不一样。原图的柱形图颜色只有四种,均为蓝色色调,每四组一循环。这里我采用了手动填充的方法,如果调用调色板貌似无法到达原图的效果。和之前一样,颜色是我通过颜色拾取器自己选的,和原图有一点色差。如果有朋友有更好的填充方法,欢迎私信。
ggplot(data = g, aes(x = as.factor(x))) + geom_col(aes(y = y), fill = "#e0dee0", color = "#e0dee0") + geom_rect(aes(xmin = 0.55 + 1*c(0:18), xmax = 1.45 + 1*c(0:18), ymin = 92, ymax = 93.5), fill = "#eae9f0") + geom_rect(aes(xmin = 0.55 + 1*c(0:18), xmax = 1.45 + 1*c(0:18), ymin = 120, ymax = 123), fill = "#eae9f0") + theme_void() + coord_polar() + ylim(-15, 125) + geom_col(aes(y = y1, fill = as.factor(x)), show.legend = F) + ### 手动填充19个柱子的颜色 ### 因为颜色是每四组一循环所有有更简练的写法 ### 这里是为了更清楚展示填充过程 scale_fill_manual(values = c("#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8"))
到这一步,我们的玫瑰图就已经成型了。重新和原图对比,我们的图还缺少最后一部分,在每个柱形图的底部都有白色的镶边。这里我们需要在主柱形图的图层上面继续添加一层柱形图,高度设定为3,设定为半透明:
ggplot(data = g, aes(x = as.factor(x))) + geom_col(aes(y = y), fill = "#e0dee0", color = "#e0dee0") + geom_rect(aes(xmin = 0.55 + 1*c(0:18), xmax = 1.45 + 1*c(0:18), ymin = 92, ymax = 93.5), fill = "#eae9f0") + geom_rect(aes(xmin = 0.55 + 1*c(0:18), xmax = 1.45 + 1*c(0:18), ymin = 120, ymax = 123), fill = "#eae9f0") + theme_void() + coord_polar() + ylim(-15, 125) + geom_col(aes(y = y1, fill = as.factor(x)), show.legend = F) + scale_fill_manual(values = c("#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8")) + ### 添加镶边 geom_col(aes(y = 3), fill = "white", color = "white", alpha = 0.4)
下一步也是最为繁琐的一步,就是给玫瑰图添加标签。原图的标签分为数字标签和文字标签,不同位置的标签都设置了不同的翻转角度。我们首先设置数字标签,从长沙开始,前三组翻转角度和柱形图翻转角度一致,佛山到合肥的数字标签和柱形图垂直,西安到东莞的翻转角度与第一组相比多转了180度,天津到深圳的数字标签同样与柱形图垂直。另外最后三组数字的颜色为黑色,所以我需要设置五种不同的标签添加方式:
ggplot(data = g, aes(x = as.factor(x))) + geom_col(aes(y = y), fill = "#e0dee0", color = "#e0dee0") + geom_rect(aes(xmin = 0.55 + 1*c(0:18), xmax = 1.45 + 1*c(0:18), ymin = 92, ymax = 93.5), fill = "#eae9f0") + geom_rect(aes(xmin = 0.55 + 1*c(0:18), xmax = 1.45 + 1*c(0:18), ymin = 120, ymax = 123), fill = "#eae9f0") + theme_void() + coord_polar() + ylim(-15, 125) + geom_col(aes(y = y1, fill = as.factor(x)), show.legend = F) + scale_fill_manual(values = c("#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8")) + geom_col(aes(y = 3), fill = "white", color = "white", alpha = 0.4) + ### 以下皆为添加数字标签 ### g[1:3, ]筛选分组 geom_text(data = g[1:3, ], aes(y = y1, label = y1), ### angle设定偏转角度 nudge_y设定纵轴位移 angle = -(360/19)*c(1:3) + 9.5, nudge_y = -4, ### 设置颜色 加粗 color = "white", fontface = "bold") + geom_text(data = g[4:7, ], aes(y = y1, label = y1), angle = 30 - 360/18*c(0:3), , nudge_y = -6.5, color = "white", fontface = "bold") + geom_text(data = g[8:12, ], aes(y = y1, label = y1), angle = -(360/20 * c(8:12) + 180), nudge_y = -4, color = "white", fontface = "bold") + geom_text(data = g[13:16, ], aes(y = y1, label = y1), angle = 34 - 18*c(0:3), nudge_y = -8, color = "white", fontface = "bold") + geom_text(data = g[17:19, ], aes(y = y1, label = y1), angle = -48 - 17*c(0:2), nudge_y = 8, fontface = "bold")
在之前的代码中我没有设置标签的字体大小,如果在RStudio里面执行我的原始代码,在RStudio的图片生成界面看到的玫瑰图像素很低,并且标签的字体会很大。你可以在RStudio里面放大图片,相当于改变图形与标签的比例。在文章最后保存图片的那行代码里,我有解释在保存图片时如何修改图片的比例和像素。
在R里面数字标签添加完以后,就是添加文字标签。长沙到重庆为一组,佛山到合肥为一组,西安到天津为一组,广州到杭州为一组,上海到北京为一组,深圳单独一组。
ggplot(data = g, aes(x = as.factor(x))) + geom_col(aes(y = y), fill = "#e0dee0", color = "#e0dee0") + geom_rect(aes(xmin = 0.55 + 1*c(0:18), xmax = 1.45 + 1*c(0:18), ymin = 92, ymax = 93.5), fill = "#eae9f0") + geom_rect(aes(xmin = 0.55 + 1*c(0:18), xmax = 1.45 + 1*c(0:18), ymin = 120, ymax = 123), fill = "#eae9f0") + theme_void() + coord_polar() + ylim(-15, 125) + geom_col(aes(y = y1, fill = as.factor(x)), show.legend = F) + scale_fill_manual(values = c("#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8")) + geom_col(aes(y = 3), fill = "white", color = "white", alpha = 0.4) + ### 以下皆为添加数字标签 ### g[1:3, ]筛选分组 geom_text(data = g[1:3, ], aes(y = y1, label = y1), ### angle设定偏转角度 nudge_y设定纵轴位移 angle = -(360/19)*c(1:3) + 9.5, nudge_y = -4, ### 设置颜色 加粗 color = "white", fontface = "bold") + geom_text(data = g[4:7, ], aes(y = y1, label = y1), angle = 30 - 360/18*c(0:3), , nudge_y = -6.5, color = "white", fontface = "bold") + geom_text(data = g[8:12, ], aes(y = y1, label = y1), angle = -(360/20 * c(8:12) + 180), nudge_y = -4, color = "white", fontface = "bold") + geom_text(data = g[13:16, ], aes(y = y1, label = y1), angle = 34 - 18*c(0:3), nudge_y = -8, color = "white", fontface = "bold") + geom_text(data = g[17:19, ], aes(y = y1, label = y1), angle = -48 - 17*c(0:2), nudge_y = 8, fontface = "bold") + ### 添加地名标签 geom_text(data = g[1:3, ], aes(y = y1, label = place), angle = -(360/19)*c(1:3) + 9.5, nudge_y = 4, fontface = "bold") + geom_text(data = g[4:7, ], aes(y = y1, label = place), angle = 30 - 360/18*c(0:3), nudge_y = 4, fontface = "bold") + geom_text(data = g[8:13, ], aes(y = y1, label = place), angle = -(360/20 * c(8:13) + 180), nudge_y = 4, fontface = "bold") + geom_text(data = g[14:16, ], aes(y = y1, label = place), angle = 34 - 18*c(1:3), nudge_y = 4, fontface = "bold") + geom_text(data = g[17:18, ], aes(y = y1, label = place), angle = -48 - 17*c(0:1), nudge_y = 22, fontface = "bold") + geom_text(data = g[19, ], aes(y = y1, label = place), angle = 9, nudge_y = 22, fontface = "bold")
到这里,我们的玫瑰图就基本画完了。网易的原图有在中间添加一个房子的图标,我们也可以使用ggimage包添加图片。我从谷歌上下载了一张类似的图片,保存在R的默认路径里命名为"house.png"。原图在房子图标外面还画了一个圈,我们也可以通过添加长方形的方法添加:
ggplot(data = g, aes(x = as.factor(x))) + geom_col(aes(y = y), fill = "#e0dee0", color = "#e0dee0") + geom_rect(aes(xmin = 0.55 + 1*c(0:18), xmax = 1.45 + 1*c(0:18), ymin = 92, ymax = 93.5), fill = "#eae9f0") + geom_rect(aes(xmin = 0.55 + 1*c(0:18), xmax = 1.45 + 1*c(0:18), ymin = 120, ymax = 123), fill = "#eae9f0") + theme_void() + coord_polar() + ylim(-15, 125) + geom_col(aes(y = y1, fill = as.factor(x)), show.legend = F) + scale_fill_manual(values = c("#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8", "#918bad", "#3e317a", "#50438c", "#8077a8")) + geom_col(aes(y = 3), fill = "white", color = "white", alpha = 0.4) + geom_text(data = g[1:3, ], aes(y = y1, label = y1), angle = -(360/19)*c(1:3) + 9.5, nudge_y = -4, color = "white", fontface = "bold") + geom_text(data = g[4:7, ], aes(y = y1, label = y1), angle = 30 - 360/18*c(0:3), , nudge_y = -6.5, color = "white", fontface = "bold") + geom_text(data = g[8:12, ], aes(y = y1, label = y1), angle = -(360/20 * c(8:12) + 180), nudge_y = -4, color = "white", fontface = "bold") + geom_text(data = g[13:16, ], aes(y = y1, label = y1), angle = 34 - 18*c(0:3), nudge_y = -8, color = "white", fontface = "bold") + geom_text(data = g[17:19, ], aes(y = y1, label = y1), angle = -48 - 17*c(0:2), nudge_y = 8, fontface = "bold") + geom_text(data = g[1:3, ], aes(y = y1, label = place), angle = -(360/19)*c(1:3) + 9.5, nudge_y = 4, fontface = "bold") + geom_text(data = g[4:7, ], aes(y = y1, label = place), angle = 30 - 360/18*c(0:3), nudge_y = 4, fontface = "bold") + geom_text(data = g[8:13, ], aes(y = y1, label = place), angle = -(360/20 * c(8:13) + 180), nudge_y = 4, fontface = "bold") + geom_text(data = g[14:16, ], aes(y = y1, label = place), angle = 34 - 18*c(1:3), nudge_y = 4, fontface = "bold") + geom_text(data = g[17:18, ], aes(y = y1, label = place), angle = -48 - 17*c(0:1), nudge_y = 22, fontface = "bold") + geom_text(data = g[19, ], aes(y = y1, label = place), angle = 9, nudge_y = 22, fontface = "bold") + ### 添加圆圈 geom_rect(aes(xmin = 0.55, xmax = 19.45, ymin = -3, ymax = -2), fill = "#3e317a") + ### 添加图片 geom_image(mapping = aes(x = 1, y = -15, image = "house.png"))
最后保存图片,这里设置了图片与标签的比例为3,如果比例太小,我们的标签部分就会变得太大,设置像素dpi为300,这样保存的图片就十分清晰。设置长和宽比值为1,保存成正方形图片:
ggsave("rosemap.png", scale = 3, dpi = 300, width = 3, height = 3)
以上就是我仿制网易的玫瑰图的全过程。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。