di 4-2 // di 是display命令的简写
di 3*5
di 8/2
di 2^3
di ln(2)
di sqrt(9)
. di 4-2 // di 是display命令的简写
2
. di 3*5
15
. di 8/2
4
. di 2^3
8
. di ln(2)
.69314718
. di sqrt(9)
3
.
运算符和表达式的用途主要有两个方面:一是与generate
、replace
等命令搭配使用,用于生成新的变量或者改变变量的取值;二是和if
连用构成if语句
,将命令的作用范围限定在特定的行(观测值)。
在Stata中,运算符可以分为四大类,分别是代数运算、字符运算、逻辑运算和关系运算。
代数运算(符)包括:
符号 | 含义 |
---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
^ | 指数 |
字符运算(符)包括:
符号 | 含义 |
---|---|
+ | 字符合并 |
逻辑运算(符)包括:
符号 含义 | |
---|---|
! | 非 |
~ | 非 |
& | 与 |
| | 或 |
关系运算(符)包括:
符号 含义 | |
---|---|
< | 小于 |
>= | 大于或等于 |
<= | 小于或等于 |
!= | 不等于 |
~= | 不等于 |
为了避免优先级带来的困扰,应当用括号将优先级顺序表示出来,位于最里层括号内的表达式将被优先执行。
代数运算包括:加(+)、减(-)乘(*)、除(/)、幂指数()、负数。当遇到缺失值的运算或者结果无意义(比如除以0或者对非正数取对数)的代数运算结果均为缺失值(missingvalue)。
例如我们可以使用代数运算符号结合display
做一些简单的计算:
di 4-2 // di 是display命令的简写
di 3*5
di 8/2
di 2^3
di ln(2)
di sqrt(9)
. di 4-2 // di 是display命令的简写
2
. di 3*5
15
. di 8/2
4
. di 2^3
8
. di ln(2)
.69314718
. di sqrt(9)
3
.
也可以计算一些稍微复杂的结果,例如当x=4, y=2,求解如下表达式的取值(结果为-1):
-\frac{x+y^{x-y}}{xy}
di -1*(4+2^(4-2))/(4*2)
. di -1*(4+2^(4-2))/(4*2)
-1
对于上面的例子,我们还可以利用Stata中的scalar
(即标量)来做一些符号替换的运算
scalar x = 4
scalar y = 2
y^(x-y))/(x*y) disp -1*(x+
. scalar x = 4
. scalar y = 2
. disp -1*(x+y^(x-y))/(x*y)
-1
.
注意,标量scalr
以及相关的概念local
和macro
我们会在@sec-control 《流程控制语句》中继续介绍。但有一点需要注意,scalar
、local
和macro
都是独立于变量和二维表数据的存在,它们不会像变量一样出现在数据二维表中,想要查看它们的取值,必须通过display
命令。
除了直接针对数字进行运算外,更常见的是在变量间进行运算,或者说运算符也可以作用在变量之间,用于生成新的变量
clear
sysuse auto, clear
generate turn2 = turn^2
gen ln_turn = ln(turn)
gen sqrt_turn2 = sqrt(turn2)
gen turn_p1 = turn+1
. clear
. sysuse auto, clear
(1978 automobile data)
. generate turn2 = turn^2
. gen ln_turn = ln(turn)
. gen sqrt_turn2 = sqrt(turn2)
. gen turn_p1 = turn+1
.
当Stata中的变量出现在generate
命令赋值号右侧的表达式exp
中的时候(例如上面的generate turn2 = turn^2
),Stata中的处理实质上是一种“逐行操作”的思路:新建一个新的变量turn2 \rightarrow 对于第一行,用变量第一行的取值代入表达式,运算结果填入turn2
变量的第一行 \rightarrow 然后是第二行、第三行、依次类推,最终到最后一行。
加号(+)同样可以用在字符运算,当加号出现在两个字符或者两个字符型变量之间时,两个字符(变量)将会连接成一个字符(变量)变量之间,用于生成新的变量:
scalar a = "我喜欢使用" + "Stata" + "工作学习" // scalar是标量,在后续章节中会介绍
scalar list a
clear
set obs 3
gen y = "y"
replace y = "y1" in 1
replace y = "y2" in 3
replace y = "y3" in 2
list
clear
set obs 3
gen y = "y"
gen x = _n
tostring x, replace
gen yy = y+x
describe
list
. scalar a = "我喜欢使用" + "Stata" + "工作学习" // scalar是标量,在后续章节中
> 会介绍
. scalar list a
a = 我喜欢使用Stata工作学习
.
. clear
. set obs 3
Number of observations (_N) was 0, now 3.
. gen y = "y"
. replace y = "y1" in 1
variable y was str1 now str2
(1 real change made)
. replace y = "y2" in 3
(1 real change made)
. replace y = "y3" in 2
(1 real change made)
. list
+----+
| y |
|----|
1. | y1 |
2. | y3 |
3. | y2 |
+----+
.
. clear
. set obs 3
Number of observations (_N) was 0, now 3.
. gen y = "y"
. gen x = _n
. tostring x, replace
x was float now str1
. gen yy = y+x
. describe
Contains data
Observations: 3
Variables: 3
-------------------------------------------------------------------------------
Variable Storage Display Value
name type format label Variable label
-------------------------------------------------------------------------------
y str1 %9s
x str1 %9s
yy str2 %9s
-------------------------------------------------------------------------------
Sorted by:
Note: Dataset has changed since last saved.
. list
+------------+
| y x yy |
|------------|
1. | y 1 y1 |
2. | y 2 y2 |
3. | y 3 y3 |
+------------+
.
需要指出的是,虽然加法可以用在代数运算和字符运算中,但不能将数字与字符相加(因为这样没有任何意义)。
关系运算主要是判断大小关系、相等关系是否成立。包括:大于、大于或等于(不小于)、等于、小于或等于、小于、不等于。注意:在Stata中的符号是两个等号==
1。
di 3 > 5 // 结果是0,在Stata中,逻辑真(True)用1表示,逻辑假(False)用0来表示
di 3 <= 5 // 结果是1
// assert 3 > 5
assert 3 <= 5 // 请通过help assert查看assert命令的含义
di 3==4 // 结果是0
di 3 != 4
di 3==3 // 结果是1
. di 3 > 5 // 结果是0,在Stata中,逻辑真(True)用1表示,逻辑假(False)用0来表
> 示
0
. di 3 <= 5 // 结果是1
1
. // assert 3 > 5
. assert 3 <= 5 // 请通过help assert查看assert命令的含义
. di 3==4 // 结果是0
0
. di 3 != 4
1
. di 3==3 // 结果是1
1
.
也可以将逻辑判断的结果(0或1)赋值给变量:
clear
set obs 5
gen x = 4<5
gen y = (3==5)
gen x1 = _n
gen y1 = mod(_n,2)
gen z1 = x1==y1
. clear
. set obs 5
Number of observations (_N) was 0, now 5.
. gen x = 4<5
. gen y = (3==5)
. gen x1 = _n
. gen y1 = mod(_n,2)
. gen z1 = x1==y1
.
当数据中存在缺失值的时候,需要特别注意。因为Stata中缺失值(missing value)设定为大于任何一个数据,我们可以利用这一特点设定条件来筛选数据
例:将下面的数据按照65岁作为分界点分成两组。缺失值显然不应该包含在任何一个分组中。
age |
---|
38 |
. |
65 |
42 |
18 |
80 |
clear
input age
38
.
65
42
18
80end
list, sep(0)
gen agegroup1 = (age>=65)
gen agegroup2 = (age>=65) if age <.
gen agegroup3 = (age<65) if age < .
list
. clear
. input age
age
1. 38
2. .
3. 65
4. 42
5. 18
6. 80
7. end
. list, sep(0)
+-----+
| age |
|-----|
1. | 38 |
2. | . |
3. | 65 |
4. | 42 |
5. | 18 |
6. | 80 |
+-----+
.
. gen agegroup1 = (age>=65)
. gen agegroup2 = (age>=65) if age <.
(1 missing value generated)
. gen agegroup3 = (age<65) if age < .
(1 missing value generated)
. list
+--------------------------------------+
| age agegro~1 agegro~2 agegro~3 |
|--------------------------------------|
1. | 38 0 0 1 |
2. | . 1 . . |
3. | 65 1 1 0 |
4. | 42 0 0 1 |
5. | 18 0 0 1 |
|--------------------------------------|
6. | 80 1 1 0 |
+--------------------------------------+
.
list
命令
list, sep(0)
这行代码是在命令list
的基础上加入了seperator
的option,可以在list
输出结果的时候不打印分隔横线。大家可以通过help list
查看对应的帮助文档。也可以对比一下和list
(不加sep(0)
这一option)的结果又什么不同。
逻辑运算包括:或(|)、与(&)、非(! or ~)三种。
例如,下面的例子基于auto.dta示例数据,列示出了价格大于1万美元的所有车或者小于4000元的国产车。
sysuse auto,clear
// 注意为了避免优先级影响结果,复杂的逻辑运算应当用括号保证运算逻辑和你的预期相符
if (price > 10000) | ((price < 4000) &(foreign == 0)) br
当然也可以用于生成新的变量
仍然用上面给年龄数据分组的例子,可以用如下逻辑运算进行改写:
clear
input age
38
.
65
42
18
80end
list, sep(0)
gen group = .
replace group = 1 if (age < .) & (age >=65)
replace group = 2 if (age < .) & (age <65)
tab group
list
. clear
. input age
age
1. 38
2. .
3. 65
4. 42
5. 18
6. 80
7. end
. list, sep(0)
+-----+
| age |
|-----|
1. | 38 |
2. | . |
3. | 65 |
4. | 42 |
5. | 18 |
6. | 80 |
+-----+
. gen group = .
(6 missing values generated)
. replace group = 1 if (age < .) & (age >=65)
(2 real changes made)
. replace group = 2 if (age < .) & (age <65)
(3 real changes made)
. tab group
group | Freq. Percent Cum.
------------+-----------------------------------
1 | 2 40.00 40.00
2 | 3 60.00 100.00
------------+-----------------------------------
Total | 5 100.00
. list
+-------------+
| age group |
|-------------|
1. | 38 2 |
2. | . . |
3. | 65 1 |
4. | 42 2 |
5. | 18 2 |
|-------------|
6. | 80 1 |
+-------------+
.
在上面的代码段中,replace group = 2 if (age < .) & (age <65)
这句代码中的两个条件age < .
和age <65
有一个是多余的(在当前的数据和逻辑下可以省略一个,应该是哪一个?
在“命令结构”的章节中,我们曾介绍过分组bysort varlist
的作用是将数据集按照varlist
的取值分成若干个组,每个组内的分别执行命令。我们首先模拟生成一组数据,其中有两个变量:id
和x
。
clear
input id x
1 1.5
1 1.6
1 1.7
2 2.5
2 2.6
2 2.7
3 3.5
3 3.6
3 3.7 end
list
save "temp.dta", replace
. clear
. input id x
id x
1. 1 1.5
2. 1 1.6
3. 1 1.7
4. 2 2.5
5. 2 2.6
6. 2 2.7
7. 3 3.5
8. 3 3.6
9. 3 3.7
10. end
. list
+----------+
| id x |
|----------|
1. | 1 1.5 |
2. | 1 1.6 |
3. | 1 1.7 |
4. | 2 2.5 |
5. | 2 2.6 |
|----------|
6. | 2 2.7 |
7. | 3 3.5 |
8. | 3 3.6 |
9. | 3 3.7 |
+----------+
. save "temp.dta", replace
file temp.dta saved
.
我们可以通过如下命令,实现在针对不同id取值对应的x
,进行描述性统计:
use "temp.dta", clear
// 根据id取值对数据分组,每个分组内分别执行summarize
bysort id: summarize x
. use "temp.dta", clear
. // 根据id取值对数据分组,每个分组内分别执行summarize
. bysort id: summarize x
-------------------------------------------------------------------------------
-> id = 1
Variable | Obs Mean Std. dev. Min Max
-------------+---------------------------------------------------------
x | 3 1.6 .1 1.5 1.7
-------------------------------------------------------------------------------
-> id = 2
Variable | Obs Mean Std. dev. Min Max
-------------+---------------------------------------------------------
x | 3 2.6 .1 2.5 2.7
-------------------------------------------------------------------------------
-> id = 3
Variable | Obs Mean Std. dev. Min Max
-------------+---------------------------------------------------------
x | 3 3.6 .1 3.5 3.7
.
我们还可以将bysort
与_n
联合起来使用,用于生成组内的自增序列,以及打印显示每个组内的第一条数据:
use "temp.dta", clear
//
// 生成自增序列(变量i),以及在每个分组内生成自增序列(变量j)
gen i = _n
bysort id: gen j = _n
list
//
// 列出每个分组内的第一行数据,可以有两种方法:
list if j == 1
bysort id: list if _n == 1
. use "temp.dta", clear
. //
. // 生成自增序列(变量i),以及在每个分组内生成自增序列(变量j)
. gen i = _n
. bysort id: gen j = _n
. list
+------------------+
| id x i j |
|------------------|
1. | 1 1.5 1 1 |
2. | 1 1.6 2 2 |
3. | 1 1.7 3 3 |
4. | 2 2.5 4 1 |
5. | 2 2.6 5 2 |
|------------------|
6. | 2 2.7 6 3 |
7. | 3 3.5 7 1 |
8. | 3 3.6 8 2 |
9. | 3 3.7 9 3 |
+------------------+
. //
. // 列出每个分组内的第一行数据,可以有两种方法:
. list if j == 1
+------------------+
| id x i j |
|------------------|
1. | 1 1.5 1 1 |
4. | 2 2.5 4 1 |
7. | 3 3.5 7 1 |
+------------------+
. bysort id: list if _n == 1
-------------------------------------------------------------------------------
-> id = 1
+------------------+
| id x i j |
|------------------|
1. | 1 1.5 1 1 |
+------------------+
-------------------------------------------------------------------------------
-> id = 2
+------------------+
| id x i j |
|------------------|
1. | 2 2.5 4 1 |
+------------------+
-------------------------------------------------------------------------------
-> id = 3
+------------------+
| id x i j |
|------------------|
1. | 3 3.5 7 1 |
+------------------+
.
也可以将包含_n
的表达式与bysort
连用,用于在每个分组内修改变量的取值
use "temp.dta", clear
// 修改变量的取值
gen y1 = x+_n
bysort id: gen y2 = x+_n
list
. use "temp.dta", clear
. // 修改变量的取值
. gen y1 = x+_n
. bysort id: gen y2 = x+_n
. list
+-----------------------+
| id x y1 y2 |
|-----------------------|
1. | 1 1.5 2.5 2.5 |
2. | 1 1.6 3.6 3.6 |
3. | 1 1.7 4.7 4.7 |
4. | 2 2.5 6.5 3.5 |
5. | 2 2.6 7.6 4.6 |
|-----------------------|
6. | 2 2.7 8.7 5.7 |
7. | 3 3.5 10.5 4.5 |
8. | 3 3.6 11.6 5.6 |
9. | 3 3.7 12.7 6.7 |
+-----------------------+
.
在Stata中,变量在特定行的取值可以使用varname[#]
的形式获取,下面的代码就将变量x
特定行的数值放入了表达式中参与运算:
use "temp.dta", clear
gen z1 = _n+x[1]
bysort id: gen z2 = _n+x[1]
list
. use "temp.dta", clear
. gen z1 = _n+x[1]
. bysort id: gen z2 = _n+x[1]
. list
+-----------------------+
| id x z1 z2 |
|-----------------------|
1. | 1 1.5 2.5 2.5 |
2. | 1 1.6 3.5 3.5 |
3. | 1 1.7 4.5 4.5 |
4. | 2 2.5 5.5 3.5 |
5. | 2 2.6 6.5 4.5 |
|-----------------------|
6. | 2 2.7 7.5 5.5 |
7. | 3 3.5 8.5 4.5 |
8. | 3 3.6 9.5 5.5 |
9. | 3 3.7 10.5 6.5 |
+-----------------------+
.
1.[计算分段函数]。下面的例子生成了一个随机序列 x ,取值为1-10之间的整数。
clear
set obs 10
gen x = int(unifrom()*10)+1
list x
请按照如下定义,生成 y 变量,可以尝试尽可能多的方法来实现。 y=\left\{ \begin{aligned} 1.5x, & 0<x<=5 \\ 2x, & 5<x=10 \end{aligned} \right.
2.[Stata中_n
的妙用与缺失值处理]。下面的代码模拟生成了1个国家10年间的GDP增长率数据,其中有年份(2015年)的数据缺失。据中包括如下变量: country_id
:国家代码 year
:年份 GDPG
:国家的GDP增长率
clear all
set obs 10
set seed 10000
gen country_id = 1
gen year = _n+2010
gen GDPG = round(uniform()/5,0.001)
replace GDPG = . if year == 2015
GDPG1
,将GDP
变量中的缺失值用本国前一年的数据填充替换;GDPG2
,将GDP
变量中的缺失值用本国前一年和后一年的平均值填充;GDPG3
,将GDP
变量中的缺失值用其他年份的平均值填充(提示:需要用到下一个章节中的均值函数)(提示:变量在特定行的取值可以使用varname[#]
的形式获取,其中#
为行号。例如GDPG[1]
就是变量GDPG
在第一行的取值;_n
代表的是当前行号,GDPG[_n]
和GDPG[_n-1]
可以获取当前GPDG
变量的当前行与上一行取值)
clear all
set obs 4000
set seed 10000
gen country_id = mod(_n,200)+1
sort country_id
codebook country_id
bysort country_id: gen year = _n+2002
gen random = uniform()
gen GDPG = round(uniform()/5,0.001)
sort country_id random
bysort country_id: replace GDPG = . if _n == 1
sort country_id year
drop random
请编写代码,实现如下两种缺失值处理的思路:
GDPG1
,将GDP
变量中的缺失值用本国前一年的数据填充替换;GDPG2
,将GDP
变量中的缺失值用本国前一年和后一年的平均值填充;GDPG3
,将GDP
变量中的缺失值用本国其他年份的平均值填充(提示:需要用到下一个章节中的均值函数)(提示:可以使用bysort
分组操作功能)
单个等号是赋值号,和generate, egen, replace等命令连用,在编程语言中是一个常见的设定。↩︎