BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
SASuserlot
Barite | Level 11

Oh wow, So much intricacy. @Ksharp @StudentSASLearn @DanH_sas . Is there any option in the template if we want to apply the pattern and fill the box with colors simultaneously? I see a pattern in the Styles section here.

DanH_sas
SAS Super FREQ

Yes, just adding FILL to the DISPLAY option on the BOXPLOT statement will turn on fill colors:

display=( median mean caps fillpattern fill)
SASuserlot
Barite | Level 11

Thanks @DanH_sas, can we customize the fill colors ?

Ksharp
Super User

Check this:

  discreteattrmap name="comboMap" / ignorecase=true;
		    value "&_SUVAsia" / lineattrs=(color=orange ) markerattrs=(color=blue symbol=diamond) FILLATTRS=(color=red) ;
            value "&_SUVEurope" / lineattrs=(color=magenta)  markerattrs=(color=blue symbol=diamond) FILLATTRS=(color=yellow);
			value "&_SUVUSA" / lineattrs=(color=grey)   markerattrs=(color=blue symbol=diamond) FILLATTRS=(color=navy);
xiong
Fluorite | Level 6

Thank you , @Ksharp . I was required to create a similar graph. I tried to play with your code.

If any of the experts  @Ksharp @DanH_sas @Jay54 @GraphGuy @ChrisNZ @Rick_SAS    won't mind, can you please answer my questions at your convenience?

 

1. What does Style pattern really do here? How does it determine which box pattern goes to which box! Graphdata1 means first First Bar in Graph?

2. I have Couple of Question in the image as well.

xiong_0-1746229833775.png

 

proc sql;
    create table car_subjects as
    select distinct 
        cats(Make, "-", Model) as USUBJID length=50,
        Type as CarType length=15,
        Origin as Region length=15,
        Horsepower,
        Weight,
        MPG_City,
        MPG_Highway,
        MSRP
    from sashelp.cars
    where Make is not null and Model is not null;
quit;

*get Count to display in legend;

proc sql;
    create table car_counts as
    select CarType, Region, count(*) as N
    from car_subjects
    group by CarType, Region;
quit;

* Add count to dataset;

proc sort data=car_subjects;
    by CarType Region;
run;

proc sort data=car_counts;
    by CarType Region;
run;

/* Step 4: Merge and build final dummy dataset */
*try with few groups first;
data dummy_cars_final ( where = (region in ('USA' 'Asia' 'Europe') and cartype in ('SUV' 'Sedan' 'Truck')));
    merge car_subjects(in=a) car_counts;
    by CarType Region;
    if a;

    /* Derived variables */
    region_type = catx("-", CarType, Region);

    Score = round(40 + ranuni(0)*60, 0.1);

    PowerIndex = round((Horsepower * 0.6 + Weight * 0.0005), 0.1);

    EcoScore = round((MPG_City * 0.4 + MPG_Highway * 0.6), 0.1);

    length LuxuryLevel $10;
    if MSRP > 50000 then LuxuryLevel = "High";
    else if MSRP > 30000 then LuxuryLevel = "Medium";
    else LuxuryLevel = "Low";

    drop Horsepower Weight MPG_City MPG_Highway MSRP;
run;






proc sql;
create table dummy_cars_final2 as
select region_type,score,region,cats(region_type,'(N=',count(*),')') as region_type2 from dummy_cars_final
 group by region_type
union
/*create a dummy "Truck-Europe"*/
select "Truck-Europe",score,region,"Truck-Europe(N=0)" from dummy_cars_final
 where region_type='Sedan-Europe'
;
quit;

*For inner Margin**;

proc sql;
create table counts as
select region_type, count(  region_type) as n from dummy_cars_final
 group by region_type;
quit;

proc sort data =dummy_cars_final2;
by region_type;
run;

proc sort data =counts;
by region_type;
run;

data dummy_cars_final3;
	merge dummy_cars_final2 (in=a) counts(in=b );
