我有一个数据集,其中包含帐号、计费周期日期和交易日期。每个帐户都应该有前两次计费周期日期,然后必须检查交易日期是否位于这两个计费周期日期之间。
我拥有的数据。
input acct_num biiling_date tran_date ;
DATALINES;
1111 23FEB2020 27FEB2020
1111 23MAR2020 27FEB2020
2222 17MAR2020 22MAR2020
2222 27APR2020 22MAR2020
2222 27MAY2020 22MAR2020
3333 23JUL2018 02JUN2022
3333 23AUG2018 02JUN2022
我想要的数据。
1111 23FEB2020 27FEB2020
1111 23MAR2020 27FEB2020
2222 17MAR2020 22MAR2020
2222 27APR2020 22MAR2020
如果您在最终数据集中看到,帐户 2222 的第三次出现已被删除,帐户 3333 的整行已被删除,因为该帐户的第一个和第二个计费周期日期之间没有 tran_date。
谢谢。
这里的主要问题是您想要为每个组向前查看一行,然后决定同时保留或删除这两行。我们可以通过将数据转换为长格式然后将其与原始数据合并来使这变得更容易:
proc transpose data=have out=have_tpose(keep=acct_num COL1-COL2);
by acct_num;
var billing_date;
run;
acct_num COL1 COL2
1111 23FEB2020 23MAR2020
2222 17MAR2020 27APR2020
3333 23JUL2018 23AUG2018
我们关心的是前两行。
COL1
和 COL2
代表前两行。
现在我们可以将其合并回原始数据集,并检查每个
COL1
的交易日期是否在 COL2
和 acct_num
之间。由于我们只需要保留前两行数据,因此我们将计算行数n
,并跟踪每个组的行数。在每组开始时,我们将重置计数器。
我们只会输出如果:
n
是 < 3tran_date
位于 COL1
和 COl2
data want;
merge have
have_tpose
;
by acct_num;
if(first.acct_num) then n = 0;
n+1;
if(n < 3 AND COL1 < tran_date < COL2);
drop n COL1-COL2;
run;
acct_num billing_date tran_date
1111 23FEB2020 27FEB2020
1111 23MAR2020 27FEB2020
2222 17MAR2020 22MAR2020
2222 27APR2020 22MAR2020
要在滞后情况下执行此操作,您仍然需要分两步执行此操作:首先创建要删除的值列表,然后创建最终的过滤列表。
data drop_list;
set have;
by acct_num;
lag_billing_date = lag(billing_date);
if(first.acct_num) then do;
n = 0;
call missing(lag_billing_date);
end;
n+1;
if(n = 2) AND NOT (lag_billing_date LE tran_date LE billing_date)
then drop_flag = 1;
if(drop_flag);
keep acct_num;
run;
data want;
merge have
drop_list(in=droplist)
;
by acct_num;
if(first.acct_num) then n = 0;
n+1;
if(n < 3 AND NOT droplist);
run;
如果您想要一种非常疯狂的方法来使用哈希表一步完成此操作,那么方法如下:
data want;
set have;
by acct_num;
retain drop_flag;
/* Load the dataset into memory so we can search ahead with hash methods */
if(_N_ = 1) then do;
format billing_date2
billing_date_start
billing_date_end date9.
;
dcl hash h(dataset: 'have(rename=(billing_date = billing_date2))', multidata: 'yes');
h.defineKey('acct_num');
h.defineData('billing_date2');
h.defineDone();
call missing(billing_date2);
end;
/* For the first value of each account, get the first billing date and next billing date */
if(first.acct_num) then do;
n = 0;
drop_flag = 0;
/* Get the first value of billing date for the current acct_num */
rc = h.Find();
billing_date_start = billing_date2;
/* Get the next value of billing date for the current acct_num */
rc = h.Find_Next();
billing_date_end = billing_date2;
/* Set a drop flag if the tran date is not between the first and next value */
drop_flag = (NOT (billing_date_start LE tran_date LE billing_date_end) );
end;
/* Count each row */
n+1;
if(n < 3 AND drop_flag = 0);
keep acct_num billing_date tran_date;
run;