分享

SAS岩论 | 使用DATA步并行处理超大数据集

 数据分析白小黑 2019-11-01

众所周知,SAS DATA步的特性是一条条数据处理,相对将数据一次性加载到内存的R语言来说,在同样的机器配置下,可以处理更大量的数据。理论上

来讲,无论多大的数据,DATA步都可以处理,只不过是时间的问题。

优势明显,劣势同样明显,那就是无法最大限度的利用机器性能。针对变量不多,大小适中的数据,如果不是非常复杂的处理过程,那么机器性能的提升对于处理时间的减少几乎没什么大的影响。因为都是一条一条数据处理,从硬盘读取数据的速度相差不大(同是机械的情况),简单的计算:加减乘除,单核CPU跟四核CPU相差不大;因此最终的运行时间也相差不大。

那么,面对超大数据集(上亿条数据),需要复杂的计算,有没有办法提高DATA步的处理效率?当然有,那就是并行处理。

因DATA步本身不支持多线程并行处理,那就只能另辟蹊径,采取多进程的方式解决。整体的流程简单来说,就是下面这张图:


具体来说,就有以下几个步骤:

  1. 拆分数据集,将超大数据集拆分成N个等大小的数据集;

  2. 基于拆分后的数据,生成对每个数据集的处理代码,并将其保存为SAS代码文件;

  3. 生成SAS代码的批处理文件,每个代码对应一个批处理文件,便于直接调用运行;

  4. 利用systask command语句调用上面的N个批处理文件,执行SAS代码;

  5. 利用waitfor语句等待所有的批处理执行完成;

  6. 将各个批处理生成的结果合并;

干货开始

1

拆分数据集

为了提高运行效率,将拆分的过程和处理的过程合并。即在将超大数据集拆分成N个子集的时候,就对这N个子集完成相应的处理,得到的结果即是可以合并的结果。拆分的方法使用两个DATA SET OPTION:FIRSTOBS=,OBS=来完成。

所以,第一步就是完成如何拆分,即是要确定每个拆分子集的FIRSTOBS和OBS。要想确定这两个参数,先得知道要拆分的数据集的观测数和要拆分为多少个子集。通过下面的方式完成:

/*定义要拆分的个数*/

%let num_partition=10;

/*定义要处理的数据集*/

%let dsn=out_lib.test_data;

/*获取要处理的数据集的观测数,并输出到宏变量*/

data  _null_;

     did=open('&dsn');

     obs=attrn(did,'nobs');

     call symput('dsobs',obs);

     re=close(did);

run;

/*计算平均每个子集的数据量,取整*/

%let tp_size=%sysfunc(int(&dsobs/&num_partition));

/*计算整除后的余量*/

%let tp_mod=%sysfunc(mod(&dsobs,&num_partition));

/*确定每个拆分子集的FIRSTOBSOBS*/

%macro split_infor;

%do i=1  %to  &num_partition;

     %let  j=%eval(&i-1);

     %if  &i=%then  %let  tp_start&i=1;

     %else %let  tp_start&i=%eval(&&tp_end&j+1);

     %if  %eval(&i  le  &tp_mod) %then  %let  tp_end&i=%eval(&&tp_start&i+&tp_size);

     %else  %let  tp_end&i=%eval(&&tp_start&i+&tp_size-1);

%end;

%mend split_infor;

小贴士

生成的拆分信息保存在宏变量tp_start1, tp_end1, tp_start2, tp_end2, tp_start3, tp_end3, …中。注意此处生成的宏变量是局部的,只在宏内有效。为了方便说明,此处单独拿出来。

测试数据集out_lib.test_data是基于sashelp.class生成而来:

data  out_lib.test_data;

     set sashelp.class;

     do i=to 5300000;

           output;

     end;

run;

2

生成SAS处理代码及批处理文件

为了程序的统一性,将要进行的数据处理操作也单独放在一个宏里面,如下所示:

/*要进行的数据处理操作(此处只举例进行简单的求和及赋值运算)*/

%macro process_content;

     sum=sum(weight+height);

     test='character test';

%mend process_content;

接下来完成生成针对每个数据子集的处理操作SAS代码及批处理文件。下面这步就一次性完成了数据拆分及处理操作。

/*定义一个存储路径(为了方便,要处理的数据也放在此处;生成的结果数据也放在此处)*/

%let  out_path=D:\SAS_DATA;

/*定义SAS可执行程序所在的全路径*/

%let  sas_path='D:\ProgramFiles\SASHome\SASFoundation\9.4\sas.exe';

%macro  generate_code;

%do  i=%to  &num_partition;

     filename temp1 '&out_path\sasjob&i..sas';

     filename temp2 '&out_path\runsasjob&i..bat';

     data  _null_;

           file temp1;

           put 'libname out_lib '&out_path';';

           put 'data out_lib.sample&i;';

           put 'set &dsn(firstobs=&&tp_start&i  obs=&&tp_end&i);';

           put '%bquote(%process_content)';

           put 'run;';

     run;

     data  _null_;

          file  temp2;

           put '%bquote(&sas_path)  -sysin  %str(%”)&out_path\sasjob&i..sas%str(%”)  -log  %str(%”)&out_path\sasjoblog&i..log%str(%”)';

     run;

%end;

%mend  generate_code;

小贴士

注:在Windows中,完成批处理SAS代码的文件为bat文件,其语法格式为:

“SAS可执行程序全路径” -sysin “要执行的SAS程序全路径” -log “生成的日志存储文件”

3

调用执行批处理文件

利用systask command语句调用bat文件,启动SAS进程运行SAS代码得到结果。

%macro execute_job;

%do i=1  %to  &num_partition;

     systask command  '&out_path\runsasjob&i..bat'  taskname='job&i';

%end;

%mend execute_job;

4

合并结果数据

利用waitfor语句等待所有的批处理进程执行完毕,然后通过DATA步合并子结果数据集。

%macro combine_data;

waitfor  _all_

     %do i=1%to &num_partition;

           job&i

     %end;

     ;

data  data_result;

     set

           %doi=1%to &num_partition;

                out_lib.sample&i

           %end;

     ;

run;

%mend combine_data;

至此,整个过程就已介绍完毕,完整的过程还请大家动手自己尝试完成。需要注意的是,我为了方便介绍说明,将部分过程拆分出来单独放到一个宏里面,因此在宏中生成的局部宏变量在其他宏就无法使用。所以,需要大家将整个流程串起来,放到一个大的Macro中就好了。

作者:辛岩,从事了多年的SAS数据分析挖掘工作,担任过项目经理、技术顾问、培训讲师等职务,拥有丰富的项目实战经验。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多