Hello,
This is my data:
Patient ID | svcdate | daysupp | prodnme | daysupp_new |
123 | 9/29/14 | 7 | ZUBSOLV | - |
123 | 10/2/14 | 30 | ZUBSOLV | - |
123 | 10/9/14 | 30 | NALTREXONE HCL | 7 |
I've computed daysupp_new as 7 for when prodnme = NALTREXONE HCL
Now I want to update the daysupp value in row #2 with the daysupp_new value from row #3 so that the data looks like this:
Patient ID | svcdate | daysupp | prodnme | daysupp_new |
123 | 9/29/14 | 7 | ZUBSOLV | - |
123 | 10/2/14 | 7 | ZUBSOLV | - |
123 | 10/9/14 | 30 | NALTREXONE HCL | 7 |
How can I do this?
I think the below graphic may illustrate what (I think) @alaxman is after. Basically, when on row 2, if the daysupp_new variable is populated on the next record (row 3), update daysupp on row 2 with the value in daysupp_new on row three. I'm recommending the "hop" macro from @LeonidBatkhan which performs a read ahead (the equivalent of a LEAD() function if there were one in SAS). Link: https://blogs.sas.com/content/sgf/2017/11/01/hopping-for-the-best-calculations-across-sas-dataset-ob...
Jim
Thank you @jimbarbour!
While the %hop macro should work fine here, an alternative way of calculating DAYSUPP_NEW could be:
Here is a code example illustrating this alternative method:
proc sort data=HAVE out=REVERSE;
by PATIENT_ID descending SVCDATE;
run;
data WANT (drop=PREDATE);
set REVERSE;
PREDATE = lag(SVCDATE);
if PRODNME eq 'NALTREXONE HCL' then DAYSUPP_NEW = PREDATE;
run;
proc sort data=WANT;
by PATIENT_ID SVCDATE;
run;
Hope this helps.
How would I use the "hop" function within this macro that i've written to get the "daysupp_new" - in order to change "daysupp" -- exactly how you have illustrated in the table below.
%macro hop(d,x,y,j);
_p_ = _n_ + &j;
if (1 le _p_ le _o_) then set &d(keep=&x rename=(&x=&y)) point=_p_ nobs=_o_;
%mend hop;
data pers_3M_12M_30DAY (drop=TEMP1);
set pers_3M_12M_30DAY;
if prodnme in ("VIVITROL", "REVIA", "NALTREXONE HCL", "NALTREXONE")
and svcdate_new le lag(svcdate_new)+ lag(daysupp)
thendo;
%hop(pers_3M_12M_30DAY,SVCDATE_NEW, TEMP1,-1)
DAYSUPP_NEW= SVCDATE-TEMP1;
end;
run;
The data I have is as follows below. If you have more data, I'm happy to run it through, but the program I've written (I'll get to that in a minute) should be able to handle different data.
DATA Drug_Data;
INFILE Datalines DSD DLM='09'x MISSOVER;
INPUT Patient_ID $
SvcDate : ANYDTDTE8.
DaySupp
ProdNme : $25.
;
FORMAT SvcDate mmddyys10.;
Datalines;
123 9/29/14 7 ZUBSOLV
123 10/2/14 30 ZUBSOLV
123 10/9/14 30 NALTREXONE HCL
;
RUN;
Next, with apologies to @LeonidBatkhan, I modified the Hop macro slightly to set a Boolean, _Found. If a record is returned, _Found =1, otherwise, _Found = 0. I wanted to NOT execute the code to update DaySupp if no next record were found.
%MACRO Hop(d, x, y, j);
_p_ = _n_ + &j;
IF (1 <= _p_ <= _o_) THEN
DO;
_Found = 1;
SET &d(KEEP=&x RENAME=(&x=&y)) POINT=_p_ NOBS=_o_;
END;
ELSE
DO;
_Found = 0;
END;
%MEND Hop;
Next, I modified the program I shared with you earlier to incorporate the Hop macro. The code is actually fairly simple and is a single step, a big advantage to the Hop macro. You could even make it simpler by deleting all my "_Prior" logic and replace it with LAG() based logic. I personally don't like LAG(). When I've used LAG() with large files, performance has been adversely affected. Manually setting _Prior type variables is more work, but performance is much better in my experience.
The trick with the program is that I'm doing a look forward not a look back. I do not read record 3 and try to back update record 2. That's not typically how SAS works. Instead, when I read record 2, I read ahead to record 3 and update record 2 in place. Variables prefixed with "_Next" are from read aheads to the record following the current record.
Here's the program:
DATA Updated_Drug_Data;
DROP _:;
SET Drug_Data;
LENGTH _Prior_ID $8;
LENGTH _Prior_Drug $25.;
LENGTH _Next_ProdNme $25.;
RETAIN _Prior_ID ' ';
RETAIN _Prior_Date 0;
RETAIN _Prior_Supp 0;
RETAIN _Prior_Drug ' ';
IF Patient_ID = _Prior_ID THEN
DO;
IF ProdNme = _Prior_Drug THEN
DO;
SvcDate = INTNX('DAYS', _Prior_Date, _Prior_Supp);
END;
%Hop(Drug_Data, ProdNme, _Next_ProdNme, 1);
IF _Found THEN
IF _Next_ProdNme IN ("VIVITROL", "REVIA", "NALTREXONE HCL", "NALTREXONE") THEN
DO;
%Hop(Drug_Data, SvcDate, _Next_SvcDate, 1);
DaySupp = INTCK('DAYS', SvcDate, _Next_SvcDate);
END;
END;
_Prior_ID = Patient_ID;
_Prior_Date = SvcDate;
_Prior_Supp = DaySupp;
_Prior_Drug = ProdNme;
RUN;
The program takes the input data:
And makes the following changes:
This reflects my understanding of what you're trying to do. Please correct me if I'm off, and we can adjust the program accordingly.
Jim
Thank you very much for this thorough explanation, really appreciate it.
While your code in its entirety did not work, your modified "hop" macro was really key, so thank you!
This is what worked:
data pers_3M_12M_30DAY ;
set pers_3M_12M_30DAY;
%Hop1(pers_3M_12M_30DAY, ProdNme, _Next_ProdNme, 1);
IF _Found THEN
IF _Next_ProdNme IN ("VIVITROL", "REVIA", "NALTREXONE HCL", "NALTREXONE")
THEN
%Hop1(pers_3M_12M_30DAY, DAYSUPP_NEW, _Next_DAYSUPP_NEW, 1);
IF _Next_DAYSUPP_NEW ne . then do;
daysupp = _Next_DAYSUPP_NEW;
drop _:;
end;
run;
Many thanks again!
Actually, this is even better than if I had just handed you a solution. You took the things you needed from my code and arranged them to fulfill your requirements. Good job! 👍 Far better to understand code advice and apply it than to simply play "monkey see, monkey do." Hopefully some of this code will come in handy as well for future applications.
Good luck, and glad I was (hopefully) able to pass on a couple of good SAS tips.
Jim
As in the last time we talked, I think the hop macro from @LeonidBatkhan would work well here. As you read each record, you want to read the daysupp_new variable from the next record. If daysupp_new is populated, you would want to update daysupp in the current record.
Jim
Your Q is ambiguous . better post more data to illustrate .
data have;
infile cards expandtabs ;
input PatientID svcdate : $20.daysupp prodnme : $20. daysupp_new;
cards;
123 9/29/14 7 ZUBSOLV .
123 10/2/14 30 ZUBSOLV .
123 10/9/14 30 NALTREXONE_HCL 7
;
data want;
set have;
if _n_=2 then do;
temp=3;
set have(keep=daysupp_new rename=(daysupp_new=daysupp)) point=temp;
end;
run;
You cannot change a value in a previous observation, but you can change a value in a current observation based on the following observation ("look-ahead"):
data have;
infile datalines dlm=',' dsd truncover;
input Id $ svcdate :mmddyy10. daysupp prodnme :$20. daysupp_new;
format svcdate yymmdd10.;
datalines;
123,9/29/14,7,ZUBSOLV,
123,10/2/14,30,ZUBSOLV,
123,10/9/14,30,NALTREXONE HCL,7
;
data want;
merge
have
have (
firstobs=2
keep=id daysupp_new
rename=(id=_id daysupp_new = _dn)
)
;
if id = _id and _dn ne . then daysupp = _dn;
drop _:;
run;
You can incorporate the logic that sets daysupp_new in this step, and avoid creating that column altogether.
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
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.
Ready to level-up your skills? Choose your own adventure.