楼主: crackman
7845 18

跟crackman读SAS程序(4) [推广有奖]

楼主
crackman 发表于 2010-8-19 23:25:02

跟crackman读SAS程序(1)

初学SAS,请教各位高手一小问题,关于循环语句中读取数据文件,如下:

简单举例,假设我有4个TXT文件,每个文件中1个数值变量,共5个观测(如1 2 3 4 5),数据分析前,需要先用循环语句对这几个文件的数据预处理,
我现在的问题在于怎么用SAS语句读取文件,以前做的分析全用VB程序编的:

1.例如,VB语句可以这样写:
For i=1 to 4
       open "E:\" & i & ".txt" for Input As 1
       While Not EOF(1)
                Input #1, b
                a(m) = b
                m = m + 1
        Wend
        Close 1
Next i

2.尝试用SAS语句:
data a;
      do i=1 to 4 by 1;
           infile "E:\" i ".txt";
      end;
      input v ;
run;

---------问题:SAS语句在“Infile”那行的“i”处提示错误,数据读不进去......请问SAS语句中有什么方法可以实现以上用途吗?

--------请各位高手不吝相助,不胜感激~~~



hopewell答案:
data temp;
        do in=1 to 4;
                fname=cats('d:\',in,'.txt');
                do until(last);
                        infile XXX filevar=fname end=last;
                        input v;
                        output;
                end;
        end;
      stop;
run;

此处注意几点:
1.DO循环:很巧妙的注意到了D盘下TXT文件名的规律性,都是一个数字命名的文件名,用变量IN,采用CATS字符串连接函数,循环创建D盘下的TXT文件名变量FNAME;
2.在读取文本数据时,很多人都知道用INFILE,以及INFILE基本参数,XXX是文件名,filevar是打开或者关闭指定的文件名,end=last是定义一个变量了解是否读到文件的结尾处,这里的LAST与until里面的last保持一致
3.STOP,这里为什么用STOP,这个很重要,如果你去掉STOP,那么你会发现无线循环下去。所以当我们在DATA步里面制定读取的数据是以随机方式读取的时候,例如这个里面的IN,或者是在SET里面的POINT等,需要用STOP。Because SAS does not detect an end-of-file with this access method, you must include program statements to prevent continuous processing of the DATA step.


在这里提出一个思路:我们在解决这类问题的时候,先考虑是一个文件的时候,你如何写程序,如何读取,如果是两个文件的时候,程序与读取一个文件时程序有什么不同,这种不同是什么原因导致,这个不同是否有规律可循,寻找到规律你就有思路了。




已有 8 人评分经验 论坛币 学术水平 热心指数 信用等级 收起 理由
bakoll + 3 + 3 精彩帖子
yatming + 1 + 1 + 1 大力支持!!!
anzhiliang + 1 + 1 + 1 我很赞同
zhentao + 1 + 1 精彩帖子
peijiamei + 3 + 3 精彩帖子
hopewell + 1 + 1 + 1 精彩帖子
pobel + 1 + 1 精彩帖子
soporaeternus + 1 + 1 支持!!!

总评分: 经验 + 3  论坛币 + 3  学术水平 + 9  热心指数 + 9  信用等级 + 3   查看全部评分

沙发
crackman 发表于 2010-8-20 00:28:07

跟crackman读SAS程序(2)

http://www.pinggu.org/bbs/thread-888994-1-1.html
原程序和解答在这里,想分解一下答案程序
data a;
        input nbr code;
        datalines;
        13900000000 10102345
13900000000 10102346
13900000000 10102347
13900000000 10102348
13900000001 10102345
13900000001 10102346
13900000001 10102349
13900000002 10102345
13900000002 10102347
13900000002 10102349
13900000003 10102345
13900000003 10102346
13900000003 10102348
13900000003 10102349
;

run;
proc sql;
        create table b as
                select
                        a.code as prom_1
                        ,b.code as prom_2
                        ,count(distinct a.nbr) as Csr_Cnt
                from a a
                inner join a b
                on a.code<b.code
                        and a.nbr=b.nbr
                group by
                a.code
                ,b.code
        ;
quit;


首先soporaeternus 哥们的SQL确实很不错,学SQL,可以看看他的程序,学习的地方太多了。
对于这个程序思路如下:
首先利用在SQL中的同一个数据集不同的别名,克隆另外一个相同的数据集,利用ON语句中条件,以及INNER JOIN进行CODE的组合,这个是解决问题的关键,先形成组合。因为不同NBR的CODE组合,有些相同,有些不相同,于是用GROUP BY 来排除重复的组合,保证组合的唯一性,为后面计算NBR提供正确唯一的组合。因为不同的NBR可能存在相同的CODE组合,反过来,同一个的CODE组合存在不同的NBR,因为我们计算的是NBR的个数,因此我们在GROUP BY 限定了CODE组合的唯一性,然后利用COUNT函数来计算NBR的个数,思路就是这样,想起来其实也不难,关键是面对数据的时候要快速的理清思路很关键。这里指的学习是:克隆相同的数据集,GROUP BY 保证CODE组合的唯一不重复。
已有 4 人评分学术水平 热心指数 信用等级 收起 理由
andy162639 + 1 + 1 精彩
peijiamei + 3 + 3 精彩帖子
soporaeternus + 1 + 1 都出2了,再次支持......
hopewell + 1 + 1 + 1 精彩帖子

总评分: 学术水平 + 6  热心指数 + 6  信用等级 + 1   查看全部评分

藤椅
crackman 发表于 2010-8-20 00:32:01
顺便转一个对INNER JOIN LEFT JOIN RIGHT JOIN的介绍的帖子

sql之left join、right join、inner join的区别
left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录
right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录
inner join(等值连接) 只返回两个表中联结字段相等的行

举例如下:
--------------------------------------------
表A记录如下:
aID     aNum
1     a20050111
2     a20050112
3     a20050113
4     a20050114
5     a20050115

表B记录如下:
bID     bName
1     2006032401
2     2006032402
3     2006032403
4     2006032404
8     2006032408

--------------------------------------------
1.left join
sql语句如下:
select * from A
left join B
on A.aID = B.bID

结果如下:
aID     aNum     bID     bName
1     a20050111    1     2006032401
2     a20050112    2     2006032402
3     a20050113    3     2006032403
4     a20050114    4     2006032404
5     a20050115    NULL     NULL

(所影响的行数为 5 行)
结果说明:
left join是以A表的记录为基础的,A可以看成左表,B可以看成右表,left join是以左表为准的.
换句话说,左表(A)的记录将会全部表示出来,而右表(B)只会显示符合搜索条件的记录(例子中为: A.aID = B.bID).
B表记录不足的地方均为NULL.
--------------------------------------------
2.right join
sql语句如下:
select * from A
right join B
on A.aID = B.bID

结果如下:
aID     aNum     bID     bName
1     a20050111    1     2006032401
2     a20050112    2     2006032402
3     a20050113    3     2006032403
4     a20050114    4     2006032404
NULL     NULL     8     2006032408

(所影响的行数为 5 行)
结果说明:
仔细观察一下,就会发现,和left join的结果刚好相反,这次是以右表(B)为基础的,A表不足的地方用NULL填充.
--------------------------------------------
3.inner join
sql语句如下:
select * from A
innerjoin B
on A.aID = B.bID

结果如下:
aID     aNum     bID     bName
1     a20050111    1     2006032401
2     a20050112    2     2006032402
3     a20050113    3     2006032403
4     a20050114    4     2006032404

结果说明:
很明显,这里只显示出了 A.aID = B.bID的记录.这说明inner join并不以谁为基础,它只显示符合条件的记录.
--------------------------------------------
注:
LEFT JOIN操作用于在任何的 FROM 子句中,组合来源表的记录。使用 LEFT JOIN 运算来创建一个左边外部联接。左边外部联接将包含了从第一个(左边)开始的两个表中的全部记录,即使在第二个(右边)表中并没有相符值的记录。

语法:FROM table1 LEFT JOIN table2 ON table1.field1 compopr table2.field2

说明:table1, table2参数用于指定要将记录组合的表的名称。
field1, field2参数指定被联接的字段的名称。且这些字段必须有相同的数据类型及包含相同类型的数据,但它们不需要有相同的名称。
compopr参数指定关系比较运算符:"=", "<", ">", "<=", ">=" 或 "<>"。
如果在INNER JOIN操作中要联接包含Memo 数据类型或 OLE Object 数据类型数据的字段,将会发生错误.
已有 2 人评分学术水平 热心指数 收起 理由
xiangzi525 + 1 + 1 很好!
peijiamei + 3 + 1 精彩帖子

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

板凳
大憨熊猫 发表于 2010-8-20 08:05:02
谢谢楼主对程序的详尽解释,很受用。
不求尽如人意,但求无愧我心。

报纸
xuwei2007 发表于 2010-8-20 08:28:45
膜拜!!!!

地板
hopewell 发表于 2010-8-20 08:52:34
楼主好热心呀!
infile语句的XXX好像只是个占位符,除了在log里露了个小脸之外,不起实际作用的

已卖:401份资源

院士

83%

还不是VIP/贵宾

-

威望
6
论坛币
91928 个
通用积分
23.5045
学术水平
424 点
热心指数
505 点
信用等级
256 点
经验
112978 点
帖子
2940
精华
0
在线时间
2532 小时
注册时间
2007-4-26
最后登录
2025-6-25

初级热心勋章 中级热心勋章 初级学术勋章 初级信用勋章

7
crackman 发表于 2010-8-20 10:47:24 |AI写论文
http://www.pinggu.org/bbs/thread-889793-1-1.html
原程序在这里
在这里提供了两个循环调用宏的方法,一个是用另外一个宏循环调用外部的已经编写好的宏 另外一个是在data步里面调用宏。
%macro outer;
%do i=1 %to 10;
%inner2(0, 1, &i);
%end;
%mend;

%outer

这个是第一个方法,其实这个方法我最熟悉也经常用的一个方法,封装在另外一个宏循环里面,结构清晰,不容易出错。
其实在这里给我们写程序提供了一个思路,对于一个比较复杂的程序来说,我们要学会功能分解,结构化的去设计程序。例如在这个程序里面,
我们讲重复需要实现的过程或者步骤写成一个宏程序,设置好宏参数。解决一个比较复杂问题的时候,将问题分解成若干个小问题,对于每一个问题或者每一个功能写成一个宏模块,但是一定要设计好参数,这个参数是与其他结构模块相互沟通的接口。就想现在建房子一样,都是组合式,每一个部门都是标准化设计,然后组合在一起实现一个整体的需要或者整体的功能。


hopewell的程序里面提供一个宏宇data步交互的方法

  • %macro inner(x,y,z);
  •         %put NOTE:*** Macro inner: x=&x y=&y z=&z ***;
  • %mend inner;
  • data _null_;
  •         do i=1 to 3;
  •                 call execute(cats('%inner(0,1,',put(i,best.),')'));
  • /*                %inner(0,1,i);*/
  •         end;
  • run;
其实要说难理解应该是call execute(cats('%inner(0,1,',put(i,best.),')'));
其实这句话里面cats连接字符函数,最后得出的结果其实就是call execute('%inner(0,1,i)')
但是在这里如果直接写call execute('%inner(0,1,i)')岂不是更简洁?
如果说是没有参数,call execute('%inner') 这样是可以的,但是如果是参数,就注意,如果写成call execute('%inner(0,1,i)'),他会解析成一个宏调用,%inner(0,1,i),同时DATA步停止,而不是我们想的%inner(0,1,1)这样,因此要想得到%inner(0,1,1)这样解析结果,就要对I进行一个处理,把%inner(0,1,1)看做一个字符串,对于这个字符串变化的那个需要用put函数将其转换一个字符,用cats来连接。

至于call execute的使用方法:可以看看下面一个我转载的帖子


[功能]
  解析参数,如果解析后的值是sas语句(sas statement),则在下一个步边界(step boundary)执行;
  如果此值为macro语言元素(macro language element),则立即执行.
  用于在data step中与macro机制进行交互.
  [分类]
  宏(macro)
  [语法]
  CALL EXECUTE(argument);
  [参数]
  argument
  指定一个产生宏调用或者sas语句的字符表达式或者常量.argument可以是:
  一个字符串,放入引号内
  data步的字符变量,不要使用引号括住
  一个字符表达式,数据步解析成宏文本表达式(macro text expression)或者sas语句.
  [详细]
  如果参数被解析成一个宏调用,则宏立刻运行,在这个宏执行时,data步的执行暂停.
  如果参数被解释成一个sas语句,或者宏的执行产生sas语句,这些语句在call execute
  子程序的所在的data步结束后才执行.
  [CALL EXECUTE子程序同步(timing)详解]
  当想有条件的执行macro时,CALL EXECUTE很有用.但牢记,如果call execute产生macro language elements,
  则他们立即执行;如果call execute产生sas language statements,或者macro language elements产生
  sas language statements,这些statements在当前data step执行后执行.
  下面两个例子阐述用户使用call execute常有的问题
  例1:
  data prices;/* ID for price category and actual price */
  input code amount;
  cards;
  56 300
  99 10000
  24 225
  ;
  %macro items;
  %global special;
  %let special=football;
  %mend items;
  data sales;/* incorrect usage */
  set prices;
  length saleitem $ 20;
  call execute('%items');
  saleitem=”&special”;
  run;
  在DATA SALES step中,在data步编译时,对SALEITEM的赋值需要macro变量SPECIAL的值.
  但是直到data step执行时,call execute才产生这个值.所以,在log中能看一个
  未解析宏变量的消息,并且SALEITEM的值为&special.
  在这个例子中,去掉宏定义或者把DATA SALES步放到宏ITEMS中更好.在这两种情况下,
  call execute都不必要,也没什么用处.下面是此程序正确的一个版本
  data prices;/* ID for price category and actual price */
  input code amount;
  cards;
  56 300
  99 10000
  24 225
  ;
  %let special=football;/* correct usage */
  data sales;
  set prices;
  length saleitem $ 20;
  saleitem=”&special”;
  run;
  例2:
  /* This version of the example shows the problem.*/
  data prices;/* ID for price category and actual price */
  input code amount;
  cards;
  56 300
  99 10000
  24 225
  ;
  data names;/* name of sales department and item sold */
  input dept $ item $;
  cards;
  BB Boat
  SK Skates
  ;
  %macro items(codevar=);/* create macro variable if needed */
  %global special;
  data _null_;
  set names;
  if &codevar=99 and dept='BB'then call symput('special',item);
  run;
  %mend items;
  data sales;/* attempt to reference macro variable fails */
  set prices;
  length saleitem $ 20;
  500 then
  call execute('%items(codevar='|| code || ')');
  saleitem=”&special”;
  run;
  在这个例子中,宏ITEMS生成一个DATA _NULL_ step,这个step在DATA SALES step执行之后
  才执行.而在DATA SALES编译时就需要DATA _NULL_ step中创建的SPECIAL.
  正确的一个例子:
  /* This version solves the problem.*/
  data prices;/* ID for price category and actual price */
  input code amount;
  cards;
  56 300
  99 10000
  24 225
  ;
  data names;/* name of sales department and item sold */
  input dept $ item $;
  cards;
  BB Boat
  SK Ski
  ;
  %macro items(codevar=);/* create macro variable if needed */
  %global special;
  data _null_;
  set names;
  if &codevar=99 and dept='BB'then
  call symput('special',item);
  run;
  %mend items;
  data _null_;/* call the macro in this step */
  set prices;
  500 then
  call execute('%items(codevar='|| code || ')');
  run;
  data sales;/* use the value created by the macro in this step */
  set prices;
  length saleitem $ 20;
  saleitem=”&special”;
  run;
  上面的例子使用一个DATA _NULL_ step来调用宏ITEMS.在这个step结束后,由ITEMS产生的
  DATA _NULL_ step执行,并创建宏变量SPECIAL.
二维码

扫码加我 拉你入群

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

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

关键词:crackman Ackman CRACK sas程序 CRA 程序 SAS crackman

回帖推荐

crackman 发表于2楼  查看完整内容

http://www.pinggu.org/bbs/thread-888994-1-1.html 原程序和解答在这里,想分解一下答案程序 data a; input nbr code; datalines; 13900000000 10102345 13900000000 10102346 13900000000 10102347 13900000000 10102348 13900000001 10102345 13900000001 10102346 13900000001 10102349 13900000002 10102345 13900000002 10102347 13900000002 10102349 13900000003 10102345 139000 ...

crackman 发表于3楼  查看完整内容

顺便转一个对INNER JOIN LEFT JOIN RIGHT JOIN的介绍的帖子 sql之left join、right join、inner join的区别 left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录 right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录 inner join(等值连接) 只返回两个表中联结字段相等的行 举例如下: -------------------------------------------- 表A记录如下: aID     aNum 1   ...
已有 7 人评分学术水平 热心指数 信用等级 收起 理由
yatming + 1 对论坛有贡献
醉_清风 + 1 + 1 + 1 楼主加油继续啊
soporaeternus + 1 + 1 4啦,别逼我追啊
sushe1527 + 1 是不是每日一更啊
hopewell + 1 + 1 + 1 精彩帖子
anzhiliang + 1 + 1 + 1 精彩帖子
peijiamei + 3 + 3 精彩帖子

总评分: 学术水平 + 8  热心指数 + 8  信用等级 + 3   查看全部评分

8
上山路 发表于 2010-8-20 10:54:28
版主分析的真详细啊,受教了

9
chouxiangdaishu 发表于 2010-8-20 15:26:20
写个宏,然后再用%do不就得了。

10
crackman 发表于 2010-8-20 21:25:07

跟crackman读SAS程序(7)

data raw;
        input date yymmdd8. week;
        format date yymmdd10.;
datalines;
20100628 1
20100629 1
20100630 1
20100701 1
20100702 1
20100705 2
20100706 2
20100707 2
20100708 2
20100709 2
20100712 3
20100713 3
20100714 3
;

data out;
        _temp=0;
        do _n_=1 by 1 until(last.week);
                set raw ;
                by week;
                _temp+1;
        end;
         if _temp=5 then output;
        do _n_=1 to _n_;
                set raw;
                if _temp<5 then output;
        end;
run;

http://www.pinggu.org/bbs/thread-865714-1-1.html原问题这此帖
1.第一个do until循环实现的是计数:就是计算同一个WEEK的累及个数,存在变量_temp中
2.IF THEN 选择输出
3.遍历RAW数据集,选择输出。

在这里注意个就是BY的用法
分组遍历数据集RAW

还要注意UNTIL里面的last.week的用法,直到last.week=1的时候停止循环

其实也可以这样来写:

data raw;
        input date yymmdd8. week;
        format date yymmdd10.;
datalines;
20100628 1
20100629 1
20100630 1
20100701 1
20100702 1
20100705 2
20100706 2
20100707 2
20100708 2
20100709 2
20100712 3
20100713 3
20100714 3
;
proc sort data=raw;
by week;
run;
data out;
set raw;
by week;
if first.week then _temp=1;
else _temp+1;
run;
data out(drop=_temp);
set out;
if week=3 or _temp=5;
run;
已有 2 人评分学术水平 热心指数 信用等级 收起 理由
peijiamei + 3 + 3 对论坛有贡献
hopewell + 1 + 1 + 1 精彩帖子

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

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

本版微信群
加好友,备注cda
拉您进交流群
GMT+8, 2025-12-9 16:30