目标
制作2021年人口金字塔
- 清洗文本型数据:usubstr和usubinstr
- 转换数据类型:encode
- 调整因子变量的值和标签的排序:elabel recode
- 水平条形图:twoway bar, horizontal
1、获取原始数据
从国家统计局网站获取按年龄分性别的人口抽样调查数据。
age | y2021 |
---|---|
男性人口数(人口抽样调查)(人) | 763842 |
0-4岁男性人口数(人口抽样调查)(人) | 38288 |
5-9岁男性人口数(人口抽样调查)(人) | 50970 |
10-14岁男性人口数(人口抽样调查)(人) | 49345 |
15-19岁男性人口数(人口抽样调查)(人) | 42677 |
20-24岁男性人口数(人口抽样调查)(人) | 41020 |
25-29岁男性人口数(人口抽样调查)(人) | 48189 |
30-34岁男性人口数(人口抽样调查)(人) | 66101 |
35-39岁男性人口数(人口抽样调查)(人) | 56115 |
40-44岁男性人口数(人口抽样调查)(人) | 50206 |
45-49岁男性人口数(人口抽样调查)(人) | 58320 |
50-54岁男性人口数(人口抽样调查)(人) | 65216 |
55-59岁男性人口数(人口抽样调查)(人) | 61237 |
60-64岁男性人口数(人口抽样调查)(人) | 35357 |
65-69岁男性人口数(人口抽样调查)(人) | 39951 |
70-74岁男性人口数(人口抽样调查)(人) | 27247 |
75-79岁男性人口数(人口抽样调查)(人) | 16457 |
80-84岁男性人口数(人口抽样调查)(人) | 10175 |
85-89岁男性人口数(人口抽样调查)(人) | 5066 |
90-94岁男性人口数(人口抽样调查)(人) | 1643 |
95岁以上男性人口数(人口抽样调查)(人) | 263 |
女性人口数(人口抽样调查)(人) | 730212 |
0-4岁女性人口数(人口抽样调查)(人) | 34690 |
5-9岁女性人口数(人口抽样调查)(人) | 45124 |
10-14岁女性人口数(人口抽样调查)(人) | 42959 |
15-19岁女性人口数(人口抽样调查)(人) | 36737 |
20-24岁女性人口数(人口抽样调查)(人) | 36236 |
25-29岁女性人口数(人口抽样调查)(人) | 43540 |
30-34岁女性人口数(人口抽样调查)(人) | 61955 |
35-39岁女性人口数(人口抽样调查)(人) | 53057 |
40-44岁女性人口数(人口抽样调查)(人) | 47817 |
45-49岁女性人口数(人口抽样调查)(人) | 56225 |
50-54岁女性人口数(人口抽样调查)(人) | 64103 |
55-59岁女性人口数(人口抽样调查)(人) | 60752 |
60-64岁女性人口数(人口抽样调查)(人) | 35399 |
65-69岁女性人口数(人口抽样调查)(人) | 41394 |
70-74岁女性人口数(人口抽样调查)(人) | 28962 |
75-79岁女性人口数(人口抽样调查)(人) | 18429 |
80-84岁女性人口数(人口抽样调查)(人) | 12368 |
85-89岁女性人口数(人口抽样调查)(人) | 7252 |
90-94岁女性人口数(人口抽样调查)(人) | 2606 |
95岁以上女性人口数(人口抽样调查)(人) | 608 |
2、数据整理
2.1 字符串变量清洗
2.1.1 利用substr系列函数清洗
/* 先用split命令对字符串变量进行拆分 */
split age, parse("性") gen(agegrp)
list
drop agegrp2
/* 利用usubstr()函数对中文字符提取 */
gen gender1=usubstr(agegrp1, -1,1)
/* 利用subinstr()函数对中文字符替换*/
replace agegrp1=subinstr(agegrp1,gender1,"", .)
list
小笔记
split *strvar* [if] [in] [, *options*]
常用选项为parse(parse_strings),设定拆分的分隔符,默认为空格。本例中自行设定为一个统一的字符“性”。
udsubstr(s,n1,n2)
提取字符串s中从第n1个字符开始,n2列的子字符串。
例如:
- udsubstr("médiane",2,3) = "édi"
- udsubstr("中值",1,1) = ""
- udsubstr("中值",1,2) = "中"
substr()不能处理中文字符,需要用替代函数usubstr()或udsubstr()。
usubstr(s,n1,n2)
提取字符串s中从第n1个字符开始,长度为n2个的子字符串。
- usubstr("中值",1,1) = "中"
- usubstr("中值",1,2) = "中值"
subinstr(s1,s2,s3,n)
字符串s1中出现的前n个子字符串s2,替换成s3。如果n=.,那么所有的子字符串都替换。
例如:
- subinstr("this is the day","is","X",1) = "thX is the day"
- subinstr("this is the hour","is","X",2) = "thX X the hour"
- subinstr("this is this","is","X",.) = "thX X thX"
如果处理中文字符,最好用替代变量usubinstr()
。
本例中,由于是变量gender1是从原变量agegrp1中提取的子符串生成,所以在函数subinstr()中可作为原变量的子字符串,这样函数中并不直接出现中文,所以也可以用函数subinstr()
来清洗。
2.1.2 利用ustrregex系列函数清洗
/*利用正规表达式将字符串拆成几个部分,分别提取变量 */
gen agegrp2=ustrregexs(1) if ustrregexm(age, "(.*)(男性|女性)(.*)")
gen gender2=ustrregexs(2) if ustrregexm(age, "(.*)(男性|女性)(.*)")
小笔记
ustrregex系列函数
- u代表unicode
- str代表string
- reg代表regular,ex代表expression,regex代表正则表达式
- m代表match匹配
- ra代表replace替换
- s代表subexpression提取子字符串
ustrregexm(s,re[,noc])
匹配
- s代表字符串,也可以是相应的变量
- re代表正则表达式
- noc,默认区分大小写。如果noc不为0,则不区分大小写。
- 字符串s中是否存在与正则表达式re相匹配的子字符串。如果有,返回1; 没有,返回0。
- ustrregexm("12345", "([0-9]){5}") = 1
- ustrregexm("de TRÈS près", "rès") = 1
- ustrregexm("de TRÈS près", "Rès") = 0
- ustrregexm("de TRÈS près", "Rès", 1) = 1
ustrregexra(s1,re,s2[,noc])
替换
- ustrregexra("très près", "rès", "X") = "tX pX"
- ustrregexra("TRÈS près", "Rès", "X") = "TRÈS près"
- ustrregexra("TRÈS près", "Rès", "X", 1) = "TX pX"
ustrregexs(n)
提取
- 在ustrregexm()之后,配合其使用,提取子字符串。
- n为非负整数,代表ustrregexm(s,re)中第n个子正则表达式对应的子字符串。
- 若n为0,则代表ustrregexm()中正则表达式对应的所有子字符串。
- 本例中将变量age的用正则表达式表达时拆成了三个部分,分别用()隔开,仅提取第1和第2部分。
3、数据整理:因子变量
3.1 encode命令转换变量类型
'twoway bar yvar xvar'命令是将数值型(y,x)数据展示成条形图。
本例中,变量agegrp1是文本型,需要转换成带标签的数值型变量,即因子变量。
/*数据转换之前,先删除总人口数据,只保留分年龄段数据*/
drop if agegrp1==""
/*包含非数字的文本型转换成因子变量*/
encode agegrp1, gen(agegrp)
/*显示agegrp的值和标签*/
label list agegrp
注意,5-9岁的位置不对,应该是2而不是10。
目前,还没有找到比较好的对标签重新排序的办法,只能采取最笨的办法重新定义。
label define agegrp 1 "0-4岁" 3 "10-14岁" 4 "15-19岁" 5 "20-24岁" ///
6 "25-29岁" 7 "30-34岁" 8 "35-39岁" 9 "40-44岁" 10 "45-49岁" ///
2 "5-9岁" 11 "50-54岁" 12 "55-59岁" 13 "60-64岁" 14 "65-69岁" ///
15 "70-74岁" 16 "75-79岁" 17 "80-84岁" 18 "85-89岁" 19 "90-94岁" ///
20 "95岁以上", replace
label list agegrp
一个可能更省事的解决办法: 生成保存label的do文件,在文件里手工改。
/*碰到r(603)错误:file mylabel.do could not be opened的解决办法*/
mkdir C:/results
cd C:/results
/*将值和标签保存到一个do文件*/
label save agegrp using mylabel.do
mylabel.do文件的内容如下:
label define agegrp 1 `"0-4岁"', modify
label define agegrp 2 `"5-9岁"', modify
label define agegrp 3 `"10-14岁"', modify
label define agegrp 4 `"15-19岁"', modify
label define agegrp 5 `"20-24岁"', modify
label define agegrp 6 `"25-29岁"', modify
label define agegrp 7 `"30-34岁"', modify
label define agegrp 8 `"35-39岁"', modify
label define agegrp 9 `"40-44岁"', modify
label define agegrp 10 `"45-49岁"', modify
label define agegrp 11 `"50-54岁"', modify
label define agegrp 12 `"55-59岁"', modify
label define agegrp 13 `"60-64岁"', modify
label define agegrp 14 `"65-69岁"', modify
label define agegrp 15 `"70-74岁"', modify
label define agegrp 16 `"75-79岁"', modify
label define agegrp 17 `"80-84岁"', modify
label define agegrp 18 `"85-89岁"', modify
label define agegrp 19 `"90-94岁"', modify
label define agegrp 20 `"95岁以上"', modify
3.2 elabel命令修改变量标签
命令elabel是外部命令,需要安装net install elabel.pkg
。
它实际上包含一组修改因子变量的值和标签的命令。
常规命令recode,只能修改值,不能修改标签,即标签不随值变动。
配合命令elabel,就能实现标签随值的修改一起变动。
elabel recode agegrp (2=3) (3=4) (4=5) (5=6) (6=7) (7=8) (8=9) (9=10) (10=2)
label list agegrp
4、作图
4.1 简图
gen pop=y2021/1000
replace pop=-pop if gender1=="男"
twoway bar pop agegrp if gender1=="男", horizontal ///
|| bar pop agegrp if gender1=="女", horizontal
4.2 标准图
#delimit ;
twoway bar pop agegrp if gender1=="男", horizontal ||
bar pop agegrp if gender1=="女", horizontal
title("2021年中国人口金字塔(抽样调查数据)")
note("数据来源:国家统计局")
xtitle("人口数(千人)")
xlabel(-80 "80" -60 "60" -40 "40" -20 "20" 0(20)80)
ytitle("年龄组") ylabel(1(1)20,valuelabel angle(0))
legend(order(1 "男" 2 "女"))
;
#delimit cr
Graph2.png
小笔记
命令换行的三种方式:
- 在行尾///
- 行尾/*,行首*/
- 段首
#delimit ;
,段尾; #delimit cr
Y轴,ylabel()默认显示yvar的值,选项valuelabel则可显示标签。
4.3 无Y轴图
gen zero=0
#delimit ;
twoway bar pop agegrp if gender1=="男", horizontal ||
bar pop agegrp if gender1=="女", horizontal ||
scatter agegrp zero, mlabel(agegrp) mlabcolor(black) msymbol(none)
title("2021年中国人口金字塔(抽样调查数据)")
note("数据来源:国家统计局")
xtitle("人口数(千人)")
xlabel(-80 "80" -60 "60" -40 "40" -20 "20" 0(20)80)
ytitle("") ylabel(none) yscale(noline)
legend(off) text(18 -50 "男") text(18 50 "女")
;
#delimit cr
Graph1.png
小笔记
- 消除Y轴: ytitle("") ylabel(none) yscale(noline)
- 构建一个垂直散点图,将年龄段刻在图正中:
gen zero=0
和scatter agegrp zero
- 添加年龄段标签,并消除点的形状和颜色:
mlabel(agegrp) mlabcolor(black) msymbol(none)
5、另一种数据结构和画法:graph hbar
- 命令twoway bar要求数据是二维的。
- 命令graph hbar要求数据是一维的。
- 选项over()将其它维度的分形放入同一图中。
- 选项by()将其它维度的分形分别作图。
5.1 整理数据
目标是生成两个性别变量的按年份分年龄段的人口数据。
year | agegrp | pop_male | pop_female |
---|
/*只保留所需的变量*/
keep y2021-y2013 gender1 agegrp
/*第一次变换,围绕历年数据,由宽表变成长表*/
/*首先,生成唯一标识的索引*/
gen no=_n
/*提取原变量y2021-y2023中的y, 保存历年人口数据*/
/*提取原变量y2021-y2023中的年份,另存在新变量year*/
reshape long y, i(no) j(year)
list
drop no
rename y pop
/*第二次变换,围绕性别,由长表变宽表*/
/*由于gender1是文本型,首先要转成数据型*/
encode gender1, gen(gender)
drop gender1
/*重新排序,方便生成reshape命令需要索引*/
sort gender year agegrp
/*不同的性别,分别生成按年份分年龄段的唯一索引*/
gen no2=_n if gender==1
replace no2=_n-160 if gender==2
reshape wide pop, i(no2) j(gender)
list
rename pop1 pop_female
rename pop2 pop_male
list
5.2 作图
replace pop_male=-pop_male
replace pop_male=pop_male/1000
replace pop_female=pop_female/1000
#delimit ;
graph hbar (asis) pop_male pop_female if year==2021,
over(agegrp, descending gap(0))
stack
legend(order(1 "男" 2 "女"))
title("2021年中国人口金字塔(抽样调查数据)")
note("数据来源:国家统计局")
ylabel(-80 "80" -60 "60" -40 "40" -20 "20" 0(20)80)
ytitle("人口数(千人)")
;
#delimit cr
小笔记
- graph hbar var 默认的统计方法是(mean), (asis)保持原数据不变。
- over()中,descending降序排列,gap()指定条形之间的间隔。
- stack堆叠,让不同维度的分形堆叠在一起。如果不设置,则分形是错开的。
- graph hbar图中的x轴实际上是它的y轴,所以人口数值和标签设置使用ylabel。
网友评论