Stata绘图:环形柱状图-实时全球新冠确诊人数

发布时间:2022-06-30 阅读 1533

Stata连享会   主页 || 视频 || 推文 || 知乎 || Bilibili 站

温馨提示: 定期 清理浏览器缓存,可以获得最佳浏览体验。

New! lianxh 命令发布了:
随时搜索推文、Stata 资源。安装:
. ssc install lianxh
详情参见帮助文件 (有惊喜):
. help lianxh
连享会新命令:cnssc, ihelp, rdbalance, gitee, installpkg

课程详情 https://gitee.com/lianxh/Course

课程主页 https://gitee.com/lianxh/Course

⛳ Stata 系列推文:

PDF下载 - 推文合集

作者: 范思妤 (南京大学)
邮箱: fansiyu@smail.nju.edu.cn

致谢: 本文摘自以下文章,特此感谢! Source: Asjad Naqvi, 2022, Blog, Stata graphs: Circular bar graphs


目录


1. 简介

说起柱状图大家并不陌生,但如果柱状图需要展示的项目过多而无法在一条直线上进行有效地平铺展示时,环形柱状图就不失为一种简洁美观展示数据特征的手段。环形柱状图是指每个条形都沿圆而不是直线显示的图形。

环形柱状图没有y轴显示,因而样本对应的数值写在每个条形上,也可以在与图表的交互中表示。总的来说,环形柱状图因其可视化效果而颇受欢迎,但需要谨慎使用,因为群体不共享相同的y轴。

基于 Our World in Data 提供的新型冠状病毒数据集,本文将详细解析以下环形柱状图的编绘过程,来帮助读者了解使用 Stata 编绘环形柱状图的基本步骤和实操代码。

建议读者使用 Stata15 及以上版本进行本文的操作。

2. 准备工作

在进行正式绘图前,我们需要设定以下项目,进行准备工作。

首先安装如下命令,并进行相关设置

ssc install schemepack, replace
set scheme white_tableau       //将背景设置为简洁的白色

安装调色板工具palettes,命令详情可以参见连享会往期推文:Stata绘图:自定义绘图利器-palettes

ssc install palettes, replace
ssc install colrspace, replace

//将默认图形字体设置为 Arial Narrow
graph set window fontface "Arial Narrow"

3. 获取数据

我们首先需要从 Our World in Data 提供的新型冠状病毒数据集中读入数据,对数据进行简单的清洗,并转化成 dta 格式。

//从网络读入数据
import delim using "https://covid.ourworldindata.org/data/owid-covid-data.csv", clear
gen date2 = date(date, "YMD")
format date2 %tdDD-Mon-yy
drop date
ren date2 date
gen date2 = date
order date date2
keep date continent location new_* total*

// 对数据进行清洗
drop if continent==""
replace location = "Bonaire Sint Eustatius" if location =="Bonaire Sint Eustatius and Saba"
replace location = "Bosnia & Herzeg." if location =="Bosnia and Herzegovina"
replace location = "Turks & Caicos Is." if location =="Turks and Caicos Islands"

compress  
save owid.dta, replace

生成我们需要的变量。其中,变量 newcases 为某国家(地区)过去 7 日内新增确诊病例数,变量 newdeaths 为某国家(地区)过去 7 日内新增死亡病例数。

newcases_100knewdeaths_100k 为对应的标准化后(每百万人口)的变量。

use owid.dta, clear
sort continent location date
by continent location: gen double newcases  = total_cases  - total_cases[_n-7]
by continent location: gen double newdeaths = total_deaths - total_deaths[_n-7]
by continent location: gen double newcases_100k  = total_cases_per_million  - total_cases_per_million[_n-7]
by continent location: gen double newdeaths_100k = total_deaths_per_million - total_deaths_per_million[_n-7]

由于一张图中只绘制一个时间点,因此我们只保留数据集中最近的日期:

