SAS-重复数据步骤以求解值
如果重复次数取决于数据步骤的结果,是否可以重复一个数据步骤多次(就像在%do-%while循环中一样) 我有一个带有数字变量a的数据集。我计算一个新变量result=min(1,a)。我希望结果的平均值等于一个目标,我可以通过用常数k缩放变量a来达到这个目标。这是对k的求解,其中target=average(min(1,A*k))——其中k和target是常数,A是列表 以下是我到目前为止的情况:SAS-重复数据步骤以求解值,sas,Sas,如果重复次数取决于数据步骤的结果,是否可以重复一个数据步骤多次(就像在%do-%while循环中一样) 我有一个带有数字变量a的数据集。我计算一个新变量result=min(1,a)。我希望结果的平均值等于一个目标,我可以通过用常数k缩放变量a来达到这个目标。这是对k的求解,其中target=average(min(1,A*k))——其中k和target是常数,A是列表 以下是我到目前为止的情况: filename f0 'C:\Data\numbers.csv'; filename f1 'C
filename f0 'C:\Data\numbers.csv';
filename f1 'C:\Data\target.csv';
data myDataSet;
infile f0 dsd dlm=',' missover firstobs=2;
input A;
init_A = A; /* store the initial value of A */
run;
/* read in the target value (1 observation) */
data targets;
infile f1 dsd dlm=',' missover firstobs=2;
input target;
K = 1; * initialise the constant K;
run;
%macro iteration; /* I need to repeat this macro a number of times */
data myDataSet;
retain key 1;
set myDataSet;
set targets point=key;
A = INIT_A * K; /* update the value of A /*
result = min(1, A);
run;
/* calculate average result */
proc sql;
create table estimate as
select avg(result) as estimate0
from myDataSet;
quit;
/* compare estimate0 to target and update K */
data targets;
set targets;
set estimate;
K = K * (target / estimate0);
run;
%mend iteration;
我可以通过运行几次%iteration
来获得所需的答案,但理想情况下,我希望运行迭代直到(target-estimate0<0.01)
。这可能吗
谢谢 前几天我也遇到了类似的问题。下面是我使用的方法,您需要将循环结构从
for
循环更改为do-while
循环(或任何适合您的目的):
首先对表执行初始扫描,以确定循环终止条件,并获得表中的行数:
data read_once;
set sashelp.class end=eof;
if eof then do;
call symput('number_of_obs', cats(_n_) );
call symput('number_of_times_to_loop', cats(3) );
end;
run;
确保结果符合预期:
%put &=number_of_obs;
%put &=number_of_times_to_loop;
在源表上再次循环多次:
data final;
do loop=1 to &number_of_times_to_loop;
do row=1 to &number_of_obs;
set sashelp.class point=row;
output;
end;
end;
stop; * REQUIRED BECAUSE WE ARE USING POINT=;
run;
答案分两部分
首先,你说什么当然可以做什么。如果你想要一个有用的迭代宏代码示例,可以在网上找到一些这样的代码示例;例如,大卫·伊兹雷尔(David Izrael)的开创性研究,通过迭代一个相对简单的过程(基本上是proc freqs)来执行一个加权过程。这和你正在做的很相似。在此过程中,它在数据步骤中查看各种终止标准,并输出一个宏变量,即满足的标准总数(因为每个分层变量分别需要满足终止标准)。然后,它检查%是否满足该标准,如果满足,则终止
这其中的核心是两件事。首先,你应该有一个固定的最大迭代次数,除非你喜欢无限循环。这个数字应该比你所需要的最大合理数字大,通常大约是两倍。其次,您需要收敛条件,以便在满足这些条件时可以终止循环
例如:
data have;
x=5;
run;
%macro reduce(data=, var=, amount=, target=, iter=20);
data want;
set have;
run;
%let calc=.;
%let _i=0;
%do %until (&calc.=&target. or &_i.=&iter.);
%let _i = %eval(&_i.+1);
data want;
set want;
&var. = &var. - &amount.;
call symputx('calc',&var.);
run;
%end;
%if &calc.=&target. %then %do;
%put &var. reduced to &target. in &_i. iterations.;
%end;
%else %do;
%put &var. not reduced to &target. in &iter. iterations. Try a larger number.;
%end;
%mend reduce;
%reduce(data=have,var=x,amount=1,target=0);
这是一个非常简单的示例,但它具有所有相同的元素。我更喜欢自己使用do-until和increment,但您也可以使用相反的方法(正如%rakinge
所做的那样)。遗憾的是,宏语言不允许像data step语言那样使用do by。哦,好吧
其次,您通常可以在单个数据步骤中执行类似的操作。即使在旧版本(9.2等)中,您也可以在一个数据步骤中完成上述所有要求,尽管它可能看起来有点笨拙。在9.3+中,特别是在9.4中,有一些方法可以在数据步骤中运行proc sql,并在不等待另一个数据步骤的情况下,使用run_宏或DOSUBL和/或FCMP语言返回结果。即使是一些简单的事情,比如:
data have;
initial_a=0.3;
a=0.3;
target=0.5;
output;
initial_a=0.6;
a=0.6;
output;
initial_a=0.8;
a=0.8;
output;
run;
data want;
k=1;
do iter=1 to 20 until (abs(target-estimate0) < 0.001);
do _n_ = 1 to nobs;
if _n_=1 then result_tot=0;
set have nobs=nobs point=_n_;
a=initial_a*k;
result=min(1,a);
result_tot+result;
end;
estimate0 = result_tot/nobs;
k = k * (target/estimate0);
end;
output;
stop;
run;
数据已经存在;
初始_a=0.3;
a=0.3;
目标=0.5;
产出;
初始_a=0.6;
a=0.6;
产出;
初始_a=0.8;
a=0.8;
产出;
跑
数据需求;
k=1;
do iter=1至20,直到(abs(目标估计值0)<0.001);
do=1至nobs;
如果_n_=1,则结果_tot=0;
设置有nobs=nobs点=_n_u;
a=初始值_a*k;
结果=最小值(1,a);
结果_tot+结果;
结束;
估计值0=结果总和/nobs;
k=k*(目标/估计值0);
结束;
产出;
停止
跑
这一切都在一个数据步骤中完成。我有点作弊,因为我正在编写自己的数据步迭代器,但这在这类事情中很常见,而且速度非常快。迭代多个数据步骤和proc sql步骤的宏通常会慢得多,因为每个步骤都会产生一些开销。如果将Target、estimate0或Target-estimate0总计放入一个或多个宏变量中。然后您可以在宏中使用%do%until循环,直到(%eval)总数为。我的代码中缺少的部分似乎是call symputx
行。我是SAS的新手,我认为宏就像C/C++预处理器命令,但如果代码正常工作,一定会有一些神奇的事情发生。谢谢