%macro tranmed (data=,     /* input data set              */
                var=,      /* variable list to calculate  */
                           /* the median from             */
                pctldef=5, /* initialize the percentile   */
                           /* definition for PROC         */
                           /* UNIVARIATE                  */
                uniqueid=, /* unique identifier           */
                out=);     /* output data set             */

     /* Rearrange the data to create a data set that      */
     /* contains one observation for each variable in     */
     /* VAR.  Each observation contains values for the BY */
     /* variable the identifier, and for one variable in  */
     /* VAR.                                              */
   proc transpose data=&data(keep=&var &uniqueid) out=transp;
      by &uniqueid notsorted;
   run;

     /* Calculate the median for each BY group in the     */
     /* data set that PROC TRANSPOSE created. Store the   */
     /* output in MED1. This data set, like the original  */
     /* data set, now contains one observation for each   */
     /* value of UNIQUEID. The NOTSORTED option indicates */
     /* that all observations with the same BY value are  */
     /* grouped together but that the groups are not in   */
     /* any particular order. In this macro, where we     */
     /* assume that there is only one observation in each */
     /* BY group, NOTSORTED lets you take advantage of BY */
     /* processing without sorting the data.              */
   proc univariate data=transp pctldef=&pctldef noprint;
      by &uniqueid notsorted;
      output out=med1 median=median;
   run;

     /* Merge the input and output data sets. A one-to-one*/
     /* merge is appropriate because both data sets are   */
     /* sorted by UNIQUEID and both have only one         */
     /* observation for each value of UNIQUEID.           */
   data &out;
     merge &data med1;
   run;

%mend tranmed;