楼主: pinseng
11066 25

[原创博文] 请教一个SAS _n_的问题 [推广有奖]

  • 2关注
  • 3粉丝

硕士生

20%

还不是VIP/贵宾

-

威望
0
论坛币
395 个
通用积分
26.2433
学术水平
5 点
热心指数
8 点
信用等级
6 点
经验
2075 点
帖子
135
精华
0
在线时间
116 小时
注册时间
2006-11-11
最后登录
2016-6-23

楼主
pinseng 发表于 2011-11-13 14:38:31 |AI写论文

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

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

经管之家联合CDA

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

感谢您参与论坛问题回答

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

+2 论坛币
看见mitbbs上的一个问题,正好我也不懂   求解释  谢谢


为什么“if _n_=1 then do until (last)”这一段中要用if _n_=1来判断呢?如果不
用的话,sum_x sum_y都是可以正确求出来,但是obs就是missing

请教一下这儿‘if _n_=1’起的什么作用?

data temp;
input x y@;
datalines;
1 2
3 4
5 6
. 9
6 7
7 .
1 8
6 3
;
run;

data q3;
    if _n_=1 then do until (last);
        set temp nobs=obs end=last;
        sum_x+x;
        sum_y+y;
    end;
    set temp;
    if x=. then x=sum_x/obs;
    if y=. then y=sum_y/obs;
run;
二维码

扫码加我 拉你入群

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

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

关键词:missing MITBBS until Input Lines mitbbs

本帖被以下文库推荐

沙发
meishanjia1900 发表于 2011-11-13 21:22:03
最有趣的是这个事实:

运行以下代码:

  1. data b1;
  2.   input b;
  3. cards;
  4. 0
  5. 0
  6. 0
  7. 1
  8. ;
  9. data b2;
  10.   do until (b);
  11.     set b1;
  12.     a+1;
  13.   end;
  14. run;
  15. proc print data=b2;
  16. run;
复制代码

结果为:

--------------------------------------------------------------------------

                                           Obs    b    a
                                            1       1    4

--------------------------------------------------------------------------

但是,只要改一改,在do前加上if _n_=1 then,即:

  1. data b1;
  2.   input b;
  3. cards;
  4. 0
  5. 0
  6. 0
  7. 1
  8. ;
  9. data b2;
  10.   if _n_=1 then do until (b);
  11.     set b1;
  12.     a+1;
  13.   end;
  14. run;
  15. proc print data=b2;
  16. run;
复制代码

其结果照说应该不变,但是,真实结果却奇迹地变成:

--------------------------------------------------------------------------

                                           Obs    b    a
                                            1       1    4
                                            2       1    4

--------------------------------------------------------------------------

我以为这是 if  语句造成的,于是我试着将其变为 if 1+1=2 then,但是,其结果仍是前者,而非后者。

也就是说,造成第二种结果的关键不在"if _n_=1 then"中的 if  ,而在于其中的"_n_=1"!

为什么会出现第二种输出结果呢?

有高手能解释一下DATA步的内部操作原理么?



藤椅
meishanjia1900 发表于 2011-11-13 21:41:02
我认为这是一个彻头彻尾的BUG,用以下程序更直接的突出这个BUG:

  1. data b1;
  2.   input b;
  3. cards;
  4. 1
  5. 2
  6. ;
  7. data b2;
  8.   if _n_<3 then do;
  9.     set b1 end=last;
  10.     final=last;
  11.   end;
  12. run;
  13. proc print data=b2;
  14. run;
复制代码

结果为:

---------------------------------------------------------------------------

                                         Obs    b    final
                                          1       1      0
                                          2       2      1
                                          3       2      .

---------------------------------------------------------------------------

注意第3行final变量的那个空值,它表示第3行是“失落”的一行,它甚至都不能表明自身是否是最后一行!

这简直是笑话。

我不知道究竟是怎样的内部机制造成了这样荒唐的结果!

更让我不解的是,将if _n_<3 换成 if _n_<=3 ,即:

  1. data b1;
  2.   input b;
  3. cards;
  4. 1
  5. 2
  6. ;
  7. data b2;
  8.   if _n_<=3 then do;
  9.     set b1 end=last;
  10.     final=last;
  11.   end;
  12. run;
  13. proc print data=b2;
  14. run;
复制代码

结果变成:

---------------------------------------------------------------------------

                                         Obs    b    final
                                          1       1      0
                                          2       2      1

---------------------------------------------------------------------------

为什么?为什么会有如此的差别?

这不是BUG是什么?

板凳
meishanjia1900 发表于 2011-11-13 22:05:50
现在我将进一步发现的“奇怪现象”记录在下面:

程序1:

  1. data b1;
  2.   input b;
  3. cards;
  4. 1
  5. 2
  6. ;
  7. data b2;
  8.   if _n_<3 then do;
  9.     set b1;
  10.     final=4;
  11.   end;
  12. run;
  13. proc print data=b2;
  14. run;
复制代码

结果为:

-----------------------------------------------------------------------------

                                         Obs    b    final
                                          1       1      4
                                          2       2      4
                                          3       2      .

