作者yonny (悠逆)
看板Statistics
标题Re: [程式] SAS选择最近的值後转置
时间Sat Mar 26 22:51:08 2016
很谢谢west1996的回覆~
还有另外一个低调的板友也有寄给我~
写法都有一些差异
我一开始想到做法是用PROC SQL中的subquery做
虽然已经有人写出来了 不过怀着运动家的精神还是把它写完
之前我卡在 如果前後同时有一样接近的点 该取哪一个的问题
因为subquery只能回传一个值
後来我参考了west1996的做法 多产生一个变项 (真是太感谢了)
每次多看别人怎麽写 自己也都会有进步!!!
我重新举了一个比较复杂的例子(包括前後距离相同)
分别抓第6个月和第12个月之前後一个月内最接近值
以下 跟大家分享:
DATA TEST1;
Input N PID Time AFP ALT;
cards;
1 1 0.1 100 10
2 1 2 48 20
3 1 5.8 45 30
4 1 6.2 11.0 .
5 1 10 8.9 50
6 1 11.3 4.7 60
7 1 12.1 4.5 .
8 1 13 4.22 71
9 1 15 3.45 72
10 1 18.2 2.13 73
11 1 31 2.12 74
12 2 0 30 24
13 2 3.5 20 25
14 2 5.9 10 26
15 2 6.3 4 27
16 2 11.8 5 20
17 2 12.9 6 30
18 3 0 5.5 40
19 3 1 2.5 50
20 3 2 4.2 60
21 3 12.1 . 20
22 3 12.5 3.5 30
RUN;
PROC sort data=test1 (keep= PID) out=test3 nodupkey;
By PID;
RUN;
%MACRO ALLTIME(VAR,T);
DATA test2;
SET test1;
where &VAR.~=.;
RUN;
Proc sql;
create table want1 as
select test3.PID , (select &VAR. from test2 where PID=test3.PID
having test2.Time<=&T. and ABS(test2.Time-&T.) = MIN(ABS(test2.Time-&T.)))
as &VAR.&T.M_less
,(select time from test2 where
PID=test3.PID having test2.Time<=&T. and ABS(test2.Time-&T.) =
MIN(ABS(test2.Time-&T.))) as &VAR.&T.M_time_less
, (select &VAR. from test2 where
PID=test3.PID having test2.Time>&T. and ABS(test2.Time-&T.) =
MIN(ABS(test2.Time-&T.))) as &VAR.&T.M_over
,(select time from test2 where
PID=test3.PID having test2.Time>&T. and ABS(test2.Time-&T.) =
MIN(ABS(test2.Time-&T.))) as &VAR.&T.M_time_over
from test3 ;
quit;
DATA want1;
SET want1;
IF &VAR.&T.M_less=. and &VAR.&T.M_over=. then Do;
&VAR._&T.M=.; &VAR._&T.M_time=.;
END;
ELSE IF &VAR.&T.M_less~=. and &VAR.&T.M_over=. then Do;
&VAR._&T.M=&VAR.&T.M_less;&VAR._&T.M_time=&VAR.&T.M_time_less;
END;
ELSE IF &VAR.&T.M_less=. and &VAR.&T.M_over~=. then DO;
&VAR._&T.M=&VAR.&T.M_over;&VAR._&T.M_time=&VAR.&T.M_time_over;
END;
ELSE IF &VAR.&T.M_less~=. and &VAR.&T.M_over~=. then DO;
&VAR._&T.M=(&VAR.&T.M_less+&VAR.&T.M_over)/2;
&VAR._&T.M_time=9999;
&VAR._&T.M_time2=CATS(&VAR.&T.M_time_less,"&",&VAR.&T.M_time_over);
END;
IF &VAR._&T.M_time=9999.0 then &VAR.&T.M =CATS(&VAR._&T.M,"(",
&VAR._&T.M_time2 ,")");
ELSE IF &VAR._&T.M_time <&T.-1 or &VAR._&T.M_time >&T+1 then &VAR.&T.M= . ;
ELSE &VAR.&T.M =CATS(&VAR._&T.M,"(", &VAR._&T.M_time ,")");
keep PID &VAR.&T.M;
RUN;
DATA test3;
Merge test3 want1;
By PID;
RUN;
%MEND ALLTIME;
%ALLTIME(AFP, 6)
%ALLTIME(AFP, 12)
%ALLTIME(ALT, 6)
%ALLTIME(ALT, 12)
PROC PRINT data=test1;RUN;
PROC PRINT data=test3;RUN;
※ 引述《west1996 ()》之铭言:
: 注意:没有data可以测,凭想像空打,程式码可能有错,请自行debug一下
: 这支macro可以一次转一个变数一个时间点,喂给他三个东西
: 1. 资料集名称
: 2. 变数名称
: 3. 月数(要给一个数字)
: macro会产生该名称该变数的结果在work里,程式码最後一行的范例是把yourdata这个
: 资料集的GOT变数的3个月资讯转出来存成work.out_GOT_3M
: P.S.要一次转多个变数多个时间点需要另外加工包装macro,比较麻烦,既然你的栏位
: 不多,时间点也不多,data也不大,所以乾脆多跑几次macro再自行merge
: 资料就好,速度上不会差很多
: 程式码如下:
: %macro aggregatedata(dsn=, var=, month=);
: data work.out_&var._&month.M;
: set &dsn.;
: by id;
: retain temp_value temp_month over_value over_month over_flag;
: length &var._&month.M $ 12;
: if first.id=1 then do; temp_value=.; temp_month=.; over_value=.; over_month=.; over_flag=0; end;
: if &month.-1<= month <= &month.+1 and &var. ^= . and temp_month ^= &month. and over_flag ^= 1 then do;
: if month <= &month. then do;
: temp_value=&var.;
: temp_month=month;
: end;
: else if month> &month. then do;
: over_value=&var.;
: over_month=month;
: over_flag=1;
: end;
: end;
: if last.id=1 then do;
: if temp_value = . and over_value= . then &var._&month.M='.';
: else if temp_value=. and over_value ^=. then &var._&month.M=cats(over_value,'(',over_month,')');
: else if temp_month = &month. then &var._&month.M=cats(temp_value,'(',temp_month,')');
: else if &month.-temp_month <= over_month-&month. then &var._&month.M=cats(temp_value,'(',temp_month,')');
: else if &month.-temp_month > over_month-&month. then &var._&month.M=cats(over_value,'(',over_month,')');
: end;
: output;
: end;
: keep id &var._&month.M;
: run;
: %mend;
: %aggregatedata(dsn=yourdata, var=GOT, month=3)
: ※ 引述《yonny (悠逆)》之铭言:
: : [软体程式类别]: SAS (R也可以 但是我跟R比较不熟)
: : [程式问题]: 资料处理
: : [软体熟悉度]:熟悉
: : [问题叙述]:
: : 我的资料大概长像这样
: : No是每次检验的流水号 ID相同是同一个人
: : GOT和GPT是在不同时间点做的检验数值
: : 但不是每个时间点 都有两个检查都做
: : 而且不同人做检查的时间点也都不相同
: : No ID Month GOT GPT
: : 1 1 0.2 41 34
: : 2 1 2.8 42 .
: : 3 1 3.5 43 36
: : 4 1 3.7 44 37
: : 5 1 4.9 45 38
: : 6 1 5.5 51 39
: : 7 1 5.7 52 40
: : 8 1 6.0 53 .
: : 9 1 6.2 54 .
: : 10 2 0.9 20 21
: : 11 2 4.0 22 .
: : 12 2 4.1 24 25
: : 13 2 5.7 . 36
: : 14 2 6.0 30 .
: : 以下是我期望的资料型态
: : ID GOT_0M GOT_3M GOT_6M GPT_0M GPT_3M GPT_6M
: : 1 41(0.2) 42(2.8) 53(6.0) 34(0.2) 36(3.5) 40(5.7)
: : 2 20(0.9) 22(4.0) 30(6.0) 21(0.9) . 36(5.7)
: : 我想要把资料转置成横的,以GOT为例,
: : 我要创造三个变项 GOT_0M GOT_3M GOT_6M (分别为0,3,6个月的GOT)
: : 这三个时间点若无完全符合的值, 则抓正负一个月内的值来代替
: : 若正负一个月内都无值, 则missing
: : 值後面括号(是用哪个时间点的值)
: : ---
: : 以上大概是我的资料型态
: : 实际的资料 有好几百人的资料
: : 每个人大概都有30~100个检验的时间点
: : 检验数值(如GOT,GPT之类的) 大概有10几个
: : 而我们要取的时间点实际上也不只有三个(0,3,6,12,18,24,36,48M)
: : 感觉有点复杂(囧)
: : 我目前大概查一下 可能用PROC SQL先写出来
: : 再看改成MACRO
: : (我今天挣扎了一下午 只写了一点点简单的 完全离目标很远XD)
: : 想请问看板上的高手有无建议要用什麽statement写比较适合?
: : 我再研究一下
: : 或是若有高手可以帮忙写出来
: : 小小3000P表达感谢!
: : 非常谢谢!!
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 61.224.48.248
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/Statistics/M.1459003870.A.437.html
1F:→ yonny: PS. 如果前後值相同 我写成求平均~ 03/26 22:54