Macros for SAS Application Developers
https://github.com/sasjs/core
mp_prevobs.sas
Go to the documentation of this file.
1 /**
2  @file
3  @brief Enables previous observations to be re-instated
4  @details Remembers the last X observations by storing them in a hash table.
5  Is a convenience over the use of lag() or retain, when an entire observation
6  needs to be restored.
7 
8  This macro will also restore automatic variables (such as _n_ and _error_).
9 
10  Example Usage:
11 
12  data example;
13  set sashelp.class;
14  calc_var=_n_*3;
15  %* initialise hash and save from PDV ;
16  %mp_prevobs(INIT,history=2)
17  if _n_ =10 then do;
18  %* fetch previous but 1 record;
19  %mp_prevobs(FETCH,-2)
20  put _n_= name= age= calc_var=;
21  %* fetch previous record;
22  %mp_prevobs(FETCH,-1)
23  put _n_= name= age= calc_var=;
24  %* reinstate current record ;
25  %mp_prevobs(FETCH,0)
26  put _n_= name= age= calc_var=;
27  end;
28  run;
29 
30  Result:
31 
32  <img src="https://imgur.com/PSjHoET.png" alt="mp_prevobs sas" width="400"/>
33 
34  Credit is made to `data _null_` for authoring this very helpful paper:
35  https://www.lexjansen.com/pharmasug/2008/cc/CC08.pdf
36 
37  @param [in] action Either FETCH a current or previous record, or INITialise.
38  @param [in] record The relative (to current) position of the previous row
39  to return.
40  @param [in] history= (5) The number of records to retain in the hash table.
41  @param [in] prefix= (mp_prevobs) The prefix to give to the variables used to
42  store the hash name and index.
43 
44  @version 9.2
45  @author Allan Bowe
46 
47 **/
48 
49 %macro mp_prevobs(action,record,history=5,prefix=mp_prevobs
50 )/*/STORE SOURCE*/;
51 %let action=%upcase(&action);
52 %let prefix=%upcase(&prefix);
53 %let record=%eval((&record+0) * -1);
54 
55 %if &action=INIT %then %do;
56 
57  if _n_ eq 1 then do;
58  attrib &prefix._VAR length=$64;
59  dcl hash &prefix._HASH(ordered:'Y');
60  &prefix._KEY=0;
61  &prefix._HASH.defineKey("&prefix._KEY");
62  do while(1);
63  call vnext(&prefix._VAR);
64  if &prefix._VAR='' then leave;
65  if &prefix._VAR eq "&prefix._VAR" then continue;
66  else if &prefix._VAR eq "&prefix._KEY" then continue;
67  &prefix._HASH.defineData(&prefix._VAR);
68  end;
69  &prefix._HASH.defineDone();
70  end;
71  /* this part has to happen before FETCHing */
72  &prefix._KEY+1;
73  &prefix._rc=&prefix._HASH.add();
74  if &prefix._rc then putlog 'adding' &prefix._rc=;
75  %if &history>0 %then %do;
76  if &prefix._key>&history+1 then
77  &prefix._HASH.remove(key: &prefix._KEY - &history - 1);
78  if &prefix._rc then putlog 'removing' &prefix._rc=;
79  %end;
80 %end;
81 %else %if &action=FETCH %then %do;
82  if &record>&prefix._key then putlog "Not enough records in &Prefix._hash yet";
83  else &prefix._rc=&prefix._HASH.find(key: &prefix._KEY - &record);
84  if &prefix._rc then putlog &prefix._rc= " when fetching " &prefix._KEY=
85  "with record &record and " _n_=;
86 %end;
87 
88 %mend mp_prevobs;