summ date
keep if date == `r(max)' - 1 
keep date continent location newcases* newdeaths*

我们通过去除空值来进一步缩小我们的样本,以最大化利用环形上的空间。我们删除过去7天的新增死亡病例为零值的样本:

drop if newdeaths_100k < 1

我们按照大洲分组,再按照标准化后的过去 7 日内新增死亡病例数升序排列:

sort continent newdeaths_100k

4. 绘制基础环形柱状图

我们通过以下操作为每一个样本在圆上分配一个位置,即某一样本在圆上对应的角度。其中,2*_pi2π ) 代表 360 度的圆形,而 _n/_N 则代表某一样本在全样本中所占位置。

cap drop angle
gen double angle = (2*_pi)*(_n/_N) 

在确定了角度以后,我们需要生成两组点。第一组表示圆上的一个点,第二组表示圆上该点的高度。

summ newdeaths_100k, d
global circ = `r(p99)'

// 圆上的点
gen double xcir = ($circ * cos(angle))
gen double ycir = ($circ * sin(angle))

// 圆上该点的高度
gen double xval = ((newdeaths_100k + $circ ) * cos(angle)) 
gen double yval = ((newdeaths_100k + $circ ) * sin(angle))

//绘制对应散点图
twoway /// 
  (scatter yval xval) /// 
  (scatter ycir xcir) /// 
  , aspect(1) legend(off)

我们将散点图的散点绘转换成柱状图的条柱,环形柱状图就已经具备了基础的“骨骼”。

twoway ///
  (pcspike yval xval ycir xcir, lw(0.6)) ///  
   , aspect(1) legend(off)

虽然我们的图形已经初现雏形,但还存在以下瑕疵:

  • 坐标轴的范围不一样。由于我们不知道条柱的尖峰会出现在圆上的哪个位置,我们需要确保两个轴上的最小值和最大值是完全相同的数字。换句话说,我们需要以原点 (0,0) 为圆心,并确保图形的范围是完全的正方形。
  • 我们需要为数据标签预留一些空间。

对此,我们进行如下调整:

summ xval
 local xmax = `r(max)'
 local xmin = `r(min)'
  
summ yval
 local ymax = `r(max)'
 local ymin = `r(min)'
global edge = max(`xmax',`ymax', abs(`xmin'), abs(`ymin')) * 1.1

twoway ///
 (pcspike yval xval ycir xcir, lw(0.5) ) ///  
  , ///
  xlabel(-$edge $edge) ///
  ylabel(-$edge $edge) ///
  aspect(1)

经过调整后,我们的环形柱状图变成了如下的样子:

接下来,我们为不同大洲分配不同样色:


// 为不同大洲分配不同样色
local cont
local i
levelsof continent, local(lvls)  
local items = `r(r)'
local i = 1
foreach x of local lvls {
 colorpalette w3, n("`items'") nograph
 
 local cont `cont' (pcspike yval xval ycir xcir if continent=="`x'", lc("`r(p`i')'") lw(0.5)) ||  
 local ++i
 
}
twoway ///
 `cont' ///  
  , ///
  xlabel(-$edge $edge) ///
  ylabel(-$edge $edge) ///
  aspect(1) legend(off)

5. 国家/地区标签

在绘制好环形柱状图的基本框架以后,我们需要为每一样本添加对应的国家安全/地区标签,标签和样本之间应当保持一定距离,我们将偏移距离设置为 30% 。

此外,为了让标签更加简洁美观,我们需要进行如下处理:

  • 首先,我们对样本值进行四舍五入以避免显示太多信息
  • 其次,我们仅将标签限制在那些值为 10 或更高的国家/地区,避免将所有国家/地区的标签堆叠挤压在一起
//设置标签所在位置
cap drop xlab ylab
gen xlab = ((newdeaths_100k + $circ * 1.3) * cos(angle)) 
gen ylab = ((newdeaths_100k + $circ * 1.3) * sin(angle))

//让标签更加简洁美观
cap drop mylab 
gen mylab = location + " (" + string(newdeaths_100k, "%8.0f") + ")" if newdeaths_100k >= 10

//绘图
twoway /// 
(pcspike yval xval ycir xcir, lw(0.5) ) ///   
(scatter ylab xlab, mlab(mylab) mlabsize(1) mlabpos(0) ms(none) ) /// 
  , /// 
  xlabel( -$edge $edge) /// 
  ylabel(-$edge $edge) /// 
  aspect(1) legend(off)

可以看到,由于所有的标签都是水平方向,所以标签有所重叠,我们需要根据标签所处的位置在不同方向上旋转标签。

具体地,我们进行如下处理:

  • 将图像划分为四个象限
  • 不同象限的标签旋转不同角度

对应命令如下:

//我们首先按如下方式分配象限
cap drop quad
gen quad = .  // quadrants
 replace quad = 1 if xcir >= 0 & ycir >= 0 
 replace quad = 2 if xcir <= 0 & ycir >= 0   
 replace quad = 3 if xcir <= 0 & ycir <= 0
 replace quad = 4 if xcir >= 0 & ycir <= 0

//旋转对应象限的标签角度
cap drop angle2 
gen double angle2 = .
 replace angle2 = (angle  * (180 / _pi)) - 180 if angle >  _pi & !inlist(quad,2,4)
 replace angle2 = (angle  * (180 / _pi))       if angle <= _pi & !inlist(quad,2,4)
 replace angle2 = (angle  * (180 / _pi)) - 180 if angle <= _pi & quad==2
 replace angle2 = (angle  * (180 / _pi))       if angle >  _pi & quad==4

之后我们重新进行绘图

// 标签 
local labs2
qui levelsof mylab , local(lvls)  //
foreach x of local lvls {
 
 qui summ angle2 if mylab== "`x'" 
 
 local labs2 `labs2' (scatter ylab xlab if mylab== "`x'"  , mc(none) mlabel(mylab) mlabangle(`r(mean)')  mlabpos(0) mlabcolor() mlabsize(1))  
}

