2015-02-10 91 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