Stata:计算预定长度的连续出现次数

时间:2013-10-07 16:38:33

标签: stata

我的数据集中的观察结果包含每个玩家的移动历史记录。我想计算游戏的前半部分和后半部分中一些预定长度(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 *”变量,并尝试识别子字符串的起始位置:

egen test1 = concat(move*)
gen test2 = subinstr(test1,"11","X",.) // find all consecutive series of length 2

选项1有几个问题: (1)它不考虑具有重叠序列的情况(“1111”被识别为2的2个序列) (2)它缩短了得到的字符串test2,使得X的位置不再对应于test1中的起始位置 (3)如果我需要检查长度大于3的序列,它不考虑子串的可变长度。

选项2:创建一组辅助变量,以识别某些固定预定义长度的1的连续集(集)的起始位置。在前面的例子的基础上,为了计算长度为2的序列,我想要得到的是一组辅助变量,如果在给定的移动中开始的序列将等于1,否则为零:

+-------+-------+-------+-------+-------+-------+-------+-------+
| 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
}

有谁知道解决任务的方法?

1 个答案:

答案 0 :(得分:1)

假设一个字符串变量连接所有移动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,在逻辑评估中非零是正确的。)

这非常适合您的特定问题,但不会更糟。

P.S。我没有努力去理解你的代码。您似乎将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 
       }
  }

这里的重要细节是

  1. 如果我们删除(模式的重复实例)并测量长度的变化,我们刚刚删除(更改长度)/(模式的长度)实例。因此,如果我查找“11”并发现长度减少了4,我只发现了两个实例。

  2. 向下工作并删除我们发现的内容可确保我们找不到误报,例如如果“1111111”被删除,我们后来找不到包含在其中的“111111”,“11111”,......,“11”。

  3. 删除意味着我们应该在克隆上工作,以免破坏感兴趣的内容。