/*

/ Program   : flatten.sas
/ Version   : 2.0
/ Author    : Roland Rashleigh-Berry
/ Date      : 13-Feb-2007
/ Purpose   : To "flatten" data so there is only one observation per "by group"
/ SubMacros : %words %varnum
/ Notes     : This calls proc transpose repeatedly for each variable you specify.
/             You must have sorted on what you define to the bygroup= parameter
/             plus any other variables required to put the data in the correct
/             sorted order. Typically, you will have sorted by the bygroup=
/             variables plus date or time (or both) variables. A variable is
/             added that contains the count of the number of observations in
/             each by group. Variables will be given the suffix 1, 2, etc.
/ Usage     : %flatten(dsin=test,bygroup=by1 by2,vars=str num)
/ 
/===============================================================================
/ PARAMETERS:
/-------name------- -------------------------description------------------------
/ dsin=             Input dataset
/ vars=             Variables you want transposing
/ dsout=            Output dataset
/ bygroup=          The "by group" variables
/ nobs=nobs         Name of the variable to contain the number of observation
/                   per "by group"
/===============================================================================
/ AMENDMENT HISTORY:
/ init --date-- mod-id ----------------------description------------------------
/ rrb  25Jun03         Make nobs= a mandatory parameter and write highest value
/                      out to global macro variable _maxn_. Now version 2
/ rrb  13Feb07         "macro called" message added
/===============================================================================
/ This is public domain software. No guarantee as to suitability or accuracy is
/ given or implied. User uses this code entirely at their own risk.
/=============================================================================*/

%put MACRO CALLED: flatten v2.0;

%macro flatten(dsin=,
               vars=,
              dsout=,
            bygroup=,
               nobs=nobs
              );

%local error i var;
%let error=0;

%global _maxn_;
%let _maxn_=0;


       /*-----------------------------------------*
           Check we have enough parameters set
        *-----------------------------------------*/

%if not %length(&dsin) %then %do;
  %let error=1;
  %put ERROR: (flatten) No input dataset defined to dsin=;
%end;

%if not %length(&vars) %then %do;
  %let error=1;
  %put ERROR: (flatten) No transpose variables defined to vars=;
%end;

%if not %length(&bygroup) %then %do;
  %let error=1;
  %put ERROR: (flatten) No by group variables defined to bygroup=;
%end;

%if not %length(&dsout) %then %let dsout=%scan(&dsin,1,%str(%());

%if not %length(&nobs) %then %let nobs=nobs;



       /*-----------------------------------------*
                Start processing the data
        *-----------------------------------------*/

data _flatten;
  set &dsin;
  keep &bygroup &vars;
run;



       /*-----------------------------------------*
                   Add the observation count
        *-----------------------------------------*/

proc summary nway missing data=_flatten;
  class &bygroup;
  output out=_flatnobs(drop=_type_ rename=(_freq_=&nobs));
run;

proc summary nway data=_flatnobs;
  id &nobs;
  output out=_flatn(drop=_type_ _freq_);
run;

data _null_;
  set _flatn;
  call symput('_maxn_',compress(put(&nobs,13.)));
run;

data _flatten;
  merge _flatnobs _flatten;
  by &bygroup;
run;



       /*-----------------------------------------*
             Transpose each variable in turn
        *-----------------------------------------*/

%do i=1 %to %words(&vars);

  %let var=%scan(&vars,&i,%str( ));

  proc transpose data=_flatten prefix=&var
                 out=_flatten&i(drop=_name_);
    by &bygroup &nobs;
    var &var;
  run;

%end;



       /*-----------------------------------------*
                Create final output dataset
        *-----------------------------------------*/

data _flatout;
  merge
  %do i=1 %to %words(&vars);
    _flatten&i
  %end;
  ;
  by &bygroup;
run;

data &dsout;
  set _flatout;
  %if %varnum(_flatout,_label_) %then %do;
    drop _label_;
  %end;
run;


       /*-----------------------------------------*
                      Tidy up and exit
        *-----------------------------------------*/

proc datasets nolist;
  delete _flat:
  ;
run;
quit;


%goto skip;
%error:
%put ERROR: (flatten) Leaving flatten macro due to error(s) listed;
%skip:
%mend;