// 大洲颜色
local cont
local i
levelsof continent, local(lvls)  
local items = `r(r)'
local i = 1
foreach x of local lvls {
 
 colorpalette w3, n("`items'") nograph
 
 local cont `cont' (pcspike yval xval ycir xcir if continent=="`x'", lc("`r(p`i')'") lw(0.5)) ||  
 local ++i
 
}

// 绘图
twoway ///
 `cont' ///
 `labs2' ///
  , ///
  xlabel(-$edge $edge) ///
  ylabel(-$edge $edge) ///
  aspect(1) legend(off)

6. 洲名标签

不同的颜色对应不同的大洲,我们需要在图片上添加洲名的标签。洲名的标签需要居中位于对应地区最大值和最小值之间。


cap drop tag
egen tag = tag(continent)
recode tag (0=.)

// 生成变量
cap drop *cont
gen double xcont     = .
gen double ycont     = .
gen double anglecont = .

// 生成角度
levelsof continent if tag==1, local(lvls)
foreach x of local lvls {
qui summ angle if continent== "`x'"
 
replace anglecont = (`r(max)' + `r(min)') / 2 if tag==1 & continent== "`x'"
replace xcont = ($circ * 0.9 * cos(anglecont)) if tag==1 & continent== "`x'"
replace ycont = ($circ * 0.9 * sin(anglecont)) if tag==1 & continent== "`x'"
}

上面代码中0.9是标签的位移值,这样它才不会和图形本身重叠。然后我们还需要将洲名的标签旋转90度,让它和圆弧相切:

replace anglecont = (anglecont  * (180 / _pi)) - 90  if tag==1

我们将洲名添加到绘图中

local labcont
levelsof continent if tag==1, local(lvls)
foreach x of local lvls {
 
 summ anglecont if continent== "`x'" & tag==1, meanonly
 
 local labcont `labcont' (scatter ycont xcont if continent== "`x'" & tag==1, mc(none) mlabel(continent) mlabangle(`r(mean)')  mlabpos(0) mlabcolor(black) mlabsize(1.8))  ||
 
}  
 
 
twoway ///
  (pcspike yval xval ycir xcir, lw(0.5) ) ///  
 `labcont' ///
  , ///
  xlabel(-$edge $edge) ///
  ylabel(-$edge $edge) ///
  aspect(1)  legend(off)

7. 附:完整绘图代码

最后,我们将所有以上代码进行汇总,绘制环形柱状图,并在圆形中心添加图名和绘图日期。

*************************
**       最终绘图       **
*************************

// 基础环形柱状图框架
local cont
local i
levelsof continent, local(lvls)  
local items = `r(r)'
local i = 1
foreach x of local lvls {
 colorpalette w3, n("`items'") nograph
 local cont `cont' (pcspike yval xval ycir xcir if continent=="`x'", lc("`r(p`i')'") lw(0.5)) ||  
 local ++i
}

