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 action Either FETCH a current or previous record, or INITialise.
38 @param record The relative (to current) position of the previous observation
39 to return.
40 @param history= The number of records to retain in the hash table. Default=5
41 @param prefix= the prefix to give to the variables used to store the hash name
42 and index. Default=mp_prevobs
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;