2015-02-10 50 views
1

这是我的第一篇文章,所以请让我知道,如果我不够清楚。这是我想要做的 - 这是我的数据集。我的做法是有一个滞后的做循环,但结果是垃圾。现在SAS做循环+滞后函数?

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; 

,我想产生一个新列 - “替换”基于以下逻辑:

  1. 如果其滞后组件的maxDate之前发生了记录的MINDATE,它不能进行更换它。如果不能替代,则跳过前进(如2,3,4不能替换1,但可以替换5)。
  2. 否则...如果mindate小于30天,替换= Y.如果不是,则替换= N.一旦一个记录替换另一个(所以在这种情况下,5替换1,因为02/02/2013但是如果它是一个N的上面一个记录,它仍然可以是一个其他记录的Y,所以,现在对6进行评估对2, 7对3等,因为这两个组合都是“Y”,所以现在评估8与4相比,但是因为其相对于4的最大值的相对大于30,它是N.但是,它接着针对
  3. 评估在...

我应该在100个记录数据集中,这将暗示第100条记录可以在技术上取代第1条记录,所以我一直在尝试循环内的滞后。任何提示/帮助非常感谢!预期输出:

     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 
+0

你能发布你的预期输出吗?我在想,SQL自加入是你需要做的,而不是循环/滞后。你尝试过的任何代码都有帮助:) – Reeza 2015-02-10 04:58:58

+0

@Reeza - 这可能也可以使用双DOW循环完成。 – user667489 2015-02-10 08:24:45

+0

@ Reeza。谢谢。预期的输出是: – AidKulesh 2015-02-10 14:54:55

回答

1

我认为这是正确的,如果提问者弄错关于更多= Y为实测值= 12

/*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 $1; 

    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,显示每行替换哪个前一行。

+0

非常感谢。这正是我所期待的。是的,我错误地认为Obs = 12,因为它确实超过了30天。 – AidKulesh 2015-02-11 02:47:46

+0

不错,我从来没有考虑过'*直到'或者'_N_'超载。 – SRSwift 2015-02-11 19:24:33

1

这是一个使用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报表可合并带来
  • 最终的连接可能是在数据步骤中用一点额外的逻辑代替
+0

非常感谢。我没有想到使用哈希表,但我可以看到它在这里如何派上用场。 – AidKulesh 2015-02-11 02:48:18