楼主: pksb
16687 11

[程序分享] 【翻译】怎样将一个数据集拆分成多个 [推广有奖]

  • 4关注
  • 1粉丝

本科生

40%

还不是VIP/贵宾

-

威望
0
论坛币
2902 个
通用积分
0
学术水平
1 点
热心指数
1 点
信用等级
0 点
经验
1912 点
帖子
6
精华
0
在线时间
180 小时
注册时间
2010-3-13
最后登录
2022-5-2

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币
原文地址http://saslist.com/blog/2015/01/26/how-to-split-one-data-set-into-many/

SAS程序员会经常需要把一个数据集分成几个小的数据集。这是一件很容易就能做到的事情,用where= 选项或者FIRSTOBS=/OBS= 的组合既可以。但是拆分数据集产生了多个文件这意味着占用了更多的硬盘空间,使用了更多的IO操作。而IO操作和硬盘资源的访问对SAS来说是最昂贵的开销。

但是如果你的领导分配你这个任务,你肯定也不会在上面花多少心思。现在我们假设你需要按照数据集的某列的值来拆分这个数据集。你肯定会这么写,例子如下:
  1. DATA out_Asia;
  2. set sashelp.cars(where=(origin='Asia'));
  3. run;
  4. DATA out_Europe;
  5. set sashelp.cars(where=(origin='Europe'));
  6. run;
  7. DATA out_USA;
  8. set sashelp.cars(where=(origin='USA'));
  9. run;
复制代码


我(原文作者)得承认,现在这不是最有效率或者最优雅的方法。不过这是SAS新手们最容易想到的方法。


上面这段代码是比较简单的,尤其是只有3个不同的值。但是如果用于区分的值有很多这样就需要考虑很多的情况,非常容易出错。这个时候我通常就会使用PROC SQL和SELECT INTO 把值赋给一个宏变量。举个简单的例子:

  1. proc sql;
  2. select distinct ORIGIN into :valList separated by ',' from SASHELP.CARS;
  3. quit;
复制代码

这段代码创建了一个宏变量VALLIST,[size=13.9200000762939px]并且将所有ORIGIN用逗号区分赋值给它。[size=13.9200000762939px]
但是我们可以用SAS函数改善输出,增加其他的代码把值织入到代码逻辑中去。举个例子,我们可以用CAT函数来连接查询来的值和SAS关键字。得出的结果是纯SAS代码可以直接在宏代码中被引用或者执行。我将共享我最终的代码,并且将代码分成几部分便于读者理解。

  1. /* define which libname.member table, and by which column */
  2. %let TABLE=sashelp.cars;
  3. %let COLUMN=origin;

  4. proc sql noprint;
  5. /* build a mini program for each value */
  6. /* create a table with valid chars from data value */
  7. select distinct
  8.    cat("DATA out_",compress(&COLUMN.,,'kad'),
  9.    "; set &TABLE.(where=(&COLUMN.='", &COLUMN.,
  10.    "')); run;") into :allsteps separated by ';'
  11.   from &TABLE.;
  12. quit;

  13. /* macro that includes the program we just generated */
  14. %macro runSteps;
  15. &allsteps.;
  16. %mend;

  17. /* and...run the macro when ready */
  18. %runSteps;
复制代码

下面是对PROC SQL部分的一些说明:
  • SELECT DISTINCT 保证了变量的每个值都只有一条记录。
  • CAT函数把一系列字符串进行了连接(注意到CAT函数有一些变异函数比如CATX,CATS,CATT等,这些函数会将字符串的空白部分去掉)​。在这个例子中我想保留所有在数据中出现的空白部分,因为我们要做相等判断。
  • 程序使用每个值作为输出数据集名称的后缀("OUT_datavalue")。SAS数据集名称只能包含字母和数字,所以这里我使用compress函数删掉所有的空格。kad选项表示值保留英文字母和数字。
  • 所有的程序语句最后赋值给了allsteps这个宏变量,我只需要在SAS程序中引用这段代码就可以运行它了。我选择了另外一种方式,将代码包在了宏里面。这样便于控制这段可执行代码的作用域和位置。

​“By each value of a variable​”仅仅只是拆分数据集的一种条件。我遇到过其他许多拆分数据集的规则:

  • ​​通过观测的数目,比如将一个300万条观测的数据集拆分成3个100万的
  • 通过排名或者百分数
  • 通过日期拆分







二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:数据集 generated proc sql Distinct separate 程序员 where 空间 资源

