问题描述
我的数据集中的观察数据包含每个球员的移动历史。我想计算一下在游戏的上半场和下半场,连续几步棋的长度(2步、3步和3步以上)。序列不能重叠,即序列1111应视为长度为4的序列,而不是长度为2的2个序列。也就是说,对于这样的观察:
+-------+-------+-------+-------+-------+-------+-------+-------+
| Move1 | Move2 | Move3 | Move4 | Move5 | Move6 | Move7 | Move8 |
+-------+-------+-------+-------+-------+-------+-------+-------+
| 1 | 1 | 1 | 1 | . | . | 1 | 1 |
+-------+-------+-------+-------+-------+-------+-------+-------+
…应生成以下变量:
Number of sequences of 2 in the first half =0
Number of sequences of 2 in the second half =1
Number of sequences of 3 in the first half =0
Number of sequences of 3 in the second half =0
Number of sequences of >3 in the first half =1
Number of sequences of >3 in the second half = 0
关于如何继续此任务,我有两种可能的选择,但这两种选择都不会导致最终解决方案:
选项1:详细说明Nick使用字符串的策略建议(Stata: Maximum number of consecutive occurrences of the same value across variables),我连接了所有"Move*"变量,并尝试确定子字符串的起始位置:
选项1有几个问题:(1)它不考虑序列重叠的情况("1111"被认为是2的2个序列)(2)它缩短了生成的字符串test2,因此X的位置不再对应于test1中的起始位置(3)如果我需要检查长度大于3的序列,则不考虑可变长度的子串。选项2:创建一个辅助变量集,以标识某个固定预定义长度的1的连续集合的起始位置。在前面的示例的基础上,为了计算长度为2的序列,我尝试获得的是一个辅助变量集,如果序列在给定的移动时开始,则等于1,否则等于0:+-------+-------+-------+-------+-------+-------+-------+-------+
| Move1 | Move2 | Move3 | Move4 | Move5 | Move6 | Move7 | Move8 |
+-------+-------+-------+-------+-------+-------+-------+-------+
| 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
+-------+-------+-------+-------+-------+-------+-------+-------+
我的代码如下所示,但当我尝试重新开始计算连续出现的事件时,它崩溃了:
quietly forval i = 1/42 {
gen temprow`i' =.
egen rowsum = rownonmiss(seq1-seq`i') //count number of occurrences
replace temprow`i'=rowsum
mvdecode seq1-seq`i',mv(1) if rowsum==2
drop rowsum
}
有人知道解决这项任务的方法吗?
推荐答案
假设连接所有移动的字符串变量all
(名称test1
很难让人联想到)。
第一次尝试:按字面意思理解您的示例
从您的8步棋的例子来看,游戏的上半场是1-4步,下半场是5-8步。因此,对于每一半来说,只有一种方法有>3步,即有4步。在这种情况下,每个子字符串将"1111"
,并且计数减少到测试一种可能性:
gen count_1_4 = substr(all, 1, 4) == "1111"
gen count_2_4 = substr(all, 5, 4) == "1111"
扩展此方法,只有两种方法可以按顺序进行3个动作:
gen count_1_3 = inlist(substr(all, 1, 4), "111.", ".111")
gen count_2_3 = inlist(substr(all, 5, 4), "111.", ".111")
在类似的风格中,不能在游戏的每一半中有两个连续的2个移动的实例,因为这将符合4个移动的资格。因此,每半部分最多有一个2步连续移动的实例。该实例必须匹配"11."
或".11"
这两个模式中的任何一个。".11."
是允许的,因此两者都包括。正如刚刚提到的,我们还必须排除任何与3个动作序列相匹配的错误。gen count_1_2 = (strpos(substr(all, 1, 4), "11.") | strpos(substr(all, 1, 4), ".11") ) & !count_1_3
gen count_2_2 = (strpos(substr(all, 5, 4), "11.") | strpos(substr(all, 5, 4), ".11") ) & !count_2_3
如果找到匹配项,则每个strpos()
评估的结果将为正,如果任一参数为正,则(arg1|
arg2)将为真(1)。(对于Stata,非零在逻辑计算中为真。)这在很大程度上是针对您的特定问题量身定做的,但也不会因此而变得更糟。
附注:我没有努力理解您的代码。您似乎混淆了subinstr()
和strpos()
。如果你想知道位置,subinstr()
无能为力。
第二次尝试
您的最后一段代码暗示您的示例具有很大的误导性:如果可以有42个移动,则上面的方法无法毫无困难地扩展。你需要一种不同的方法。
假设字符串变量all
可以是42个字符。我将搁置上半场和下半场之间的区别,这可以通过修改这种方法来解决。简单来说,只需将历史分为两个变量,一个用于前半部分,另一个用于后半部分,然后重复两次这种方法。
您可以通过以下方式克隆历史记录
clonevar work = all
gen length1 = .
gen length2 = .
并设置count
变量。这里count_4
将包含4个或更多的计数。
gen count_4 = 0
gen count_3 = 0
gen count_2 = 0
首先,我们寻找长度为42,...,2的移动序列。每次我们找到一个移动序列,我们就把它清空并增加计数。 qui forval j = 42(-1)2 {
replace length1 = length(work)
local pattern : di _dup(`j') "1"
replace work = subinstr(work, "`pattern'", "", .)
replace length2 = length(work)
if `j' >= 4 {
replace count4 = count4 + (length1 - length2) / `j'
}
else if `j' == 3 {
replace count3 = count3 + (length1 - length2) / 3
}
else if `j' == 2 {
replace count2 = count2 + (length1 - length2) / 2
}
}
这里的重要细节是
如果我们删除一个模式(重复的实例)并测量长度的变化,我们就删除了该模式的(长度变化)/(模式的长度)实例。所以,如果我查找"11",发现长度减少了4,我只找到了两个实例。
向下工作,删除我们发现的内容,确保我们不会发现误报,例如,如果删除"1111111",我们就不会发现后面包含的"111111"、"11111"、……、"11"。
删除意味着我们应该在克隆上工作,以便不破坏感兴趣的内容。
这篇关于Stata:计算预定义长度的连续出现的次数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!