I would like to find an easier solution to a problem I am having. I can manage this task, however it's though several steps outside SAS (SQL LIMIT). At best I don't get all the observations correctly in SAS alone.
I have used a POINT and SET however I can't get the BY variables working.
Would be good to have a data step or PROC SQL to be able to achieve this in SAS alone.
The code I have modified is below. Currently, it does not discriminate between the ID, it just follows the order of the set.
data WANT;
set HAVE nobs=nobs;
if FLAG = 'Y' then do;
current = _N_;
prev = current - 1;
next = current + 1;
if prev > 0 then do;
set HAVE point = prev;
POSTION = 'PREVIOUS';
output;
end;
set HAVE point = current;
POSTION = 'CURRENT';
output;
if next <= nobs then do;
set HAVE point = next;
POSTION = 'NEXT';
output;
end;
end;
run;
I have a table with multiple ID's. Within each ID there is an observation with a flag.
OBS | ID | FLAG |
---|---|---|
1 | RED | |
2 | RED | |
3 | RED | Y |
4 | RED | |
5 | RED | |
6 | BLUE | Y |
7 | BLUE | |
8 | GREEN | |
9 | GREEN | Y |
10 | PURPLE | |
11 | PURPLE | |
12 | PURPLE | |
13 | PURPLE | Y |
14 | PURPLE | |
15 | PURPLE |
I would like to locate and tag the current, previous and next observation based on OBS order (or dates) and the ID as sorted above.
The output would look like below.
OBS | ID | FLAG | POSTION |
---|---|---|---|
1 | RED | ||
2 | RED | PREVIOUS | |
3 | RED | Y | CURRENT |
4 | RED | NEXT | |
5 | RED | ||
6 | BLUE | Y | CURRENT |
7 | BLUE | NEXT | |
8 | GREEN | PREVIOUS | |
9 | GREEN | Y | CURRENT |
10 | PURPLE | ||
11 | PURPLE | ||
12 | PURPLE | PREVIOUS | |
13 | PURPLE | Y | CURRENT |
14 | PURPLE | NEXT | |
15 | PURPLE |
Any help would be greatly appreciated.
/* dertermine the succeeding records, using lag() function and original order */
data int1 (drop=oldflg oldid);
set have;
length POSTION $ 8;
count = _N_;
oldflg = lag(flag);
oldid = lag(id);
if oldflg = 'Y' and oldid = id then POSTION = 'NEXT';
if flag = 'Y' then POSTION = 'CURRENT';
run;
/* turn the dataset upside down */
proc sort data=int1;
by descending count;
run;
/* dertermine the preceding records, using the reverse order */
data int2 (drop=oldflg oldid);
set int1;
oldflg = lag(flag);
oldid = lag(id);
if oldflg = 'Y' and oldid = id then POSTION = 'PREVIOUS';
run;
/* establish original order */
proc sort data=int2 out=want (drop=count);
by count;
run;
/* dertermine the succeeding records, using lag() function and original order */
data int1 (drop=oldflg oldid);
set have;
length POSTION $ 8;
count = _N_;
oldflg = lag(flag);
oldid = lag(id);
if oldflg = 'Y' and oldid = id then POSTION = 'NEXT';
if flag = 'Y' then POSTION = 'CURRENT';
run;
/* turn the dataset upside down */
proc sort data=int1;
by descending count;
run;
/* dertermine the preceding records, using the reverse order */
data int2 (drop=oldflg oldid);
set int1;
oldflg = lag(flag);
oldid = lag(id);
if oldflg = 'Y' and oldid = id then POSTION = 'PREVIOUS';
run;
/* establish original order */
proc sort data=int2 out=want (drop=count);
by count;
run;
As an alternative, an SQL based approach:
proc sql;
create table WANT as
select A.*,
case when A.OBS=B.OBS-1 then "Previous"
when A.OBS=B.OBS then "Current"
when A.OBS=B.OBS+1 then "Next"
else "" end as POSITION
from WORK.HAVE A
left join (select * from WORK.HAVE where FLAG="Y") B
on A.ID=B.ID
order by A.OBS;
quit;
Thank you Kurt and RW9, both awesome answers.
Nice work.
You can do it just using FIRST. and LAST. flags. At least for your sample data.
data want ;
set have;
by color flag notsorted ;
if last.flag and flag=' ' and not last.color then position='PREVIOUS';
else if flag='Y' then position='CURRENT';
else if first.flag and flag=' ' and not first.color then position='NEXT';
run;
Elegant! I know it goes without saying with all Tom's code, but I just couldn't help with this one! Thank you Tom !
Yes, interesting idea Tom, would not have thought about using flag as a grouping variable with First/Last.
data have; input OBS ID $ FLAG $; cards; 1 RED . 2 RED . 3 RED Y 4 RED . 5 RED . 6 BLUE Y 7 BLUE . 8 GREEN . 9 GREEN Y 10 PURPLE . 11 PURPLE . 12 PURPLE . 13 PURPLE Y . 14 PURPLE . 15 PURPLE . ; run; data want; merge have have(firstobs=2 keep=id flag rename=(id=_id flag=_flag)); length POSTION $ 20; lag_flag=lag(flag);lag_id=lag(id); if FLAG='Y' then POSTION='CURRENT'; else if missing(FLAG) and _FLAG='Y' and id=_id then POSTION='PREVIOUS'; else if missing(FLAG) and lag_flag='Y' and id=lag_id then POSTION='NEXT'; drop lag_: _:; run;
Xia Keshan
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.