Search code examples
sasproc-report

Proc REPORT move group value (row header) closer to totals


I have some data that is structured as below. I need to create a table with subtotals, a total column that's TypeA + TypeB and a header that spans the columns as a table title. Also, it would be ideal to show different names in the column headings rather than the variable name from the dataset. I cobbled together some preliminary code to get the subtotals and total, but not the rest.

enter image description here

data tabletest;
    informat referral_total $50. referral_source $20.;
    infile datalines delimiter='|';
    input referral_total referral_source TypeA TypeB ;
    datalines;
    Long Org Name | SubA | 12 | 5
    Long Org Name | SubB | 14 | 3
    Longer Org Name | SubC | 0 | 1
    Longer Org Name | SubD | 4 | 12
    Very Long Org | SubE | 3 | 11
    Very Long Org | SubF | 9 | 19
    Very Long Org | SubG | 1 | 22
    ;
    run;

Code that I wrote:

proc report data=tabletest nofs headline headskip;
column referral_total referral_source TypeA TypeB;
define referral_total / group ;
define referral_source / group;
define TypeA / sum ' ';
define TypeB / sum ' ';
break after referral_total /  summarize style={background=lightblue font_weight=bold };
rbreak after /summarize;
compute referral_total;
    if _break_ = 'referral_total' then
    do;
    referral_total = catx(' ', referral_total, 'Total');
    end;
else if _break_ in ('_RBREAK_') then
    do;
    referral_total='Total';
    end;
endcomp;
run;

This is the desired output:

enter image description here


Solution

  • The DEFINE statement has an option NOPRINT that causes the column to not be rendered, however, the variables for it are still available (in a left to right manner) for use in a compute block.

    Stacking in the column statement allows you to customize the column headers and spans. In a compute block for non-group columns, the Proc REPORT data vector only allows access to the aggregate values at the detail or total line, so you need to specify .

    This sample code shows how the _total column is hidden and the _source cells in the sub- and report- total lines are 'injected' with the hidden _total value. The _source variable has to be lengthened to accommodate the longer values that are in the _total variable.

    data tabletest;
      * ensure referral_source big enough to accommodate _total || ' TOTAL';
    
      length referral_total $50 referral_source $60;
    
      informat referral_total $50. referral_source $20.;
      infile datalines delimiter='|';
      input referral_total referral_source TypeA TypeB ;
    datalines;
    Long Org Name | SubA | 12 | 5
    Long Org Name | SubB | 14 | 3
    Longer Org Name | SubC | 0 | 1
    Longer Org Name | SubD | 4 | 12
    Very Long Org | SubE | 3 | 11
    Very Long Org | SubF | 9 | 19
    Very Long Org | SubG | 1 | 22
    run;
    
    proc report data=tabletest;
      column 
      ( 'Table 1 - Stacking gives you custom headers and hierarchies'
        referral_total 
        referral_source 
        TypeA TypeB
        TypeTotal
      );
      define referral_total / group noprint;                 * hide this column;
      define referral_source / group;
      define TypeA / sum 'Freq(A)';                          * field labels are column headers;
      define TypeB / sum 'Freq(B)';
      define TypeTotal / computed 'Freq(ALL)';               * specify custom computation;
      break after referral_total /  summarize style={background=lightblue font_weight=bold };
      rbreak after /summarize;
    
      /*
       * no thanks, doing this in the _source compute block instead;
      compute referral_total;
          if _break_ = 'referral_total' then
          do;
          referral_total = catx(' ', referral_total, 'Total');
          end;
      else if _break_ in ('_RBREAK_') then
          do;
          referral_total='Total';
          end;
      endcomp;
      */
    
      compute referral_source;
        * the referral_total value is available because it is left of me. It just happens to be invisible;
        * at the break lines override the value that appears in the _source cell, effectively 'moving it over';
        select (_break_);
          when ('referral_total') referral_source = catx(' ', referral_total, 'Total');
          when ('_RBREAK_') referral_source = 'Total';
          otherwise;
        end;
      endcomp;
    
      compute TypeTotal;
        * .sum is needed because the left of me are groups and only aggregate values available here;
        TypeTotal = Sum(TypeA.sum,TypeB.sum); 
      endcomp;
    run;
    

    enter image description here