用 ggplot 重绘天猫双十一销售额图
前一段时间,很多人被这张图刷屏了:
这张图的来源是一篇名为 “淘宝2009-2018年历年双11销售额数据造假” 的文章。图中散点为天猫双十一销售额数据,曲线为原作者拟合的回归线。乍一看散点完美地分布在曲线上,原作者便直接声称:“淘宝双十一销售额数据造假!不可能有这么完美的拟合!” 可事实真的是这样吗?
作为一个热爱画画的博主,我们先不来考虑这个问题,直接来试着在 R 中绘制出上图,一个更好看版本的上图。
Let’s start!!!
温馨小提示:在这篇博客中,你可以学会下述 ggplot
的绘图小技巧:
- 如何在图像中拟合二次曲线;
- 如何在图像中添加文本或自适应地添加文本;
- 如何删除一些不必要的背景线;
- 还有一些常规操作:换主题、改刻度、改颜色等等… …
偷偷放个最终成果:
输入数据
首先我们查到具体每一年的天猫销售额数据,然后在 R 中构建 data frame
.
year <- 2009:2019
sales <- c(0.5, 9.36, 52, 191, 350, 571, 912, 1207, 1682, 2135, 2684)
dat_sales <- data.frame(year = year, sales = sales)
由于我们想重点凸显出 2019 年的具体表现,所以我们还需要添加一列 index 表示是否为 2019 年(非 2019 年为 1
, 2019 年为 2
):
dat_sales$ind <- factor(c(rep(x = 1, 10), 2))
数据框长着如下这样:
year sales ind
1 2009 0.50 1
2 2010 9.36 1
3 2011 52.00 1
4 2012 191.00 1
5 2013 350.00 1
6 2014 571.00 1
7 2015 912.00 1
8 2016 1207.00 1
9 2017 1682.00 1
10 2018 2135.00 1
11 2019 2684.00 2
好啦,有了上述数据框,我们就可以开始进行 ggplot
的绘图了!
粗略绘图
首先我们就用默认参数画出散点变化趋势,同时 2019 年标注出不一样的颜色:
library(ggplot2)
ggplot(dat = dat_sales) +geom_point(aes(x = year, y = sales, col = ind))
但是这样的绘图有很多问题:缺少标题,坐标轴的标题改中文,x 轴的显示不是离散的年份,散点过小,图例问题等等问题,我们先一步一步来进行完善。
完善散点图
ggplot(dat = dat_sales) +geom_point(aes(x = year, y = sales, col = ind), size = 4) +scale_x_continuous(breaks = 2009:2019, labels = 2009:2019) +labs(title = "2009 - 2019 年销售额", x = "年份", y = "销售额 (亿元)") +theme(legend.position = "none",plot.title = element_text(hjust = 0.5))
其中,size = 4
, 表示将散点进行放大,具体的尺寸可以自己进行设置; breaks = 2009:2019
表示原本数据集中在 x 轴上对应的值; labels = 2009:2019
表示映射到图像上 x 轴的值; legend.position = "none"
表示取消 legend 显示。
强调:这里为了显示年份,其实可以采用将年份转换成 factor 型的方法,但由于我们还需要使用年份数据进行曲线拟合,若转化成 factor 会使得曲线拟合失败,因此我们采用稍微复杂一些的修改 x 轴刻度的方法。
添加拟合曲线
下面我们就开始添加二次函数拟合曲线:
ggplot(dat = dat_sales) +geom_point(aes(x = year, y = sales, col = ind), size = 4) +geom_smooth(aes(x = year, y = sales), se = FALSE, method = "lm", formula = y ~ x + I(x^2), size = 2) +scale_x_continuous(breaks = 2009:2019, labels = 2009:2019) +labs(title = "2009 - 2019 年销售额", x = "年份", y = "销售额 (亿元)") +theme(legend.position = "none",plot.title = element_text(hjust = 0.5))
在 ggplot
中通常都是使用 geom_smooth
来进行曲线或者直线的拟合,对于线性、二次、三次函数,我们都是使用 method = "lm"
; se = FALSE
表示不显示置信区间; formula = y ~ x + I(x^2)
表示使用二次函数进行拟合; 最后的 size = 2
表示调整线的粗细。
在散点上添加销售额
可以发现,这样的曲线只能看出趋势,但是却看不出每年的具体销售额,所以接下来我们尝试在图像的每个散点加上具体的销售额:
# library(ggrepel)
ggplot(dat = dat_sales) +geom_point(aes(x = year, y = sales, col = ind), size = 4) +geom_smooth(aes(x = year, y = sales), se = FALSE, method = "lm", formula = y ~ x + I(x^2), size = 2) +geom_text(aes(x = year, y = sales, label = sales), hjust = 0.5, vjust = -1) +# geom_text_repel(aes(x = year, y = sales, label = sales)) +ylim(0, 3000) +scale_x_continuous(breaks = 2009:2019, labels = 2009:2019) +labs(title = "2009 - 2019 年销售额", x = "年份", y = "销售额 (亿元)") +theme(legend.position = "none",plot.title = element_text(hjust = 0.5))
当我们需要在绘图中添加文本时,通常可以使用 geom_text
函数,然后 aes(label = )
中设定需要显示的变量名称,最后的 hjust = 0.5, vjust = -1
表示调整显示的相对位置,前者表示水平位置,后者表示垂直位置,这个根据绘图的不同需要自己进行手动调整。
同时,2019 年的销售额较高,如果不调整 y 轴的显示范围,最上面显示的销售额 2684 会被遮挡,所以我们添加了 ylim(0, 3000)
。
另外,细心的童鞋一定发现了,我们在代码中添加了两行注释,注释的内容同样是添加文本的语句: geom_text_repel(aes(x = year, y = sales, label = sales))
,这个语句可以自适应地调整每个文本显示内容,使文本不会挡住我们的散点和曲线,这个函数在包 ggrepel
中。这里之所以是因为这个方式大多用于图像中散点比较多,比较乱的情况,如果在此幅图中使用,会使得销售额的文本显示略显混乱。
删除不必要的背景线
再细心一些的童鞋可能观察到了,背景的网格图,在 x 轴每两个年份之间都有一根垂直线,这个垂直线是毫无意义的。这时我们不禁要问,是否有方法能够将这根线消去呢?
答案是肯定的:
ggplot(dat = dat_sales) +geom_point(aes(x = year, y = sales, col = ind), size = 4) +geom_smooth(aes(x = year, y = sales), se = FALSE, method = "lm", formula = y ~ x + I(x^2), size = 2) +geom_text(aes(x = year, y = sales, label = sales), hjust = 0.5, vjust = -1) +ylim(0, 3000) +scale_x_continuous(breaks = 2009:2019, labels = 2009:2019) +labs(title = "2009 - 2019 年销售额", x = "年份", y = "销售额 (亿元)") +theme(panel.grid.minor = element_blank(),legend.position = "none",plot.title = element_text(hjust = 0.5))
修改起来其实也不难,可以发现,每个年份对应的垂直线叫做 major
,而年份没对应到的网格线叫做 minor
,因此我们直接在 theme
中添加 panel.grid.minor = element_blank()
即可。
终极美化
到这里,我们的基本元素的拼凑已经告一段落了,但是整体图看起来依旧是不够美观,所以接下来我们再进行一些操作来美化绘图,最终代码与结果如下:
ggplot(dat = dat_sales, aes(x = year, y = sales)) +geom_smooth(se = FALSE, method = "lm", formula = y ~ x + I(x^2), size = 2, col = "#b3cde3") +geom_point(aes(col = ind), size = 4) +ylim(0, 3000) +geom_text(aes(label = sales), hjust = 0.5, vjust = -1) +scale_x_continuous(breaks = 2009:2019, labels = 2009:2019) +labs(title = "2009 - 2019 年销售额", x = "年份", y = "销售额 (亿元)") +theme_bw(base_family = "Times") +theme(legend.position = "none",panel.grid.minor = element_blank(),panel.border = element_blank(),plot.title = element_text(hjust = 0.5))
这里其实做了一些细节的调整,首先先绘制拟合曲线,再绘制散点,这样散点就会在曲线的上方,这样看起来会更加的美观。其次改变了拟合曲线的颜色:col = "#b3cde3"
,改变了绘图的主题:theme_bw(base_family = "Times")
,删除了丑丑的边框:panel.border = element_blank()
。
至此,我们美美的绘图重构就完成了!
小作业
最后,感兴趣的童鞋不妨将我们图中的二次拟合曲线替换成三次函数拟合,或者两者均添加,然后修改一下透明度,使得两条线都能看见一部分。
用 ggplot 重绘天猫双十一销售额图相关推荐
- 浏览器的回流与重绘 (Reflow Repaint)
参考<极客学院--浏览器工作原理与实践> 渲染流程大致可总结为如下: 1. 渲染进程将 HTML 内容转换为能够读懂的 DOM 树结构. 2. 渲染进程将 CSS 样式表转化为浏览器可以理 ...
- Stable Diffusion系列课程上:安装、提示词入门、常用模型(checkpoint、embedding、LORA)、放大算法、局部重绘、常用插件
文章目录 一.Stable Diffusion简介与安装 二.文生图(提示词解析) 2.1 提示词入门 2.2 权重 2.3 负面提示词( Negative prompt) 2.4 出图参数设置 2. ...
- 如何重绘「江南百景图」?近300页 PPT 免费分享!
去年,古风模拟经营类手游<江南百景图>成功破圈,成为年度现象级爆款.如何将它搬到小游戏平台?是转换还是重写?使用哪些技术方案,能在包体大小仅为原版1/20的同时,达到与 App 版相当的游 ...
- AI 绘画用 Stable Diffusion 图生图局部重绘功能给美女换装(这是我能看的嘛)
昨天带大家一起装好了 Stable Diffusion 的环境,今天就来带大家一起体验一下 Stable Diffusion 的局部重绘功能. 没装好环境的可以看上一篇:AI 绘画基于 Kaggle ...
- HighChart学习-更新数据data Series与重绘
一:HighChart介绍 基于JQuery的纯JavaScript的图标库,支持各种图表显示,同时还支持Mootools 与Prototype详细版本支持在这里: JQuery 1.3.2 - 1. ...
- winform控件大小改变是防止背景重绘导致的闪烁
在工作中需要做一个伸缩控件,这个自定义控件继承于Panel.这个伸缩控件分为两个部分,头部是一个自定义组件,伸缩控件的背景为灰色,头部背景要求白色.伸缩控件在点击按钮时会重绘,同时他内部的控件也会重绘 ...
- java使用重绘实现拖动_原生JS使用Canvas实现拖拽式绘图功能
一.实现的功能 1.基于oop思想构建,支持坐标点.线条(由坐标点组成,包含方向).多边形(由多个坐标点组成).圆形(包含圆心坐标点和半径)等实体 2.原生JavaScript实现,不依赖任何第三方j ...
- Android视图状态及重绘流程分析,带你一步步深入了解View(三)
在前面一篇文章中,我带着大家一起从源码的层面上分析了视图的绘制流程,了解了视图绘制流程中onMeasure.onLayout.onDraw这三个最重要步骤的工作原理,那么今天我们将继续对View进行深 ...
- winform 异步弹窗窗体_玩转控件:重写/重绘Dev中MessageBox弹窗控件
很久没有更新博客了,本想着直接发一篇<手撕ERP>系列,从控件重写.重绘,到框架搭建,再到部分模块实现+业务的.但是每次动手的时候,都觉得难以下手.直接从数据库设计开始吧,模块设计还没定下 ...
最新文章
- listener.ora、sqlnet.ora、tnsnames.ora 详解
- 函数计算自动化运维实战2 -- 事件触发 eip 自动转移
- Git使用教程:最详细、最浅显、一文读懂Git常用操作!
- 玩转接口测试,那些必备的技能
- 智能电话机器人中的语音识别技术是什么?
- 风变Python6---布尔值,break,continue,pass,else等语句的学习
- 最新b站后端源码,仅学习使用,请勿用于商业用途,如拿去非法使用与本人无关!
- Word的样式库在 选项卡中_2分钟学会在Word中制作田字格 米字格 书法练字再也不用买本子了...
- 小程序中引用阿里云图标库
- 情境领导者-第四章、选择合适的领导风格
- ❤️React Hooks⭐
- 全志A40i工业核心板,100%国产4核ARM Cortex-A7,支持“双屏异显”【显示接口能力,工业HMI首选方案】
- 大疆自动驾驶,首次官宣即交货
- 蚂蚁金服黑科技:SOFA DTX分布式事务,保障亿级资金操作一致性
- 分析肖特基二极管的优势与结构应用
- 关于IDEA导包出现错误,爆红的情况
- [系统安全] Windbg Preview调试记录
- 年末的大厂前端面试总结(20届双非二本)-终入字节
- 机器学习(1): 线性回归——最小二乘法 小结
- Stream流:基本API操作详细笔记