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.
Yes, just adding FILL to the DISPLAY option on the BOXPLOT statement will turn on fill colors:
display=( median mean caps fillpattern fill)
Thanks @DanH_sas, can we customize the fill colors ?
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);
Thank you Very much.
Thank you.
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.
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;
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;
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?
Here you go:
proc template; define statgraph boxplot_template; begingraph/ATTRPRIORITY=NONE DATACONTRASTCOLORS=(black);
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.
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?
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;
Thank you
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?
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.
Ready to level-up your skills? Choose your own adventure.