BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
endofline
Obsidian | Level 7

Hi I'm trying to dynamically change a name of a variable being used in some if/then statements based upon the observation line information. 

 

Data Structure 

 

 

Patient_ID Admit_date Member_200801 …(to)…. Member_200812
1 1/1/2008 1   0
1 11/2/2008 1   0
2 4/5/2008 0   0
3 12/10/2008 0   1
3 6/8/2008 1   0

 

What I'd like to do is based upon the admission date, reference the correct membership variable to see if they were eligible (0/1). I've tried using the symput and symputx to pull the date from the observational information, which it has, but only for the last observation in my data. 

 

 

data merged1;
set merged;


call symput('check',put(admit_date,yymmn6.));


if Member_&check = 1 then elig=1;

 

run; 

 

%put var=✓

 

When I run this code above, it does assign a date to &check but its the last observation of my data set but that's not relevant to each individual patient in my data. Any help would be great. Thanks!  

1 ACCEPTED SOLUTION

Accepted Solutions
mkeintz
PROC Star

If I understand your task correctly, you'd be better served by declaring a 2 dimensional array with variable names

member_200801 member_200802 .... member_200812 member_200901 .... member_200912 (assuming you have a two year range covering 2008 and 2009).  The 1st dimension of the array is the year, the 2nd is the month.  Then using the year and month functions inside to establish array row and column will pinpoint the desired variable:

 

data want;
  set have;
  array membs {2008:2009,12}
    member_200801-member_200812
    member_200901-member_200912;
  if membs{year(admit_date),month(admit_date)}=1 then elig=1;
run;

 

The array above has 2 rows (2008 and 2009) and 12 columns (1 through 12).

 

And if you only have one year, then a one-dimensional array:

 

data want;
  set have;
  array membs {12} member_200801-member_200812
  if membs{month(admit_date)}=1 then elig=1;
run;

 

No need for macro, single pass of the data. 

--------------------------
The hash OUTPUT method will overwrite a SAS data set, but not append. That can be costly. Consider voting for Add a HASH object method which would append a hash object to an existing SAS data set

Would enabling PROC SORT to simultaneously output multiple datasets be useful? Then vote for
Allow PROC SORT to output multiple datasets

--------------------------

View solution in original post

6 REPLIES 6
Patrick
Opal | Level 21

@endofline

SAS data steps have a compilation and a execution phase. All variables get defined in the compilation phase and though it's not possible to dynamically introduce new variables during data step execution.

Macro variables resolve before the data step gets compiled so they just allow you to dynamically write data step code BEFORE the SAS compiler then picks up this code (at this point the code is static). That's why what you're trying to do can't work.

 

Having the data in this wide instead of a long structure makes this simple task ridiculously difficult. But here you go.

data have;
  format admit_date date9.;
  member_200801='1';
  member_200805='0';
  admit_date='01jan2008'd;
  output;
  admit_date='15may2008'd;
  output;
  stop;
run;

data want(drop=_:);
  /* map variables */
  if 0 then set have;
  /* define array for variables member_.... */
  array members {*} member_:;

  /* define and load hash with name and index number of array elements */
  if _n_=1 then 
    do;
      length _el_name $32 _el_index 8;
      dcl hash h1(hashexp:3);
      h1.defineKey('_el_name');
      h1.defineData('_el_index');
      h1.defineDone();
      do _i=1 to dim(members);
        h1.add(key:upcase(vname(members(_i))), data:_i);
      end;
    end;
    call missing(of _all_);

    set have;
    /* find index number of array element with the required data */
    _rc=h1.find(key:cat('MEMBER_',put(admit_date,yymmn6.)));
    /* populated elig with the value from the identified array element */
    if not missing(_el_index) then elig=members[_el_index];
run;

Using a hash to find the required array element avoids the need to loop for every row over the array to find the array element with the required name.

Reeza
Super User

It's more complex than that unfortunately. 

 

Here's one way:

 

*Assumptions:
*You have a date for every month, if not you may have missing months
*you likely want to collapse an ID to a single record per ID
*you have more than one year of data
;

data have;
    format admit_date date9.;
    id=1;
    admit_date='01jan2008'd;
    output;
    admit_date='01Nov2008'd;
    output;
    admit_date='01jan2009'd;
    output;
    admit_date='01Nov2010'd;
    output;
    id=2;
    admit_date='15may2008'd;
    output;
    admit_date='15Dec2008'd;
    output;
    admit_date='15may2009'd;
    output;
    admit_date='15Dec2010'd;
    output;
run;


data want;
    set have;
    by id;
    array member(2008:2010, 1:12) /*Use a two dimensional array and 
                                  index array with year/month*/
                                  member_2008_1-member_2008_12 
                                  member_2009_1-member_2009_12 
                                  member_2010_1-member_2010_12 
                                    (36*0) /*initialize to 0, implicit retain*/;

