使用宏优化构建SAS表



我有一个SAS程序,它可以动态地用如下宏构建一个表:

%macro Projection;
%do i=1 %to &number_of_Years;
    %Advance_Years;
     proc sql;
      create table Projection as 
      select *, Year_&previous_year.*(1+return) as Year_&current_year.
      from Projection;
      quit;
%end;
%Mend Projection;
%Projection;

这是我的代码的简化版本。%Advance_Years宏基本上推进了1年的&current_year&previous_year宏。如您所见,该表每年获取1个变量。问题是,这个表的行数可能达到数十万,我看到执行时间飙升,达到了几个小时才能完成。

我尝试过option compress=yes,它有助于减少执行时间,但不是很多。我已经尝试了SAS的大多数加速执行的技巧和窍门,但同样,没有太大区别。我正在32b上运行Base SAS 9.2。

我认为我对变量加法技术的理解是错误的。我在每个循环中覆盖表的事实对执行效率有影响吗?如果是,我该如何重写它,使其成为最高效的代码?请记住,我不能"转置"这个表而只添加更多的行。提前感谢!

没有理由在多次数据传递中这样做,除非我在这里遗漏了一些重要的东西。正如你所推测的那样,这当然是问题所在。

data projection;
set <whatever came before projection>;
array years year_1-year_&number_of_years.;
year_1=1;
do _t = 2 to dim(years);
 years[_t] = years[_t-1]*(1+return);
end;
run;

SQL解决方案不会有太大的不同,只是如果没有数组,您必须通过宏语言动态构建它,但仍然需要一次... from projection;传递

%macro add_to_sql(current_year=,previous_year=);
Year_&previous_year.*(1+return) as Year_&current_year.
%mend add_to_sql;
data calllist;
do current_year = 2 to &number_of_years;
 previous_year=current_year-1;
 output;
end;
run;
proc sql;
select cats('%add_to_sql(current_year=',current_year,',previous_year=',previous_year,')')
into :addlist separated by ',' from callist;
select year_1,&addlist from projection;
quit;

注意:您应该尽可能通过数据来驱动程序执行。尽管这看起来效率稍低,但与使用宏来增加一些宏变量相比,读取和调试要容易得多,这还违反了SAS的受保护/私有/任何面向对象的whatnot版本——SAS不反对这种版本,但确实很糟糕。使用参数调用宏,并从调用环境中提供这些参数;宏不应更改调用环境中的宏变量。因此,我用参数设置了add_to_sql,并创建了一个包含这些参数的数据集(可以是从其他地方导入的数据集,也可以是在这里创建的数据集)&number_of_eyears也应该是该模块的入口参数,除非这是主程序,在这种情况下,全局变量是可以的。

一般来说,在SAS(以及其他语言中,尤其是SAS)中,I/O时间将超过任何其他处理时间,特别是考虑到当前的计算经济性(CPU功能强大,比10年前快了很多数量级,而除非你使用闪存,否则你的存储速度不到10年前的一个数量级)。

我认为在同一个表上写入会导致很多性能问题。

让我描述一下我认为你应该采取的解决方案。我的SQS宏编程技巧有点生疏,所以我将把实际实现留给您。

首先,更改Projection表,使其具有要添加的列。理想情况下,您可以在一个声明中完成此操作。但你可以做一些类似的事情:

%do i=1 %to &number_of_Years;
    %Advance_Years;
    alter table Projection
        add Year_&current_year double;
%end;

然后,循环并进行适当的计算:

%do i=1 %to &number_of_Years;
    %Advance_Years;
    update table Projection
        set Year_&current_year = Year_&previous_year.*(1+return);
%end;

最新更新