by region_type;
	*if a and not b then n =0;
	label n = 'n';
run;

 
proc freq data=dummy_cars_final3 noprint;
table region_type2/out=levels;
run;

data _null_;
set levels;
call symputx('_'||compress(scan(region_type2,1,'()'),,'kad') ,region_type2 );
run;



proc template;
   define statgraph boxplot_template;
      begingraph/ATTRPRIORITY=NONE ;

         discreteattrmap name="comboMap" / ignorecase=true;
		    value "&_SUVAsia" / lineattrs=(color=black ) markerattrs=(color=blue symbol=diamond) FILLATTRS=(color=white) ;
            value "&_SUVEurope" / lineattrs=(color=black)  markerattrs=(color=blue symbol=diamond) FILLATTRS=(color=white);
			value "&_SUVUSA" / lineattrs=(color=black)   markerattrs=(color=blue symbol=diamond) FILLATTRS=(color=white);
            value "&_SedanAsia" / lineattrs=(color= black) markerattrs=(color=green symbol=circle) FILLATTRS=(color=white);
            value "&_SedanEurope" / lineattrs=(color=black) markerattrs=(color=green symbol=circle)FILLATTRS=(color=white);
            value "&_SedanUSA" / lineattrs=(color=black) markerattrs=(color=green symbol=circle) FILLATTRS=(color=white);
            value "&_TruckAsia" / lineattrs=(color= black) markerattrs=(color=red symbol=plus) FILLATTRS=(color=white);
			value "&_TruckEurope" / lineattrs=(color=white) markerattrs=(color=white symbol=plus) FILLATTRS=(color=white);
			value "&_TruckUSA" / lineattrs=(color=black) markerattrs=(color=red symbol=plus) FILLATTRS=(color=white);

         enddiscreteattrmap;




         discreteattrvar attrvar=patgroup var=region_type2 attrmap='comboMap';

         layout lattice / rows=2 columns=1 columndatarange=union ROWWEIGHTS=(.75 .25) ;
            layout overlay / 
               xaxisopts=(label="Region" labelattrs=(size=12pt weight=bold)
                          tickvalueattrs=(size=12pt weight=bold))
               yaxisopts=(offsetmin=0.05 offsetmax=0.05 label="Rating Score"  
                          linearopts=(tickvaluesequence=(start=0 end=100 increment=10)));

               boxplot x=region y=score /
                  name='BoxLegend'
                  group=patgroup 
                  groupdisplay=cluster 
                  boxwidth=0.6 clusterwidth=0.5 
                  display=( median mean caps fillpattern ) 
                  ;
        innermargin;
            axistable x= region value= n/ class= region_type2 /*yord*/ classdisplay=cluster  
					  clusterwidth=0.53 classorder=ascending 
                		valueattrs=(weight=bold) labelattrs=(weight=bold) colorgroup=trtgrp; 
        endinnermargin;

annotate / id="BAR";

            endlayout;

			discretelegend 'BoxLegend' / 
			      border=false 
			      valueattrs=(size=8pt weight = bold) 
			      across=3 location=inside valign=top  ;


            endlayout;

      endgraph;
   end;
run;

proc template;
   define style styles.mypatterns;
      parent=styles.listing;
      style GraphData1 from GraphData1 / fillpattern="R1" ;
      style GraphData2 from GraphData2 / fillpattern="R1" ;
      style GraphData3 from GraphData3 / fillpattern="R1" ;
      style GraphData4 from GraphData4 / fillpattern="X1" ;
      style GraphData5 from GraphData5 / fillpattern="X1" ;
      style GraphData6 from GraphData6 / fillpattern="X1" ;
      style GraphData7 from GraphData7 / fillpattern="L1" ;
      style GraphData8 from GraphData8 / fillpattern="L1" ;
      style GraphData9 from GraphData9/ fillpattern="L1" ;
   end;
run;



