0

我有一个 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。

我认为我对变量添加技术的看法是错误的。我在每个循环上覆盖我的表这一事实是否会影响执行效率?如果是的话,我怎么能把它改写成最有效的代码呢?请记住,我不能“转置”表格而只能添加更多行。提前致谢!

4

2 回答 2

3

没有理由在不止一次遍历数据时这样做,除非我在这里遗漏了一些重要的东西。正如您所猜测的那样,这当然是问题所在。

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 版本的受保护/私有/任何面向对象的东西——SAS 不反对,但实际上风格不好。调用带有参数的宏,并从调用环境中提供这些;宏不应更改调用环境中的宏变量。因此,我使用参数设置了 add_to_sql,以及包含这些参数的数据集(可以是从其他地方导入的数据集,或在此处创建的数据集)。&number_of_years 也应该是这个模块的入口参数,除非这是主程序,在这种情况下全局变量是可以的。

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

于 2013-08-06T03:20:04.997 回答
1

我想在同一张表上写会导致很多性能问题。

让我描述一下我认为您应该采取的解决方案。我的 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;
于 2013-08-05T23:57:58.807 回答