Stata 中如何进行跨行观测值比较?

有个小伙伴问到了这样一个问题:

对于 Stata 数据集中的观测值 A,如果数据集中能找到一个和 A 的 id 相同,class 相同的观测值 B,且 A 的 year 减 B 的 year 的差值大于 0 小于 5,则将 A 标记为 1;如果找不到这样的观测值 B,则将 A 标记为 0。

她还提供了一个示例数据:

clear all
input id year num str10 class
000001 2013 1 "G06Q"
000001 2013 2 "G06Q"
000001 2013 3 "G06Q"
000001 2016 4 "G06Q"
000001 2016 5 "G06Q"
000001 2016 6 "G06Q"
000001 2016 7 "G06Q"
000001 2017 8 "G06Q"
000001 2017 9 "G06Q"
000001 2017 10 "G06Q"
000001 2017 11 "G06Q"
000001 2017 12 "G06Q"
000001 2017 13 "G06Q"
000001 2017 14 "G06Q"
000001 2017 15 "G06Q"
000001 2018 16 "G06Q"
000002 2005 17 "E04H1/00"
000002 2006 18 "B65F"
000002 2006 19 "E03C1/12"
000002 2006 20 "B65F"
000002 2006 21 "B65F"
end

我们再生成一个 ID 变量以更好的区分各个观测值:

gen ID = _n

save data, replace

为了进行跨行比较,我们需要生成一个交叉数据集:

use data, clear
ren ID ID2
ren id id2
ren year year2
ren num num2
ren class class2
cross using data

由于筛选条件里面有对年份差值的比较,所以我们再生成一个 yeardiff 变量:

gen yeardiff = year - year2

这样我们就可以筛选出符合要求的观测值了:

keep if id == id2 & class == class2 & (yeardiff > 0 & yeardiff < 5) & ID != ID2

然后我们再把结果和最初的 data.dta merge 起来就可以了:

keep ID id year num class
duplicates drop ID, force
gen kind = 1
merge 1:1 ID using data
gsort ID
drop ID
replace kind = 0 if missing(kind)
drop _m
save res, replace

这样我们就解决了这个问题。

另外我还把这个问题问了 chatGPT,它给出了这样的答案:

*- 可以使用以下stata代码实现要求:
sort id year class //按照id、year和class排序
gen mark = 0 //新建一个标记变量,初值为0
forvalues i = 1/`=_N' {
local id1 = id[`i']
local year1 = year[`i']
local class1 = class[`i']
forvalues j = `i'-1/-1 {
if (id[`j'] == `id1' & class[`j'] == "`class1'" & year[`i'] - year[`j'] > 0 & year[`i'] - year[`j'] < 5) {
replace mark = 1 in `i'
break
}
}
}
list id year num class mark //查看结果
*- 代码解释:
*- 首先对数据集按照id、year和class排序,方便后面的循环操作。
*- 然后新建一个标记变量mark,初值为0。
*- 使用forvalues循环遍历数据集中的每一个观测值A,对于每一个A,再用一个forvalues循环
*- 遍历A之前的所有观测值B(因为B必须在A之前),如果找到一个和A的id相同,class相同的
*- 观测值B,且A的year减B的year的差值大于0小于5,则将A标记为1,并跳出循环。如果循环完
*- 所有的B都找不到符合要求的观测值,则将A标记为0。
*- 最后输出结果,查看标记变量mark的取值。

这段代码的思路看起来也是正确的,也就是通过两层循环判断,但是它给的代码是运行不通的:

*- 这段代码是错误、运行不通的:
use data, clear
sort id year class //按照id、year和class排序
gen mark = 0 //新建一个标记变量,初值为0
forvalues i = 1/`=_N' {
local id1 = id[`i']
local year1 = year[`i']
local class1 = class[`i']
forvalues j = `i'-1/-1 {
if (id[`j'] == `id1' & class[`j'] == "`class1'" & year[`i'] - year[`j'] > 0 & year[`i'] - year[`j'] < 5) {
replace mark = 1 in `i'
break
}
}
}
list id year num class mark

我们来改改试试,首先语法错误的点在于:

*- 非可运行代码:
forvalues j = `i'-1/-1 {
......
}

i - 1应该这样表示:

*- 非可运行代码:
forvalues j = `=`i'-1'/-1 {
......
}

不过这种循环也是错误的(只是能运行通顺了),下面是我修改正确后的代码:

use data, clear
gen mark = 0
forvalues i = 1/`=_N' {
local id1 = id[`i']
local year1 = year[`i']
local class1 = class[`i']
local ID1 = ID[`i']
forvalues j = 1/`=_N' {
if (id[`j'] == `id1' & class[`j'] == "`class1'" & ///
`year1' - year[`j'] > 0 & `year1' - year[`j'] < 5 & ///
ID[`j'] != `ID1') {
replace mark = 1 in `i'
break
}
}
}

不过还是不建议使用这种嵌套循环,非常绕人。

点击这里跳转到 RStata 短书平台获取附件:Stata 中如何进行跨行观测值比较?

评论