/*Mask the  dummy "Truck-Europe" box and legend*/
%sganno
data sganno;
 %SGRECTANGLE(ID="BAR", X1=52,Y1=10,HEIGHT=4,WIDTH=15,DRAWSPACE="GRAPHPERCENT" ,FILLCOLOR="white" ,DISPLAY="FILL",FILLTRANSPARENCY=0 )

run;


options orientation=landscape;
ods rtf file="c:\temp\boxplot_grouped.rtf" style=mypatterns;
ods graphics / reset width=8.5in height=5.5in border=off outputfmt=png;

proc sgrender data=Dummy_cars_final3 template=boxplot_template sganno=sganno;
run;

ods rtf close;
Ksharp
Super User

For your this special case, which is much more simple than OP  original question.

 

proc sql;
    create table car_subjects as
    select distinct 
        cats(Make, "-", Model) as USUBJID length=50,
        Type as CarType length=15,
        Origin as Region length=15,
        Horsepower,
        Weight,
        MPG_City,
        MPG_Highway,
        MSRP
    from sashelp.cars
    where Make is not null and Model is not null;
quit;

*get Count to display in legend;

proc sql;
    create table car_counts as
    select CarType, Region, count(*) as N
    from car_subjects
    group by CarType, Region;
quit;

* Add count to dataset;

proc sort data=car_subjects;
    by CarType Region;
run;

proc sort data=car_counts;
    by CarType Region;
run;

/* Step 4: Merge and build final dummy dataset */
*try with few groups first;
data dummy_cars_final ( where = (region in ('USA' 'Asia' 'Europe') and cartype in ('SUV' 'Sedan' 'Truck')));
    merge car_subjects(in=a) car_counts;
    by CarType Region;
    if a;

    /* Derived variables */
    region_type = catx("-", CarType, Region);

    Score = round(40 + ranuni(0)*60, 0.1);

    PowerIndex = round((Horsepower * 0.6 + Weight * 0.0005), 0.1);

    EcoScore = round((MPG_City * 0.4 + MPG_Highway * 0.6), 0.1);

    length LuxuryLevel $10;
    if MSRP > 50000 then LuxuryLevel = "High";
    else if MSRP > 30000 then LuxuryLevel = "Medium";
    else LuxuryLevel = "Low";

    drop Horsepower Weight MPG_City MPG_Highway MSRP;
run;













proc sort data=dummy_cars_final;
by Region CarType;
run;
data dummy_cars_final2;
set dummy_cars_final;
by  Region CarType;
if not first.CarType then call missing(n);
run;




proc template;
   define statgraph boxplot_template;
      begingraph/ATTRPRIORITY=NONE ;


         discreteattrmap name="comboMap" / ignorecase=true;
		    value "SUV" /  markerattrs=(color=blue symbol=diamond)  FILLATTRS=(color=white) ;
            value "Sedan" / markerattrs=(color=green symbol=circle) FILLATTRS=(color=white );
            value "Truck" /  markerattrs=(color=red symbol=plus) FILLATTRS=(color=white );
         enddiscreteattrmap;




         discreteattrvar attrvar=patgroup var=CarType attrmap='comboMap';

         layout lattice / rows=2 columns=1 columndatarange=union ROWWEIGHTS=(.75 .25) ;
            layout overlay / 
               xaxisopts=(label="Region" labelattrs=(size=12pt weight=bold)
                          tickvalueattrs=(size=12pt weight=bold))
               yaxisopts=(offsetmin=0.05 offsetmax=0.05 label="Rating Score"  
                          linearopts=(tickvaluesequence=(start=0 end=100 increment=10)));

               boxplot x=region y=score /
                  name='BoxLegend'
                  group=CarType
                  groupdisplay=cluster 
                  boxwidth=0.6 clusterwidth=0.5 
                  display=( median mean caps fillpattern ) 
                  MEDIANATTRS=(pattern=solid)
                  WHISKERATTRS=(pattern=solid)

                  ;
        innermargin;
            axistable x= region value= n/ class= CarType classdisplay=cluster  
					  clusterwidth=0.53 classorder=ascending 
                		valueattrs=(weight=bold) labelattrs=(weight=bold) ; 
        endinnermargin;

