事情的起因是,一直以来想写个自动筛选控制变量的命令,在查阅资料之余偶然间发现了这个开源项目。但是使用之后发现程序本身还有不少需要完善的地方,并且此项目已经很久没人更新了,因此,打算自己动手进行完善。在使用的过程中,根据自己的需求完善了部分内容。具体思路和代码如下:
想法很简单,直接遍历所有的控制变量集,通过tuples命令实现,下边简单介绍下改命令的使用方式
sysuse auto.dta, claer // 导数数据 tuples weight length turn displacement , min(2) max(4) // 返回子集,其中每个子集包括2-4个元素
运行完程序之后,我们重点需要关注其返回值
dis "`ntuples'" // nutuples满足条件的子集个数 dis "`tuple1'" // 第一个子集 -- "`tuple`i''"调用第i个
运行完之后,我们可以发现,满足条件的子集共有11个,且第1个为turn displacement。
于是,我们可以将这些子集放入逐个进行回归。并通过一些条件语句,将满足条件的子集储存下来。最后即可得到满足所有条件的集合。同时,有些时候我们需要同时对多个方程进行限定,需对程序进行些许的改动。大致思路是:在多方程程序中,调用单方程的返回结果,并取交集,即可实现。
2. 完整代码
这里提供附带详细注释的,用于筛选控制变量的完整代码。大家在使用的过程中,可以将其复制到stata的do文件编辑器中,并保存为.ado格式即可自行进行调用。
第一个ado程序:对于单个方程进行筛选
/* 新增内容:1. 增加能让变量不显著的判定 2. 设置不用在意显著性和符号的控制变量集合 3. 支持分组回归 4. 支持因子变量和时间序列变量 有待补充: 1. 需要对多个变量的符号进行控制(如交互项、二次项等) 2. 计算速率优化 -- 只用计算系数可以不调用reg 3. 将核心变量和显著性输入到一个参数中[]*/cap program drop select_vars // 删除缓存中的文件 program select_vars, byable(recall) rclass // 设定程序返回值类型为r-classversion 16.0 // 设定版本为stata 16syntax varlist(min = 2 fv ts) [if] [in], Control(varlist fv ts) MODel(string) /// [Must(varlist fv ts) min(int 1) max(int 10) Times(real 10) /// P_value(real 0.05) SYmbol(string) /// XSEcend(varlist fv ts max = 1) PSEcend(real 0.05) SYSEcend(string) /// Negative *] // 输入参数************* * 变量说明: * ************* * varlist -- 变量集 至少两个数值型(numeric)变量 * [if] [in] -- 可选条件 if &in * control -- 控制变量集 varlist形式 * model -- 回归模型 str * must -- 必有的变量 varlist形式 * min -- 最少子集数 int形式 默认为1 * max -- 最多子集数 int形式 默认为10 * time -- 多久显示一次进度条 默认为10% * p_value -- 核心解释变量临界P值 real number形式 默认为0.05 * symbol -- 核心解释变量符号 string形式 * xsecend -- 第二个需要关注的变量 * psecend -- 第二个变量的p值 * sysecend -- 第二个变量的符号 * negative -- 可选调节参数 选择时变量不显著 * fv -- factor variables * ts -- time-series operators************* * 预先处理: * ************* gettoken y x: varlist // 将varlist分为y(第一个)和x(剩余的) gettoken x_core x_other: x // 将x分为核心(x_core)和其他(x_other)qui tuples `control', min(`min') max(`max') // 设置控制变量为全集 * min -- 子集最少元素数 * max -- 子集最多元素数matrix results = J(`ntuples', 1, 0) // 生成`ntuples'行1列的0矩阵 matrix colnames results = "flag" // 定义列为flag local n 0 // 统计满足条件的方程数量 local con_list // 统计满足条件的控制变量集合 * 注意:negative并不影响二次项 -- 若需要二次项不显著,则不加二次项即可 if "`negative'" == ""{ local comp "<" // 正常情况下P值越小越好 } else{ local comp ">" // 反之则需要P值越大越好 } if ("`symbol'" == "" &"`sysecend'" == ""){ // 均不限定 local symbol_judge "1" // 不加符号限定恒为真 } else if("`symbol'" != "" &"`sysecend'" == "" ) { // 只限定核心解释 local symbol_judge "_b[`x_core'] `symbol' 0" // 否则需判定其与0之间的关系 } else if("`symbol'" == "" &"`sysecend'" != "") { // 只限定第二变量 local symbol_judge "_b[`xsecend'] `sysecend' 0" } else if("`symbol'" != "" &"`sysecend'" != ""){ local symbol_judge "_b[`x_core'] `symbol' 0 &_b[`xsecend'] `sysecend' 0" } if("`xsecend'" == ""){ // 若没有输入第二变量 local psecend_judge "1" // 恒为真 } else{ // local psecend_judge = "`p_sece' <`psecend'" // 这样不能读入p_sece会报错 local psecend_judge = "2 * ttail(e(df_r),abs(_b[`xsecend']/_se[`xsecend'])) <`psecend'" }marksample touse // 使用touse记录有效样本* >>>>>>主体开始 <<<<<<preserve // 暂时保存样本qui keep if `touse' // 只保存有效样本,且不显示 local j 1 forvalues i = 1/`ntuples'{ // `ntuples'返回全集的子集个数* 设置进度条 if(floor(`i'/`ntuples' * 100)/(`times') >= `j' | `i' == `ntuples'){ local percent = `j' * `times' // 记录百分比 if(`i' == `ntuples'){ local percent = 100 } dis "...`percent'%" _c // _c不换行 local j = `j' + 1 }local x_con "`tuple`i''" // 设置控制变量 qui `model' `y' `x_core' `xsecend' `x_other' `must' `x_con', `options'* 回归结果的分析 local p_core = 2 * ttail(e(df_r),abs(_b[`x_core']/_se[`x_core'])) // 统计P值 // 根据t值计算其对应的p值if(`p_core' `comp' `p_value' &`psecend_judge' &`symbol_judge'){ // 如果满足条件 local n = `n' + 1 // 满足条件的方程数量 + 1 local con_list = "`con_list'" + ", " + "`x_con'" // 添加符合的集合 matrix results[`i', 1] = 1 // 结果满足 } else{ matrix results[`i', 1] = 0 // 结果不满足 }} restore // 恢复原始样本* >>>>>>主体结束 <<<<<<local con_list = subinstr("`con_list'", ", ", "", 1) // 删除第一个", " local list = "`con_list'" // 存储控制变量集 local con_list: subinstr local con_list ", " "+", all // 将所有的逗号替换成 + local con_list: subinstr local con_list " " "-", all // 将所有的空格替换成 - local con_list: subinstr local con_list "+" " ", all // 将所有的加号替换成************* * 结果返回: * ************* * 矩阵的返回 return matrix sets = results // 返回结果矩阵* 值的返回 return scalar num_set = `n' // 返回满足条件的集合个数* 暂元返回 return local y = "`y'" // 返回被解释变量 return local x_core = "`x_core'" // 返回核心解释变量 return local list = "`list'" // 返回满足条件的控制变量集 -->用于展示 return local con_list = "`con_list'" // 返回满足条件的控制变量集 -->用于调用end
第二个ado程序:同时满足多方程
cap program drop select_multi program select_multi, rclass syntax anything [if] [in], [List]local equations = subinstr(subinstr(subinstr("`anything'","[","",1),"]","",.),"[","",.) * 程序效果: 将输入的 "[]" 中的内容用 "" 分割 * step 1. eq1 = subinstr("`anything'", "[", "", 1) -- 删除输入参数的第一个 "[" * step 2. eq2 = subinstr(`eq1', "]", "", .) -- 删除eq1中的所有的 "]" * step 3. eq3 = subinstr(`qe2', "[", "", .) -- 将eq2中所有的 "[" 替换为 "" local equations: list retokenize equations // 去掉多余空格 local eqnum = ustrlen("`equations'") - ustrlen(subinstr("`equations'", "", "", .)) + 1 * 方程数 = 分号数 + 1 = 有分号的长度 - 无分号的长度 + 1 tokenize "`equations'", parse("") // 将方程按照分号分割 其中第i个方程用 `i'表示 forvalues i = 1/`eqnum'{ // 遍历方程 local j = 2*`i' - 1 // 由于分号会占位,第2*i-1个位置为方程位置 local equation = "``j''" // j = 1 时 `j' = 1 -- 暂元的引用 ``j'' = `1' 表示第一个方程 dis "方程`i'进度:" _c select_vars `equation' // 执行子程序 if(`i' == 1){ local com_lists = r(con_list) // 满足条件的集合 } else{ local con_list = r(con_list) // 结果存放到con_list local com_lists: list com_lists &con_list // 取交集 } local solunum = r(num_set) // 统计满足条件的个数 dis " -- 有`solunum'个集合" } local re_lists = "`com_lists'" local com_lists :subinstr local com_lists " " ", ", all local com_lists :subinstr local com_lists "-" " ", all local listnum = ustrlen("`com_lists'") - ustrlen(subinstr("`com_lists'", ",", "", .)) + 1 if ("`list'" != ""){ // 需要展示结果 dis "满足所欲条件的结果包括:" tokenize "`com_lists'", parse(",") // 按逗号分割 forvalues i = 1/`listnum'{ local j = 2*`i' - 1 dis "solution`i': ``j''" // 展示第i个结果 } } return scalar lists_num = `listnum' // 满足条件的集合数 return local list = "`re_lists'" // 满足条件的集合 -- 用于返回 return local com_lists = "`com_lists'" // 满足条件的集合 -- 用于展示 end
第三个do文件:程序调用样例
do "select_vars.ado" // 调用select_vars文件 do "select_multi.ado" // 调用select_multi文件sysuse auto, clear // 导入数据 cls // 清屏local y price // 被解释变量 local x rep78 // 解释变量local x_con "turn trunk weight headroom" // 控制变量 local x_must "i.foreign" // 必有变量* 单方程筛选 select_vars price rep78 , c(`x_con') mod(reg) m(`x_must') n p(0.5) t(25) min(2) symbol(">") r /* 其中price为y,rep78为所关心的核心x c -- 需要筛选的控制变量 mod -- 选择普通回归模型reg命令进行回归 m -- 必须存在的控制变量 n -- p值需要大于临界值 p -- p的临界值为0.05 t -- 进度条间隔为25% min -- 可选控制变量至少得有2个 symbol -- 核心解释变量的系数需大于0 r -- 返回稳健性标准误 */ return list // 返回列表 dis "`r(list)'" // 展示满足条件的集合* 多方程筛选 select_multi [price length, c(`x_con') mod(reg) m(`x_must') n symbol(<) min(2) r] /// [price rep78, c(`x_con') mod(reg) m(`x_must') n min(2) r] // "[]" 中为需要限定的单方程模型 "///" 表示换行 return list dis "`r(com_lists)'" // 展示满足条件的集合
3. 使用 *** 作
注意:如果自己懂得外部命令的手动安装,以及路径设置的基本知识,可以自行发挥。如果不是很清楚这方面的知识,请严格安装以下要求进行调用,为保证后续样例代码能运行,需将ado文件保存为样例名,并和样例文件放在同一个文件夹下。
1.打开stata并创建新的do文件
创建空白do文件
2.将代码另存为文件
将代码另存为
需要注意的是,对于第1和2个程序,文件名(右边红框)需和program后的程序名(左边红框)相同。对于第3个程序,另存为什么名字则无所谓。
3.保存完成:我们能够得到这样的三个文件
保存完成
注意:三个文件需要在同一个文件夹下;并且test是do文件,另外两个为ado文件
4.运行test文件(也就是提供的程序3)
输出结果演示
单方程变量的结果存放在r(list)暂元中、多方程存放在r(com_lists)中,其中使用,对满足条件的子集进行分割。
maxcpuclockoverride不启作用suggestion: ONLY enable "Max CPU Boost Clock Override" if you are close (within ~ 25MHz) of saturating the factory Fmax*, and upon saturation, only increase the Fmax by the minimum available amount at the time. Unless saturated, unnecessarily large increases will only deteriorate the result. * (3600 = 4.200GHz, 3600X = 4.400GHz, 3700X 4.400GHz, 3800X = 4.550GHz, 3900X = 4.650GHz, 3950X = 4.725GHz, for other SKUs check HWiNFO). The bios releases featuring the newly added "PBO Fmax Enhancer" will also increase the available Fmax margin from 200MHz to 400MHz, to ensure that SKUs with a low total Fmax ceiling (factory + offset), such as 3600 will have a sufficient headroom available. The initial test versions will still have the standard +200MHz limit however, the said change has already been implemented in the code.A suggestion: In some cases, the results can be improved even further, by increasing the "Precision Boost Overdrive Scalar" value from the default 1x setting. This option directly affects the silicon reliability and because of that, it should be only increased from the stock if the performance improvements justify it (which they frankly, seldom do). Personally, I would never increase it to higher than 4x. The current level can be checked with HWiNFO (Central Processors >>CPU PBO Scalar (Reliability Reduction)).
”
解释一下重点:
如果在不开Max CPU Boost Clock Override时,可以观察到核心最高频率可以达到你CPU的Fmax-25MHz到Fmax这个范围(称为“饱和”),才需要动Max CPU Boost Clock Override,否则调高Max CPU Boost Clock Override只会使性能下降。举个例子,3800X的Fmax是4550MHz,减去25MHz就是4525MHz,即4525-4550MHz。Fmax可以用hwinfo看。如果频率确实饱和了,建议设置为最低增量,即+25MHz。
某些时候提高Scalar可能提高性能,但它会影响cpu的可靠性。除非观察到提高Scalar可以提高性能,否则没必要调。作者从不设置超过4X。
下面是我的测试和观点,不保证正确。
关于Precision Boost Overdrive Scalar,我实测发现Scalar对R20跑分性能影响微乎其微,最多1分,也无法观察到它对频率行为的影响。以下内容均在Scalar=4X的设置下进行。
Max CPU Boost Clock Override,也就是auto oc(这么称呼是因为如果调了该选项,ryzen master里显示的模式叫 自动超频 auto overclocking),在3800X,X570-E平台上有如下选项。
Max CPU Boost Clock Override
我测试了0 25 75 100 200MHz这些值。这些值对R20跑分几乎没有影响,它们只会在极轻负载时瞬时出现,所以只能观察频率行为。
测试场景只有两个,在桌面上划动鼠标,和守望先锋主界面。
首先按作者说的,测试0MHz,1X状态下,频率是否饱和。在桌面划动鼠标,可以发现所有核心都可以很快到达45.25x,饱和。
设置为25MHz,划动鼠标,每个核心boost瞬间较多的看到45.25x,较少是45.00x。游戏内可以看到多个核心上到45.75x。
那俩45.25x的是体质排名最靠后的两个。
设置为75MHz,划动鼠标,每个核心boost瞬间较多的看到45.00x,较少是45.25x,正好和之前相反。游戏内可以看到46.25x。
上述超过45.50x的频率,在ow主界面很容易看到,不需要一直等,一分钟之内就可以看到有核心boost上去。但进入对战后,多核心占用率高了,就不会再有超过45.25x的频率了。
设置为100MHz,划动鼠标时的45.25x更少了,桌面上很难看到46.25x,游戏内很少看得到超过45.50x的频率了。
设置为200MHz,无论是游戏内,还是桌面,都非常非常非常难看到超过45.50x的频率。而且桌面上划动鼠标,44.75x出现的比较多。
所以说这个值并不是越高越好。
原理只能猜一下,大概率不靠谱。
每个核心体质不同,有各自的boost上限频率。如果auto oc值设置的过高,那些没有能力上这么高频率的核心,根本就不会尝试auto oc频率,而且为了让少数几个核心上到高频,其它核心就会boost的低一些。而且即使是体质好的核心,上那么高频率,也会快速触发内部限制(应该是瞬时电流或者局部温度),导致降频,更难抓到,毕竟zen2的频率切换速度是1ms。
不同体质的cpu,最佳值应该不一样。对于我这个3800x,最佳值应该是25MHz或75MHz。
但我观察,如果设置75MHz,游戏内只有8和2号核心(体质序列的前两个)能看到46.25MHz。如果设置25MHz,除了1和5(体质序列里最后两个),其它核心都能上到45.75x,而且看到45.75x的频率比75MHz状态多一些。。。
所以最后我选择了25MHz。
(如果有50MHz选项,很可能是最好用的)
考虑到auto oc频率只能在非常轻度负载时候才能出现,只对看网页,办公,游戏UI时的性能有微乎其微的提升,所以在这个值上较真并没有太大意义。
另外在我的3800X和X570-E平台上,升级最新版的芯片组驱动,没有发现额外的电源计划,依然只有amd ryzen高性能和ryzen平衡。以上测试都在ryzen高性能计划下进行。
综上,我个人的观点是这样的:
如果你在开fmax,开pbo,0mhz,1x时候在桌面划1分钟鼠标,可以看到hwinfo里所有核心都上过你cpu (fmax-25mhz)到fmax这个频率范围,那么设置为25mhz 4x。如果到不了,就设置0mh
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)