美文网首页
人口金字塔:从数据清洗到制图(STATA)

人口金字塔:从数据清洗到制图(STATA)

作者: 冬之心 | 来源:发表于2023-03-13 19:51 被阅读0次

    目标

    制作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=0scatter 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。
    Graph3.png

    相关文章

      网友评论

          本文标题:人口金字塔:从数据清洗到制图(STATA)

          本文链接:https://www.haomeiwen.com/subject/asocrdtx.html