annotate / id="BAR";

            endlayout;

			discretelegend 'BoxLegend' / 
			      border=false 
			      valueattrs=(size=8pt weight = bold) 
			      across=3 location=inside valign=top  ;


            endlayout;

      endgraph;
   end;
run;



proc template;
   define style styles.mypatterns;
      parent=styles.listing;
      style GraphData1 from GraphData1 / fillpattern="L1" ;
      style GraphData2 from GraphData2 / fillpattern="X1" ;
      style GraphData3 from GraphData3 / fillpattern="R1" ;
   end;
run;

options orientation=landscape;
ods rtf file="c:\temp\boxplot_grouped.rtf" style=mypatterns;
ods graphics / reset width=8.5in height=5.5in border=off outputfmt=png;

proc sgrender data=dummy_cars_final2 template=boxplot_template ;
run;

ods rtf close;

Ksharp_0-1746239944963.png

 

xiong
Fluorite | Level 6

Thank you very much @Ksharp.  Since we have different patterns, can we make all of them black? I tried the lineattr to Black but it did not change the Color.

if you know answer to my first question , can you please let me know that regarding style patterns and 'Graphdatax' is it changes as we change the Sort order?

Ksharp
Super User

Here you go:

proc template;
   define statgraph boxplot_template;
      begingraph/ATTRPRIORITY=NONE   DATACONTRASTCOLORS=(black);

Ksharp_0-1746322401037.png

 

 

 

 

 

 

For your first question, yes the pattern would change if you change the order of bar.

Graphdata1 is for the first appeared level.

Graphdata2 is for the second appeared level.

Graphdata3 is for the third appeared level.

............

E.X. if your data of CAR was 

Truck    <--Graphdata1 is assigned to it ,due to it is appeared firstly in data.

Sedan   <--Graphdata2 is assigned to it ,due to it is appeared secondly in data.

SUV    <--Graphdata3 is assigned to it ,due to it is appeared thirdly in data.

 

 

xiong
Fluorite | Level 6

Thank you very much for your patience and detailed answer. I really really aprreciate your time. One thing I observed with the new code that markerattrs symbols no longer matching with the graph, So the code overwriting the Dsicreteattrmap?

StudentSASLearn
Obsidian | Level 7
I think you can use DATASYMBOLS option at begin grraph statement like @Ksharp colors options.
Ksharp
Super User

OK. Try this one :

 

proc sql;
    create table car_subjects as
    select distinct 
        cats(Make, "-", Model) as USUBJID length=50,
        Type as CarType length=15,
        Origin as Region length=15,
        Horsepower,
        Weight,
        MPG_City,
        MPG_Highway,
        MSRP
    from sashelp.cars
    where Make is not null and Model is not null;
quit;

*get Count to display in legend;

proc sql;
    create table car_counts as
    select CarType, Region, count(*) as N
    from car_subjects
    group by CarType, Region;
quit;

* Add count to dataset;

proc sort data=car_subjects;
    by CarType Region;
run;

proc sort data=car_counts;
    by CarType Region;
run;

/* Step 4: Merge and build final dummy dataset */
*try with few groups first;
data dummy_cars_final ( where = (region in ('USA' 'Asia' 'Europe') and cartype in ('SUV' 'Sedan' 'Truck')));
    merge car_subjects(in=a) car_counts;
    by CarType Region;
    if a;

    /* Derived variables */
    region_type = catx("-", CarType, Region);

    Score = round(40 + ranuni(0)*60, 0.1);

    PowerIndex = round((Horsepower * 0.6 + Weight * 0.0005), 0.1);

    EcoScore = round((MPG_City * 0.4 + MPG_Highway * 0.6), 0.1);

    length LuxuryLevel $10;
    if MSRP > 50000 then LuxuryLevel = "High";
    else if MSRP > 30000 then LuxuryLevel = "Medium";
    else LuxuryLevel = "Low";

    drop Horsepower Weight MPG_City MPG_Highway MSRP;