-----------------------------------------------------------------------------

程序2:将程序1的final=4从do循环里拿出来,变为:

  1. data b1;
  2.   input b;
  3. cards;
  4. 1
  5. 2
  6. ;
  7. data b2;
  8.   final=4;
  9.   if _n_<3 then do;
  10.     set b1;
  11.   end;
  12. run;
  13. proc print data=b2;
  14. run;
复制代码

结果为:

-----------------------------------------------------------------------------

                                         Obs    final    b
                                          1         4      1
                                          2         4      2
                                          3         4      2

-----------------------------------------------------------------------------

程序3:将程序1中的final=4改为final+4,变为:

  1. data b1;
  2.   input b;
  3. cards;
  4. 1
  5. 2
  6. ;
  7. data b2;
  8.   if _n_<3 then do;
  9.     set b1;
  10.     final+4;
  11.   end;
  12. run;
  13. proc print data=b2;
  14. run;
复制代码

结果为:

-----------------------------------------------------------------------------

                                         Obs    b    final
                                          1       1      4
                                          2       2      8
                                          3       2      8

-----------------------------------------------------------------------------

这些记录有助于找出_n_相关的内在机理。

报纸
denver 发表于 2011-11-13 22:26:52
有意思,关注中
Denver大家一起读Paper系列索引贴:
https://bbs.pinggu.org/thread-1430892-1-1.html

地板
soporaeternus 发表于 2011-11-14 10:41:59
在对应的地方添加put _all_;语句并且看下log,可以帮助理解data步的机制
_N_不是记录的条数,而是data步循环的执行次数,虽然在某些简单代码里可以等同于当前记录数

希望对你有帮助
Let them be hard, but never unjust

7
tj0412ymy 发表于 2011-11-14 15:17:07
Very interesting!上面几个例子中的_N_同时充当了2个角色:录观测的变量循环变量。在读取第一个set语句之前,_N_扮演的是循环变量,读set语句时,扮演的是记录观测数的变量。所以得到的结果的记录数要比原数据集多1条观测。当_N_的值变为1,进入第二次循环时,_N_已经等于2了(相当于retain变量,每次循环加1),point已经指向了原数据集的第二条记录。当读完最后一条观测是,_N_的值已经变为obs+1了,所以在set语句之后直接break了,不会继续执行下面的语句了。如此反复,请高手指教!
对SAS和统计方面感兴趣的朋友,请加SAS学习和认证讨论群:169157207。欢迎在群上讨论!

8
soporaeternus 发表于 2011-11-14 16:16:47
tj0412ymy 发表于 2011-11-14 15:17
Very interesting!上面几个例子中的_N_同时充当了2个角色:记录观测数的变量和循环变量。在读取第一个set语 ...
_N_
is initially set to 1. Each time the DATA step loops past the DATA statement, the variable _N_ increments by 1. The value of _N_ represents the number of times the DATA step has iterated.

这个就是_N_的本质
等于记录数只是一个巧合!
Let them be hard, but never unjust

9
haizhilan 发表于 2011-11-14 16:20:36
我看了你给出的程序和运行结果,SAS的执行是不存在BUG的(我想也不应该存在,否则SAS公司还怎么混,呵呵)。因为你给的程序太多,逐条解释很麻烦,这里面给出几点提示,如果有任何不明白的地方,欢迎讨论:
1.SAS 数据步隐含循环过程;
2.加法语句(就是sum_x+x之类的),和Set Statement生成的变量具有Retain语句指定变量一样的功能,循环执行结束后不会变成缺失值;
3.在没有明确的output语句的情况下,Sas在每执行完一次数据步后,会生成一条记录,然后返回从头开始执行,直到读取完全部数据,即end指定的变量取值变成1.
我会针对第一个程序解释一下,希望有帮助

10
haizhilan 发表于 2011-11-14 16:33:03
回复:
为什么“if _n_=1 then do until (last)”这一段中要用if _n_=1来判断呢?如果不
用的话,sum_x sum_y都是可以正确求出来,但是obs就是missing

请教一下这儿‘if _n_=1’起的什么作用?

data temp;
input x y@;
datalines;
1 2
3 4
5 6
. 9
6 7
7 .
1 8
6 3
;
run;
*前面的应该不存在疑问data q3;
    if _n_=1 then do until (last);
        set temp nobs=obs end=last;
        sum_x+x;
        sum_y+y;
    end;
*这里用if 语句的目的是生成x和y变量的和,以为后面求均值赋给缺失值做准备;
*为什么要用_n_=1而不是_n_=2或其它数值呢,因为变量_n_是从1开始赋值,每执行一次数据步往后累加的,如果用_n_=2,则对第1条记录来说,if语句是不执行的,此时无法计算出x和y变量各自的和,如果刚好第1个x或y就缺失的话,是无法被赋予正确的值的,所以为保险起见,只能用_n_=1而不是其它的值
    set temp;
    if x=. then x=sum_x/obs;
    if y=. then y=sum_y/obs;
run;

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

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