BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
Reeza
Super User
How much over are you? Changing the prefix from LAST_ to something shorter may be a quick solution, eg, P_ for prior values would give you 3 more characters to work with. If you're over by a lot then you need to consider renaming.
ANKH1
Pyrite | Level 9

We were not over by much. Using P_ solved it! Thanks again.

Astounding
PROC Star

For most of this, macros are not really needed.  This is a program that automatically adapts to the variables you have, except for determining names for the new variables.  That part you have to add yourself.

 

data want;
   do until (last.id);
      update have (obs=0) have;
      by id;
   end;
   *** The next 3 lines are being hard coded.  If you are clever, it can be automated;
   late_bp = bp;
   late_weight = weight;
   late_bc = bc;
   do until (last.id);
      set have;
      by id;
      output;
call missing (of late_:); end; run;

The code is untested, so post again if something isn't working for you.

Quentin
Super User

I like @Astounding's  approach (and wish I could write code like that without testing it : ).

 

As mentioned, the block of code to create the late_ variables could be automated with a macro.  Since the variable names you want to LOCF will differ, it's helpful if you have a utility macro that will give you a list of the variables in a dataset, something like:

 

%macro VarList(data) ;
  %local rc varnames ;
  %let rc = %sysfunc(dosubl(%nrstr(
    proc contents data=&data
      out=__VarList(keep=name) noprint ;
    run ;
    proc sql noprint ;
      select name into :varlist separated by ' '
      from __VarList ;   
      drop table __VarList ;
    quit ;
  )));

&varlist /*return*/
%mend ;

Then you can use that macro to build a list of the variables that need to be locf'd.  In below macro, instead of generating an assignment statement to create each last_ variable, I used a RENAME option on the UPDATE statement.  

 

%macro locf(data=
           ,out=
           ,by=id
           ,nolocf=form_number
           ) ;

  %local
    varlist    /*list of variables in data that will be locf*/
    renamelist /*list of renames to add last_ prefix */
    i
    vari
  ;

  %*build rename list ;
  %let varlist=%varlist(&data(drop=&by &nolocf)) ;

  %do i=1 %to %sysfunc(countw(&varlist,%str( ))) ;
    %let vari=%scan(&varlist,&i,%str( )) ;
    %let renamelist=&renamelist &vari=last_&vari  ;
  %end ;
  
  %*put &=varlist &=renamelist ;

  data &out;
     do until (last.%sysfunc(scan(&by,-1,%str( ))));
        update &data (rename=(&renamelist) obs=0) &data(rename=(&renamelist) );
        by &by;
     end;

     do until (last.%sysfunc(scan(&by,-1,%str( ))));
        set &data;
        by &by;
        output; 
        call missing (of last_:);
     end;
  run;
%mend locf ;

Test like:

data have ;
input ID$ bp weight bc form_number;
datalines;
1 120 78 178 1
1 134 80 177 2
1 143 .  176 3
2 111 58 .   1
3 154 55 160 1
3 178 56 144 2
;
run;

%locf(data=have,out=want)

proc print data=want ;
run ;

That said, I agree with @Reeza also.  The structure of the input data is not weird.  It's great that the input data is in a vertical format.  The output structure you want is unusual, where you essentially merge the results of LOCF back onto the data but only for the first record of each ID.

BASUG is hosting free webinars Next up: Mike Raithel presenting on validating data files on Wednesday July 17. Register now at the Boston Area SAS Users Group event page: https://www.basug.org/events.
Tom
Super User Tom
Super User

If you want the last non-missing value per variable then treat the whole dataset as a series of transactions by using the UPDATE statement.

data test;
  input ID $ bp weight bc form_number;
datalines;
1 120 78 178 1
1 134 80 177 2
1 143 . 176 3
2 111 58 . 1
3 154 55 160 1
3 178 56 144 2
;

data want;
  update test(obs=0) test;
  by id;
run;

Which would result in:

                                     form_
Obs    ID     bp    weight     bc    number

 1     1     143      80      176       3
 2     2     111      58                1
 3     3     178      56      144       2

If you need to do it for multiple datasets then get the list of datasets into a dataset and use that to generate the code.

data datasets;
   input input :$41. output :$41.;
cards;
work.test work.want
;

filename code temp;
data _null_;
  set datasets;
  file code;
  put 'data ' output ';' / '  update ' input '(obs=0) ' input ';' / '  by id;' / 'run;' ;
run;
%include code / source2;

Ready to join fellow brilliant minds for the SAS Hackathon?

Build your skills. Make connections. Enjoy creative freedom. Maybe change the world. Registration is now open through August 30th. Visit the SAS Hackathon homepage.

Register today!
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
  • 34 replies
  • 1410 views
  • 12 likes
  • 7 in conversation