run;













proc sort data=dummy_cars_final;
by Region CarType;
run;
data dummy_cars_final2;
set dummy_cars_final;
by  Region CarType;
if not first.CarType then call missing(n);
run;




proc template;
   define statgraph boxplot_template;
      begingraph/ATTRPRIORITY=NONE DATACONTRASTCOLORS=(black);


         discreteattrmap name="comboMap" / ignorecase=true;
		    value "SUV" /  markerattrs=(color=blue symbol=diamond)  FILLATTRS=(color=white) ;
            value "Sedan" / markerattrs=(color=green symbol=circle) FILLATTRS=(color=white );
            value "Truck" /  markerattrs=(color=red symbol=plus) FILLATTRS=(color=white );
         enddiscreteattrmap;




         discreteattrvar attrvar=CarType var=CarType attrmap='comboMap';

         layout lattice / rows=2 columns=1 columndatarange=union ROWWEIGHTS=(.75 .25) ;
            layout overlay / 
               xaxisopts=(label="Region" labelattrs=(size=12pt weight=bold)
                          tickvalueattrs=(size=12pt weight=bold))
               yaxisopts=(offsetmin=0.05 offsetmax=0.05 label="Rating Score"  
                          linearopts=(tickvaluesequence=(start=0 end=100 increment=10)));

               boxplot x=region y=score /
                  name='BoxLegend'
                  group=CarType
                  groupdisplay=cluster 
                  boxwidth=0.6 clusterwidth=0.5 
                  display=( median mean caps fillpattern ) 
                  MEDIANATTRS=(pattern=solid)
                  WHISKERATTRS=(pattern=solid)

                  ;
        innermargin;
            axistable x= region value= n/ class= CarType classdisplay=cluster  
					  clusterwidth=0.53 classorder=ascending 
                		valueattrs=(weight=bold) labelattrs=(weight=bold) ; 
        endinnermargin;

annotate / id="BAR";

            endlayout;

			discretelegend 'BoxLegend' / 
			      border=false 
			      valueattrs=(size=8pt weight = bold) 
			      across=3 location=inside valign=top  ;


            endlayout;

      endgraph;
   end;
run;



proc template;
   define style styles.mypatterns;
      parent=styles.listing;
      style GraphData1 from GraphData1 / fillpattern="L1" ;
      style GraphData2 from GraphData2 / fillpattern="X1" ;
      style GraphData3 from GraphData3 / fillpattern="R1" ;
   end;
run;

options orientation=landscape;
ods rtf file="c:\temp\boxplot_grouped.rtf" style=mypatterns;
ods graphics / reset width=8.5in height=5.5in border=off outputfmt=png;

proc sgrender data=dummy_cars_final2 template=boxplot_template ;
run;

ods rtf close;

Ksharp_0-1746356383113.png

 

DanH_sas
SAS Super FREQ

Hopefully, the answers below will address your questions...

 

1. What does Style pattern really do here? How does it determine which box pattern goes to which box! Graphdata1 means first First Bar in Graph?

Answer: When a discrete attributes map is not used, the first-occurring group value is assigned to the first part, the second value to the second pattern, etc.

 

2. To make the whiskers to be solid, set WHISKERATTRS=(PATTERN=1) on the BOXPLOT statement.

 

3. To make one legend with three entries, you will need to strip the "-<region>(n-<N>" from you group values. Then, the legend will naturally return to type-only entries

 

4. The AXISTABLE code looks correct. Are your "n" values correct?

 

hackathon24-white-horiz.png

The 2025 SAS Hackathon Kicks Off on June 11!

Watch the live Hackathon Kickoff to get all the essential information about the SAS Hackathon—including how to join, how to participate, and expert tips for success.

YouTube LinkedIn

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.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 39 replies
  • 2625 views
  • 13 likes
  • 6 in conversation
OSZAR »