SAS做循环+滞后功能?
SAS do loop + lag function?
这是我的第一个post,所以如果我不够清楚,请告诉我。这是我正在尝试做的 - 这是我的数据集。我的方法是一个有延迟的 do 循环,但结果是垃圾。
data a;
input @1 obs @4 mindate mmddyy10. @15 maxdate mmddyy10.;
format mindate maxdate date9.;
datalines;
1 01/02/2013 01/05/2013
2 01/02/2013 01/05/2013
3 01/02/2013 01/05/2013
4 01/03/2013 01/06/2013
5 02/02/2013 02/08/2013
6 02/02/2013 02/08/2013
7 02/02/2013 02/08/2013
8 03/10/2013 03/11/2013
9 04/02/2013 04/22/2013
10 04/10/2013 04/22/2013
11 05/04/2013 05/07/2013
12 06/10/2013 06/20/2013
;
run;
现在,我正在尝试根据以下逻辑生成一个新专栏 - "Replacement":
- 如果记录的 mindate 发生在其滞后的 maxdate 之前,则不能替代它。如果不能替换,则向前跳(所以- 2,3,4 不能替换 1,但 5 可以)。
- 否则...如果 mindate 少于 30 天,替换 = Y。如果不是,替换 = N。一旦一条记录替换了另一条记录(因此,在这种情况下,5 确实替换了 1,因为 02/02 /2013 比 2013 年 1 月 5 日 <30,它不能复制作为另一条记录的替代品。但如果上面的一条记录是 N,它仍然可以是其他记录的 Y。因此,现在评估 6 2、7 对 3 等。由于这两个组合都是 "Y",因此现在对 8 和 4 进行评估,但因为它的 mindate >30 相对于 4 的 maxdate,所以它是 N。但是,然后根据
- 等等...
我应该在 100 条记录的数据集中,这意味着第 100 条记录在技术上可以取代第 1 条,所以我一直在尝试循环内的滞后。非常感谢任何 tips/help!预期输出:
obs mindate maxdate Replacement
1 02JAN2013 05JAN2013
2 02JAN2013 05JAN2013
3 02JAN2013 05JAN2013
4 03JAN2013 06JAN2013
5 02FEB2013 08FEB2013 Y
6 02FEB2013 08FEB2013 Y
7 02FEB2013 08FEB2013 Y
8 10MAR2013 11MAR2013 Y
9 02APR2013 22APR2013 Y
10 10APR2013 22APR2013 N
11 04MAY2013 07MAY2013 Y
12 10JUN2013 20JUN2013 Y
这是一个使用 SQL 和哈希表的解决方案。这不是最佳方法,但它是我想到的第一种方法。
/* Join the input with its self */
proc sql;
create table b as
select
a1.obs,
a2.obs as obs2
from a as a1
inner join a as a2
/* Set the replacement criteria */
on a1.maxdate < a2.mindate <= a1.maxdate + 30
order by a2.obs, a1.obs;
quit;
/* Create a mapping for replacements */
data c;
set b;
/* Create two empty hash tables so we can look up the used observations */
if _N_ = 1 then do;
declare hash h();
h.definekey("obs");
h.definedone();
declare hash h2();
h2.definekey("obs2");
h2.definedone();
end;
/* Check if we've already used this observation as a replacement */
if h2.find() then do;
/* Check if we've already replaced his observation */
if h.find() then do;
/* Add the observations to the hash table and output */
h2.add();
h.add();
output;
end;
end;
run;
/* Combine the replacement map with the original data */
proc sql;
select
a.*,
ifc(c.obs, "Y", "N") as Replace,
c.obs as Replaces
from a
left join c
on a.obs = c.obs2
order by a.obs;
quit;
有几种方法可以对此进行简化:
- 日期可以通过第一个
proc sql
if
条语句可以合并
- 最后的连接可以用数据步骤中的一些额外逻辑代替
如果提问者误认为 obs = 12 的替换 = Y,我认为这是正确的。
/*Get number of obs so we can build a temporary array to hold the dataset*/
data _null_;
set have nobs= nobs;
call symput("nobs",nobs);
stop;
run;
data want;
/*Load the dataset into a temporary array*/
array dates[2,&NOBS] _temporary_;
if _n_ = 1 then do _n_ = 1 by 1 until(eof);
set have end = eof;
dates[1,_n_] = maxdate;
dates[2,_n_] = 0;
end;
set have;
length replacement ;
replacement = 'N';
do i = 1 to _n_ - 1 until(replacement = 'Y');
if dates[2,i] = 0 and 0 <= mindate - dates[1,i] <= 30 then do;
replacement = 'Y';
dates[2,i] = _n_;
replaces = i;
end;
end;
drop i;
run;
如果您愿意,可以使用散列对象 + 散列迭代器来代替临时数组。我还包含了一个额外的变量,replaces
,以显示每行替换前一行。
这是我的第一个post,所以如果我不够清楚,请告诉我。这是我正在尝试做的 - 这是我的数据集。我的方法是一个有延迟的 do 循环,但结果是垃圾。
data a;
input @1 obs @4 mindate mmddyy10. @15 maxdate mmddyy10.;
format mindate maxdate date9.;
datalines;
1 01/02/2013 01/05/2013
2 01/02/2013 01/05/2013
3 01/02/2013 01/05/2013
4 01/03/2013 01/06/2013
5 02/02/2013 02/08/2013
6 02/02/2013 02/08/2013
7 02/02/2013 02/08/2013
8 03/10/2013 03/11/2013
9 04/02/2013 04/22/2013
10 04/10/2013 04/22/2013
11 05/04/2013 05/07/2013
12 06/10/2013 06/20/2013
;
run;
现在,我正在尝试根据以下逻辑生成一个新专栏 - "Replacement":
- 如果记录的 mindate 发生在其滞后的 maxdate 之前,则不能替代它。如果不能替换,则向前跳(所以- 2,3,4 不能替换 1,但 5 可以)。
- 否则...如果 mindate 少于 30 天,替换 = Y。如果不是,替换 = N。一旦一条记录替换了另一条记录(因此,在这种情况下,5 确实替换了 1,因为 02/02 /2013 比 2013 年 1 月 5 日 <30,它不能复制作为另一条记录的替代品。但如果上面的一条记录是 N,它仍然可以是其他记录的 Y。因此,现在评估 6 2、7 对 3 等。由于这两个组合都是 "Y",因此现在对 8 和 4 进行评估,但因为它的 mindate >30 相对于 4 的 maxdate,所以它是 N。但是,然后根据
- 等等...
我应该在 100 条记录的数据集中,这意味着第 100 条记录在技术上可以取代第 1 条,所以我一直在尝试循环内的滞后。非常感谢任何 tips/help!预期输出:
obs mindate maxdate Replacement
1 02JAN2013 05JAN2013
2 02JAN2013 05JAN2013
3 02JAN2013 05JAN2013
4 03JAN2013 06JAN2013
5 02FEB2013 08FEB2013 Y
6 02FEB2013 08FEB2013 Y
7 02FEB2013 08FEB2013 Y
8 10MAR2013 11MAR2013 Y
9 02APR2013 22APR2013 Y
10 10APR2013 22APR2013 N
11 04MAY2013 07MAY2013 Y
12 10JUN2013 20JUN2013 Y
这是一个使用 SQL 和哈希表的解决方案。这不是最佳方法,但它是我想到的第一种方法。
/* Join the input with its self */
proc sql;
create table b as
select
a1.obs,
a2.obs as obs2
from a as a1
inner join a as a2
/* Set the replacement criteria */
on a1.maxdate < a2.mindate <= a1.maxdate + 30
order by a2.obs, a1.obs;
quit;
/* Create a mapping for replacements */
data c;
set b;
/* Create two empty hash tables so we can look up the used observations */
if _N_ = 1 then do;
declare hash h();
h.definekey("obs");
h.definedone();
declare hash h2();
h2.definekey("obs2");
h2.definedone();
end;
/* Check if we've already used this observation as a replacement */
if h2.find() then do;
/* Check if we've already replaced his observation */
if h.find() then do;
/* Add the observations to the hash table and output */
h2.add();
h.add();
output;
end;
end;
run;
/* Combine the replacement map with the original data */
proc sql;
select
a.*,
ifc(c.obs, "Y", "N") as Replace,
c.obs as Replaces
from a
left join c
on a.obs = c.obs2
order by a.obs;
quit;
有几种方法可以对此进行简化:
- 日期可以通过第一个
proc sql
if
条语句可以合并- 最后的连接可以用数据步骤中的一些额外逻辑代替
如果提问者误认为 obs = 12 的替换 = Y,我认为这是正确的。
/*Get number of obs so we can build a temporary array to hold the dataset*/
data _null_;
set have nobs= nobs;
call symput("nobs",nobs);
stop;
run;
data want;
/*Load the dataset into a temporary array*/
array dates[2,&NOBS] _temporary_;
if _n_ = 1 then do _n_ = 1 by 1 until(eof);
set have end = eof;
dates[1,_n_] = maxdate;
dates[2,_n_] = 0;
end;
set have;
length replacement ;
replacement = 'N';
do i = 1 to _n_ - 1 until(replacement = 'Y');
if dates[2,i] = 0 and 0 <= mindate - dates[1,i] <= 30 then do;
replacement = 'Y';
dates[2,i] = _n_;
replaces = i;
end;
end;
drop i;
run;
如果您愿意,可以使用散列对象 + 散列迭代器来代替临时数组。我还包含了一个额外的变量,replaces
,以显示每行替换前一行。