复杂的 SAS 循环和排列问题:需要计算所有日期变量之间的天数并找到符合条件的集合
Complicated SAS Loop & Permutation Problem: Need to Calculate # of Days Between All Date Variables and Find Set That Matches Criteria
我有一个包含 6 个日期和 6 种类型的广泛数据集。数据集有 2 到 400 万行,所以我需要最有效的方法来做到这一点。每个类型编号对应于日期编号。我需要比较每个日期和类型(一次 2 个)并找到满足这些要求的 最早 日期:
条件
如果两种类型都= P,则日期之间的差异必须为17或更多。
所有其他组合必须相隔 24 天或更长时间
我必须找到最早的日期匹配。所以在我的示例中,我的“有”数据集包含所有 6 个条目。我想要的数据集有两个最早的日期满足我的标准,即 2 和 4。我可以手动计算,但无法弄清楚如何在 SAS 中进行计算。我很想在某种迭代宏程序中使用它,而不是占用数百行代码。
我纸上谈兵的逻辑
- 计算每个admin_date(所有排列)之间的天数
- 忽略相隔至少 17 天的任何内容
- 在>=17和<24的中,看两个类型是否都是P,如果是,取第2个日期最早的组合。
- 如果不是,检查>=24的那些。取最早的第2个日期的组合。
我对日差的计算:
1 & 2: 1天(忽略,小于17)
1 & 3: 7 天(忽略,小于 17)
1 & 4: 18天(忽略,不是两个P)
1 & 5: 20 天(忽略,不是两个 P)
1 & 6: 34 天 (考虑, ge 24)
2 & 3: 6 天(忽略,小于 17)
2 & 4: 17天(考虑,ge 17,都是P)
2 & 5: 19天(考虑,ge 17,都是P)
2 & 6: 33 天 (考虑, ge 24)
等等
在所有符合条件的集合中:1和6、2和4、2和5、2和6。4是最早的,所以我想要。
data have;
input person $
admin_date1 : ?? mmddyy10.
admin_date2 : ??mmddyy10.
admin_date3 : ?? mmddyy10.
admin_date4 : ?? mmddyy10.
admin_date5 : ?? mmddyy10.
admin_date6 : ?? mmddyy10.
type1 $
type2 $
type3 $
type4 $
type5 $
type6 $;
format admin_date1 mmddyy10.
admin_date2 mmddyy10.
admin_date3 mmddyy10.
admin_date4 mmddyy10.
admin_date5 mmddyy10.
admin_date6 mmddyy10.;
datalines;
JohnDoe 01/12/2021 01/13/2021 01/19/2021 01/30/2021 02/01/2021 02/15/2021 M P M P P J
;
run;
data want;
input person $
admin_date1 : ?? mmddyy10.
admin_date2 : ??mmddyy10.
type1 $
type2 $
;
format admin_date1 mmddyy10.
admin_date2 mmddyy10.
;
datalines;
JohnDoe 01/13/2021 01/30/2021 P P
;
run;
have dataset
want dataset
如果转置为 long,这将非常容易。基本上只是用指向下一行的 point
做第二个嵌套 set
并遍历同一个人的其他记录。它不是 超级 快,有更快的方法,但它很简单,除非你有数百万行。
data have_long( rename=(_admin_date = admin_date _type = type));
set have;
array admin_date[6];
array type[6];
do _i = 1 to dim(admin_date);
_admin_date = admin_date[_i];
_type = type[_i];
output;
end;
keep person _admin_date _type;
format _admin_date mmddyy10.;
run;
data want;
set have_long nobs=nobs;
if type = 'P';
do _i = _n_ + 1 to nobs until (person ne _person);
set have_long(rename=(person=_person type=_type admin_date=admin_date2)) point=_i;
if person eq _person and admin_date2 ge (admin_date + 17) and _type = 'P' then do;
output;
leave;
end;
end;
drop _:;
run;
另一个类似的解决方案是使用散列 table,只要 have_long
能够加载到内存中即可。这可能会更快,也可能不会,具体取决于数据的各种细节。
data want;
if _n_ eq 1 then do; *declare the hash table;
declare hash hl(dataset:'have_long', ordered:'a', multidata:'y'); *ordered ascending and multiple records per key is allowed;
hl.defineKey('person','type');
hl.defineData('admin_date');
hl.defineDone();
end;
format admin_date1 mmddyy8.;
set have_long;
if type = 'P';
admin_date1 = admin_date; *set the original admin_date aside;
rc = hl.find(); *find the first match;
do while (rc eq 0 and admin_date1 gt (admin_date - 17)); *loop unless we either run out of matches or find a valid date;
rc = hl.find_next();
end;
if rc eq 0 then output; *if we exited due to a valid date then output;
run;
我有一个包含 6 个日期和 6 种类型的广泛数据集。数据集有 2 到 400 万行,所以我需要最有效的方法来做到这一点。每个类型编号对应于日期编号。我需要比较每个日期和类型(一次 2 个)并找到满足这些要求的 最早 日期:
条件
如果两种类型都= P,则日期之间的差异必须为17或更多。
所有其他组合必须相隔 24 天或更长时间
我必须找到最早的日期匹配。所以在我的示例中,我的“有”数据集包含所有 6 个条目。我想要的数据集有两个最早的日期满足我的标准,即 2 和 4。我可以手动计算,但无法弄清楚如何在 SAS 中进行计算。我很想在某种迭代宏程序中使用它,而不是占用数百行代码。
我纸上谈兵的逻辑
- 计算每个admin_date(所有排列)之间的天数
- 忽略相隔至少 17 天的任何内容
- 在>=17和<24的中,看两个类型是否都是P,如果是,取第2个日期最早的组合。
- 如果不是,检查>=24的那些。取最早的第2个日期的组合。
我对日差的计算:
1 & 2: 1天(忽略,小于17)
1 & 3: 7 天(忽略,小于 17)
1 & 4: 18天(忽略,不是两个P)
1 & 5: 20 天(忽略,不是两个 P)
1 & 6: 34 天 (考虑, ge 24)
2 & 3: 6 天(忽略,小于 17)
2 & 4: 17天(考虑,ge 17,都是P)
2 & 5: 19天(考虑,ge 17,都是P)
2 & 6: 33 天 (考虑, ge 24)
等等
在所有符合条件的集合中:1和6、2和4、2和5、2和6。4是最早的,所以我想要。
data have;
input person $
admin_date1 : ?? mmddyy10.
admin_date2 : ??mmddyy10.
admin_date3 : ?? mmddyy10.
admin_date4 : ?? mmddyy10.
admin_date5 : ?? mmddyy10.
admin_date6 : ?? mmddyy10.
type1 $
type2 $
type3 $
type4 $
type5 $
type6 $;
format admin_date1 mmddyy10.
admin_date2 mmddyy10.
admin_date3 mmddyy10.
admin_date4 mmddyy10.
admin_date5 mmddyy10.
admin_date6 mmddyy10.;
datalines;
JohnDoe 01/12/2021 01/13/2021 01/19/2021 01/30/2021 02/01/2021 02/15/2021 M P M P P J
;
run;
data want;
input person $
admin_date1 : ?? mmddyy10.
admin_date2 : ??mmddyy10.
type1 $
type2 $
;
format admin_date1 mmddyy10.
admin_date2 mmddyy10.
;
datalines;
JohnDoe 01/13/2021 01/30/2021 P P
;
run;
have dataset
want dataset
如果转置为 long,这将非常容易。基本上只是用指向下一行的 point
做第二个嵌套 set
并遍历同一个人的其他记录。它不是 超级 快,有更快的方法,但它很简单,除非你有数百万行。
data have_long( rename=(_admin_date = admin_date _type = type));
set have;
array admin_date[6];
array type[6];
do _i = 1 to dim(admin_date);
_admin_date = admin_date[_i];
_type = type[_i];
output;
end;
keep person _admin_date _type;
format _admin_date mmddyy10.;
run;
data want;
set have_long nobs=nobs;
if type = 'P';
do _i = _n_ + 1 to nobs until (person ne _person);
set have_long(rename=(person=_person type=_type admin_date=admin_date2)) point=_i;
if person eq _person and admin_date2 ge (admin_date + 17) and _type = 'P' then do;
output;
leave;
end;
end;
drop _:;
run;
另一个类似的解决方案是使用散列 table,只要 have_long
能够加载到内存中即可。这可能会更快,也可能不会,具体取决于数据的各种细节。
data want;
if _n_ eq 1 then do; *declare the hash table;
declare hash hl(dataset:'have_long', ordered:'a', multidata:'y'); *ordered ascending and multiple records per key is allowed;
hl.defineKey('person','type');
hl.defineData('admin_date');
hl.defineDone();
end;
format admin_date1 mmddyy8.;
set have_long;
if type = 'P';
admin_date1 = admin_date; *set the original admin_date aside;
rc = hl.find(); *find the first match;
do while (rc eq 0 and admin_date1 gt (admin_date - 17)); *loop unless we either run out of matches or find a valid date;
rc = hl.find_next();
end;
if rc eq 0 then output; *if we exited due to a valid date then output;
run;