已有 1 人评分学术水平 热心指数 收起 理由
mengyuy + 1 + 1 精彩帖子

总评分: 学术水平 + 1  热心指数 + 1   查看全部评分

很实用,谢谢楼主,不过直接复制运行出错啊! cat("DATA out_",compress(&COLUMN.,,'kad'),                                                                                                                                                       -
                                                                                                                                                      22(这里提示错误

使用道具

  1. data a;
  2. input origin$ numb;
  3. cards;
  4. Asia 2
  5. Asia 4
  6. Europe 6
  7. Europe 12
  8. Europe 1.2
  9. USA 8
  10. ;
  11. run;

  12. %let TABLE=a;
  13. %let COLUMN=origin;

  14. proc sql noprint;
  15. select distinct
  16.    cat("DATA out_",compress(&COLUMN.,'kad'),
  17.    "; set &TABLE.(where=(&COLUMN.='", &COLUMN.,
  18.    "')); run;") into :allsteps separated by ';'
  19.   from &TABLE.;
  20. quit;

  21. %put &allsteps;
复制代码

楼主,我按你这个,怎么只跑出两个数据集呢?只有out_europe,out_usa,还有个out_asia出错,能否看看哪里出错了

使用道具

板凳
sushe1527 发表于 2015-2-3 16:30:03 |只看作者 |坛友微信交流群
我舅是_黄蓉 发表于 2015-2-3 10:03
楼主,我按你这个,怎么只跑出两个数据集呢?只有out_europe,out_usa,还有个out_asia出错,能否看看哪里出 ...
有些版本不认空格 需要加上“”
  1. data a;
  2. input origin$ numb;
  3. cards;
  4. Asia 2
  5. Asia 4
  6. Europe 6
  7. Europe 12
  8. Europe 1.2
  9. USA 8
  10. ;
  11. run;

  12. %let TABLE=a;
  13. %let COLUMN=origin;
  14. proc sql noprint;
  15. select distinct
  16.    cat("DATA out_",compress(&COLUMN.,"",'kad'),
  17.    "; set &TABLE.(where=(&COLUMN.='", &COLUMN.,
  18.    "')); run;") into :allsteps separated by ';'
  19.   from &TABLE.;
  20. quit;
  21. %macro runSteps;
  22. &allsteps.;
  23. %mend;
  24. %runSteps;
复制代码
已有 3 人评分经验 学术水平 热心指数 信用等级 收起 理由
李会超 + 40 + 2 精彩帖子
heguima + 1 + 1 + 1 精彩帖子
我舅是_黄蓉 + 1 + 1 + 1 精彩帖子

总评分: 经验 + 40  学术水平 + 4  热心指数 + 2  信用等级 + 2   查看全部评分

使用道具

sushe1527 发表于 2015-2-3 16:30
有些版本不认空格 需要加上“”
{:0_248:},可以了,谢谢楼主的分享,又学到一招

使用道具

地板
heguima 发表于 2015-2-4 09:45:59 |只看作者 |坛友微信交流群
学习了{:4_212:}

使用道具

7
gaotao0727 发表于 2015-2-4 18:42:15 |只看作者 |坛友微信交流群
谢谢~~~

使用道具

8
tangliang0905 发表于 2015-2-5 04:39:53 |只看作者 |坛友微信交流群
/* define which libname.member table, and by which column */
%let TABLE=sashelp.cars;
%let COLUMN=origin;

proc sort data=&table out=b nodupkey;
by &COLUMN;
run;

data _null_;
set b;
call execute ('data out_'||strip(&COLUMN)||';');
call execute ("set &table (where=(origin='"||&COLUMN||"'));");
call execute ('run;');
run;

you can compare these two methods. I think the former one is more efficient. Maybe the later one (mine) is more readable, you know what you are doing all the time.

使用道具

9
soporaeternus 发表于 2015-2-5 16:51:15 |只看作者 |坛友微信交流群
为什么不是data d1...dn;用if output来做,就一次
1000个分类值的话原始数据全表遍历1000次也是醉了

使用道具

10
tangliang0905 发表于 2015-2-6 04:03:34 |只看作者 |坛友微信交流群
good point

使用道具

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加好友,备注cda
拉您进交流群

京ICP备16021002-2号 京B2-20170662号 京公网安备 11010802022788号 论坛法律顾问:王进律师 知识产权保护声明   免责及隐私声明

GMT+8, 2024-4-25 06:42