// 国家/地区标签
local labs2
qui levelsof mylab , local(lvls)  //
foreach x of local lvls {
 qui summ angle2 if mylab== "`x'" 
 local labs2 `labs2' (scatter ylab xlab if mylab== "`x'"  , mc(none) mlabel(mylab) mlabangle(`r(mean)')  mlabpos(0) mlabcolor() mlabsize(1))  
}   
  
// 大洲标签
local labcont
levelsof continent if tag==1, local(lvls)
foreach x of local lvls {
 qui summ anglecont if continent== "`x'" & tag==1, meanonly
 local labcont `labcont' (scatter ycont xcont if continent== "`x'" & tag==1, mc(none) mlabel(continent) mlabangle(`r(mean)')  mlabpos(0) mlabcolor(black) mlabsize(1.2))  ||
 
}   
 
 
// 数据日期
summ date
 local date = r(max)
 local date : di %tdDD_Mon_yy `date'

// 绘图
twoway ///
 `cont' ///
 `labs2' ///
 `labcont' ///
  , ///
  xlabel(-$edge $edge, nogrid) ///
  ylabel(-$edge $edge, nogrid) ///
  legend(off) ///
  aspect(1) xsize(1) ysize(1) ///
  yscale(off) xscale(off) ///
  text( 15 0 "{fontface Merriweather Bold:COVID-19}", size(2.5) box just(center) margin(t+2 b+2) fcolor(none) lw(none) color()) ///
  text(-10 0 "deaths per one million pop" "in the past 7 days", size(1.5) box just(center) margin(t+2 b+2) fcolor(none) lw(none) color()) ///
  text(-30 0 "(`date')", size(1.1) box just(center) margin(t+2 b+2) fcolor(none) lw(none) color()) ///
  note("Source: Our World in Data. Countries with no reported deaths are dropped from the graph. Only countries with over 10 deaths are labeled.", size(1.2))

我们得到了最终的全球多个国家/地区过去7日内新增死亡病例(每百万人口)的环形柱状图:

以上就是本文分享的全部内容了。读者可以仔细探索环形柱状图的一些绘图技巧,熟稔于心,为研究中数据的图形可视化提供更多可能性!

8. 相关推文

Note:产生如下推文列表的 Stata 命令为:
lianxh 柱状图 直方图 可视化
安装最新版 lianxh 命令:
ssc install lianxh, replace

相关课程

免费公开课

最新课程-直播课

专题 嘉宾 直播/回看视频
最新专题 文本分析、机器学习、效率专题、生存分析等
研究设计 连玉君 我的特斯拉-实证研究设计-幻灯片-
面板模型 连玉君 动态面板模型-幻灯片-
面板模型 连玉君 直击面板数据模型 [免费公开课,2小时]
  • Note: 部分课程的资料,PPT 等可以前往 连享会-直播课 主页查看,下载。

课程主页

课程主页

关于我们

  • Stata连享会 由中山大学连玉君老师团队创办,定期分享实证分析经验。
  • 连享会-主页知乎专栏,700+ 推文,实证分析不再抓狂。直播间 有很多视频课程,可以随时观看。
  • 公众号关键词搜索/回复 功能已经上线。大家可以在公众号左下角点击键盘图标,输入简要关键词,以便快速呈现历史推文,获取工具软件和数据下载。常见关键词:课程, 直播, 视频, 客服, 模型设定, 研究设计, stata, plus, 绘图, 编程, 面板, 论文重现, 可视化, RDD, DID, PSM, 合成控制法

连享会小程序:扫一扫,看推文,看视频……

扫码加入连享会微信群,提问交流更方便

✏ 连享会-常见问题解答:
https://gitee.com/lianxh/Course/wikis

New! lianxh 命令发布了:
随时搜索连享会推文、Stata 资源,安装命令如下:
. ssc install lianxh
使用详情参见帮助文件 (有惊喜):
. help lianxh