一个循环内数据子集的变量采用的唯一值的数量

问题描述

我正在用Stata写一些代码,但是当前的迭代效率很低。我知道如果我要在 python/pandas 中编写它以使事情变得更好,我将如何改变事情,但我不确定如何做到这一点/如果在 Stata 中可能。出于工作流程的原因,我需要将此代码保留为 Stata 语言。

基本问题是我有两个分类变量catacatb。每个都可以具有数千个值。我想通过仅在同一 catb 中至少出现两个观察值时保留一个类别来减少 cata 中的类别数量。我使用 egen group() 来确保类别是数字,最大值是两个变量的类别数。

我当前的代码如下所示:

foreach var in cata catb {
    qui sum `var'
    local `var'_max = r(max)
}
local list
forvalues i=1/`cata_max' {
    if mod(`i',10)==0 di `i' " of " `cata_max'
    forvalues i=j/`catb_max' {
        qui count if cata==`i' & catb==`j'
        if r(N)>1 local list `list' `j'
    }
}
gen mask=0
foreach i in `list' {
    replace mask=1 if catb==`i'
}
replace catb=-1 if mask==0

我相信这段代码可以运行并且会起作用,但我估计在大约 16 小时内完成大约 10 万次观察的数据。

其中有问题的部分是嵌套循环,因为我正在遍历 catb 的所有可能值,即使只有大约 10ish 出现(cata 平均大小为 20-30)。我只想选择 catb 的相关部分在嵌套循环中循环,而不是 catb 的所有可能值。在熊猫/蟒蛇我会做这样的事情:

val_list=[]
for i in df['cata'].unique():
    df_temp=df[df['cata']==i]
    rel_vals=df_temp['catb'].unique()
    for j in rel_vals:
        if len(df_temp[df_temp['catb']])>1:
            val_list+=[j]

虽然不是很好的代码,但说明了仅循环 catb 的相关值的想法。这应该可以将第二个循环中花费的时间减少约 300 倍,即使在查找唯一值时存在一些开销,这也会产生显着的加速。

如何在这个循环结构中只获得一个变量的唯一值?我的搜索只产生 this 之类的帖子,这是一种计算变量唯一值的迂回方法(在我的情况下,它不适用于数据子集的唯一值)。

示例:

index   cata    catb    catb2
0       A       1       -1
1       B       2       2
2       C       1       -1
3       A       2       2
4       B       2       2
5       C       2       2

说明:catb2catb 的新版本,它取代了类别“1”,因为没有cata 中出现多个“1”。类别“2”保持不变,因为它在 cata B.

中出现了两次

解决方法

如果我理解正确,您希望 dropcatacatb 组合只出现一次:

 bysort cata catb: drop if _N == 1 
,

我认为即使只循环观察 catb 的值也是一种相对低效的方法。我建议使用 duplicates 命令(为了扩展,我建议使用非常有用的 gtools 包中的 gduplicates 命令 - ssc install gtools)。下面是一个实现示例:

clear
input index str1(cata) catb    
0 "A" 1       
1 "B" 2       
2 "C" 1       
3 "A" 2       
4 "B" 2       
5 "C" 2       
end

// Tags unique pairs of cata and catb with N-1
// i.e. a value of 2 means that there are 3 observed pairs given cata and catb
duplicates tag cata catb,gen(observed_pairs)

// The maximum unique pairs that catb is a part of
egen catb_max = max(observed_pairs),by(catb)

// Will make new catb2 that is -1 if a catb at most 1 in each cat a
gen catb2 = catb
replace catb2 = -1 if catb_max == 0