根据唯一 ID(帐户编号)检查日期是否位于不同列中的两个日期之间

问题描述 投票:0回答:1

我有一个数据集,其中包含帐号、计费周期日期和交易日期。每个帐户都应该有前两次计费周期日期,然后必须检查交易日期是否位于这两个计费周期日期之间。

我拥有的数据。

 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。

谢谢。

sas sas-macro
1个回答
0
投票

这里的主要问题是您想要为每个组向前查看一行,然后决定同时保留或删除这两行。我们可以通过将数据转换为长格式然后将其与原始数据合并来使这变得更容易:

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
,并跟踪每个组的行数。在每组开始时,我们将重置计数器。

我们只会输出如果:

  1. n
    是 < 3
  2. tran_date
    位于
    COL1
    COl2
  3. 之间
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;
© www.soinside.com 2019 - 2024. All rights reserved.