*if first ID set all to 0, to prevent previous ID carrying over;
    if first.id then
        do;

            do year=2008 to 2010;

                do month=1 to 12;
                    member(year, month)=0;
                end;
            end;
        end;
        
*determine year/month, could next directly into member assignment;        
    year=year(admit_date);
    month=month(admit_date);
    
*Assign 1 to the appropriate year/month;    
    member(year, month)=1;

*output only when last record for person;
    if last.id then
        output;
run;

@endofline wrote:

Hi I'm trying to dynamically change a name of a variable being used in some if/then statements based upon the observation line information. 

 

Data Structure 

 

 

Patient_ID Admit_date Member_200801 …(to)…. Member_200812
1 1/1/2008 1   0
1 11/2/2008 1   0
2 4/5/2008 0   0
3 12/10/2008 0   1
3 6/8/2008 1   0

 

What I'd like to do is based upon the admission date, reference the correct membership variable to see if they were eligible (0/1). I've tried using the symput and symputx to pull the date from the observational information, which it has, but only for the last observation in my data. 

 

 

data merged1;
set merged;


call symput('check',put(admit_date,yymmn6.));


if Member_&check = 1 then elig=1;

 

run; 

 

%put var=✓

 

When I run this code above, it does assign a date to &check but its the last observation of my data set but that's not relevant to each individual patient in my data. Any help would be great. Thanks!  


 

mkeintz
PROC Star

If I understand your task correctly, you'd be better served by declaring a 2 dimensional array with variable names

member_200801 member_200802 .... member_200812 member_200901 .... member_200912 (assuming you have a two year range covering 2008 and 2009).  The 1st dimension of the array is the year, the 2nd is the month.  Then using the year and month functions inside to establish array row and column will pinpoint the desired variable:

 

data want;
  set have;
  array membs {2008:2009,12}
    member_200801-member_200812
    member_200901-member_200912;
  if membs{year(admit_date),month(admit_date)}=1 then elig=1;
run;

 

The array above has 2 rows (2008 and 2009) and 12 columns (1 through 12).

 

And if you only have one year, then a one-dimensional array:

 

data want;
  set have;
  array membs {12} member_200801-member_200812
  if membs{month(admit_date)}=1 then elig=1;
run;

 

No need for macro, single pass of the data. 

--------------------------
The hash OUTPUT method will overwrite a SAS data set, but not append. That can be costly. Consider voting for Add a HASH object method which would append a hash object to an existing SAS data set

Would enabling PROC SORT to simultaneously output multiple datasets be useful? Then vote for
Allow PROC SORT to output multiple datasets

--------------------------
Tom
Super User Tom
Super User

The macro processor just generates text that is then used by SAS just as if you had originally typed it that way.  The macro processor cannot travel backwards in time. It cannot change the code of a data step after it has been compiled and started running. 

 

There is no need to do anything dynamic for your problem. Just use an ARRAY.  Calculate the index into the array based on the value or your date variable.

array member member_201801 ..... ;
index=intck('month','01JAN2018'd,admit_date)+1;
if 1 <= index <= dim(member) then elig=member(index);

If you did want to try something "dynamic" then looking into using one of the functions with X as the last letter. In this case the VVALUEX() function which can find the value of variable whose name can be the result of an expression.

elig = input(vvaluex(cats('member_',put(admit_date,yymmn6.))),32.);

 

SuryaKiran
Meteorite | Level 14

Here is another way using Transpose and SAS functions.

 

data have;
format date mmddyy10.;
input patient date mmddyy10. mem_200801 mem_200802 mem_200803 mem_200804;
datalines;
1 01/01/2008 1 0 0 0
2 02/01/2008 0 1 0 0
3 03/01/2008 0 0 0 0
4 04/01/2008 0 0 0 1
;
run;
proc transpose data=have out=trans_have prefix=new;
by patient date;
run;
data trans_have_mod;
set trans_have;
if put(date,yymmn6.)=strip(scan(_name_,2,'_')) and new1=1 then elig=1;
if elig=1;
drop _name_ new1;
run;
data want;
merge have trans_have_mod;
by patient date;
run;
Thanks,
Suryakiran
Reeza
Super User

Just a note, that if you use @SuryaKiran solution if a month is not in the data it will not be in the columns. If you need a record for every month you'll have to add that in somehow. 

 

 

sas-innovate-2024.png

Don't miss out on SAS Innovate - Register now for the FREE Livestream!

Can't make it to Vegas? No problem! Watch our general sessions LIVE or on-demand starting April 17th. Hear from SAS execs, best-selling author Adam Grant, Hot Ones host Sean Evans, top tech journalist Kara Swisher, AI expert Cassie Kozyrkov, and the mind-blowing dance crew iLuminate! Plus, get access to over 20 breakout sessions.

 

Register now!

How to Concatenate Values

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.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 6 replies
  • 5536 views
  • 5 likes
  • 6 in conversation