I am trying to produce a graph with multiple groupings. The sample data code is:
proc sort data=sashelp.cars out=cars;
by DriveTrain;
where n(Cylinders);
run;
I used dattrmap to add distinct colors to the different groups as follow:
data MyAttrMap;
length MARKERCOLOR CONTRASTCOLOR color $25;
ID='myreg'; value='All' ; MARKERCOLOR='red'; color='red'; MARKERSYMBOL = 'circle'; output;
ID='myreg'; value='Front'; MARKERCOLOR='blue'; color='blue'; MARKERSYMBOL = 'circle'; output;
ID='myreg1'; value='USA'; CONTRASTCOLOR='yellow'; color='yellow'; output;
ID='myreg1'; value='Europe'; CONTRASTCOLOR='black'; color='black'; output;
ID='myreg1'; value='Asia'; CONTRASTCOLOR='green'; color='green'; > > output;
run;
proc sgplot data=work.cars
dattrmap=MyAttrMap;
hbarparm category=enginesize response=horsepower/group=DriveTrain barwidth=.5 attrid=myreg name='dt';
scatter X=MPG_City Y=enginesize /group=origin name='origin' attrid=myreg1;
keylegend 'dt' / title='Drive Train' location=outside position=bottom;
keylegend 'origin' / title='Origin' location=outside position=bottom;
where DriveTrain in ('All' 'Front');
run;
The Attrmap was created with the intention of having different colors for Origin and DriveTrain however, when the output is created the same colors applied to Origin are applied to DriveTrain.
I also tried using Proc template to change the style as follow:
/*Different colors from the ones used above*/
proc template;
define style MyStyle;
parent = Styles.Listing;
STYLE graphdata1 /
MARKERSYMBOL = 'circle'
LINESTYLE = 1
CONTRASTCOLOR = liypk
COLOR = red
;
STYLE graphdata2 /
MARKERSYMBOL = 'circle'
LINESTYLE = 1
CONTRASTCOLOR = stybr
COLOR = yellow
;
STYLE graphdata3 /
MARKERSYMBOL = 'circle'
LINESTYLE = 1
CONTRASTCOLOR = mog
COLOR = green
;
STYLE graphdata4 /
MARKERSYMBOL = 'circle'
LINESTYLE = 1
CONTRASTCOLOR = brown
COLOR = pink
;
STYLE graphdata5 /
MARKERSYMBOL = 'circle'
LINESTYLE = 1
CONTRASTCOLOR = black
COLOR = grey
;
end;
run;
But still the same results were obtained. Could anyone please tell me what I'm doing wrong or how to get this to work? I'm using SAS 9.3.
Another issue I'm encountering is the sorting. I want to sort the bars so that the same origins appear together and by the horsepower. I sorted using sortkey=national and used grouporder=data as recommended by SAS but this didn't change the ordering in the output. Any help is appreciated.
.
Thanks.
You might find SGPANEL a better option for visually presenting the distributions of different groups.
ods html style=normal;
ods graphics / height=900px;
proc sgpanel data=sashelp.cars;
panelby origin
/ columns=3
;
hbar enginesize
/ group=drivetrain
groupdisplay=cluster
;
where
DriveTrain in ('Front', 'All')
and not missing(cylinders)
;
run;
Check your attribute map data set. Because you haven't specified the lengths for the Value and ID column they're truncated and don't match your data so they don't get assigned correctly.
Simplifying your problem, I assigned all the elements for testing:
I also assumed this was mocked up because of the errors in the log.
proc sort data=sashelp.cars out=cars;
by DriveTrain;
where n(Cylinders);
run;
data MyAttrMap;
length ID $10. linecolor MARKERCOLOR CONTRASTCOLOR fillcolor color value $25;
ID='myreg1';
value='USA';
contrastcolor='cxaf8dc3';
LINECOLOR='cxaf8dc3';
MARKERCOLOR='cxaf8dc3';
fillcolor='cxaf8dc3';
output;
ID='myreg1';
value='Europe';
contrastcolor='cx7fbf7b';
LINECOLOR='cx7fbf7b';
MARKERCOLOR='cx7fbf7b';
fillcolor='cx7fbf7b';
output;
ID='myreg1';
value='Asia';
contrastcolor='cx91bfdb';
LINECOLOR='cxfc8d59';
MARKERCOLOR='cxfc8d59';
fillcolor='cxfc8d59';
output;
run;
ods graphics / attrpriority=none;
proc sgplot data=work.cars dattrmap=MyAttrMap;
scatter X=MPG_City Y=enginesize /group=origin name='origin' attrid=myreg1;
where DriveTrain in ('All' 'Front');
run;
Related
I'm trying to do a bar plot with categories using gchart in SAS. I have in the horizontal axis dates, so I don't want that appear because looks chaotic. I'm using the following code
axis1 label=none value=none;
axis2 label=(angle=90 'Porcentaje');
legend1 label=('Categoría') frame;
proc gchart data=base_fechas;
vbar REPORTE_FCH/ discrete subgroup=TPO_SEX
group=REPORTE_FCH g100 nozero
type=percent
inside=percent width=20
gaxis=axis1 raxis=axis2
legend=legend1;
run;
quit;
but the values of the axis still apear. How can I delete de values of the horizontal axis?
You have vbar <date> / … group=<date> g100 … ;
The group=<date> axis rendering is controlled by the gaxis option. The vbar <date> are mid-points and their rendering are controlled by the maxis option.
You can hide the midpoints by specifying vbar option maxis=axis1
This example hides both the midpoints and the groups:
data have;
do date = today()-100 to today();
do index = 1 to 50 * ranuni(123);
sex = substr('MF',1+(rannor(123) > 0.75),1);
output;
end;
end;
format date yymmdd10.;
run;
axis1 label=none value=none;
axis2 label=(angle=90 '%');
proc gchart data=have;
vbar date
/ discrete
subgroup = sex
GROUP = date
G100
nozero
type=percent
inside=percent width=20
gaxis = axis1
raxis = axis2
maxis = axis1
;
where date > today()-20;
run;
I am trying to make a boxplot by using the SGPLOT in SAS. I would like to use SGPLOT with VBOX statement to flag out the Mean and Median on the gragh for each box.
Below is the data set I created as an example. Can someone give me a kind help on that?
/* Set the graphics environment */
goptions reset=all cback=white border htitle=12pt htext=10pt;
/* Create a sample data set to plot */
data one(drop=i);
do i=1 to 10;
do xvar=1 to 9 by 2;
yvar=ranuni(0)*100;
output;
end;
end;
run;
/* Sort the data by XVAR */
proc sort data=one;
by xvar;
run;
/* Use the UNIVARIATE procedure to determine */
/* the mean and median values */
proc univariate data=one noprint;
var yvar;
by xvar;
output mean=mean median=median out=stat;
run;
/* Merge the mean and median values back */
/* into the original data set by XVAR */
data all;
merge one stat;
by xvar;
run;
Use VBOX for box plot, SCATTER for mean/median.
/*--Compute the Mean and Median by sex--*/
proc means data=sashelp.heart;
class deathcause;
var cholesterol;
output out=heart(where=(_type_ > 0) keep=deathcause mean median _type_)
mean = mean
median = median;
run;
/*--Merge the data--*/
data heart2;
keep deathcause mean median cholesterol;
set sashelp.heart heart;
run;
proc print data=heart2;run;
/*--Box plot with connect and group colors--*/
ods graphics / reset ANTIALIASMAX=5300 width=5in height=3in imagename='Box_Group_Multi_Connect';
title 'Cholesterol by Cause of Death';
proc sgplot data=heart2 noautolegend noborder;
vbox cholesterol / category=deathcause group=deathcause;
scatter x=deathcause y=mean / name='mean' legendlabel='Mean' markerattrs=(color=green);
scatter x=deathcause y=median / name='median' legendlabel='Median' markerattrs=(color=red);
keylegend "mean" "median" / linelength=32 location=inside across=1 position=topright;
xaxis display=(nolabel);
run;
EDIT: Within SGPLOT and the VBOX statement, you can also plot the median as the line, and the mean as a point on the box plot, without any other manual calculations ahead of time. This is available as of SAS 9.4 M5+.
ods graphics / reset ANTIALIASMAX=5300 width=5in height=3in imagename='Box_Group';
title 'Cholesterol by Cause of Death';
proc sgplot data=sashelp.heart noborder;
vbox cholesterol / category=deathcause
displaystats=(median mean)
meanattrs=(color=red)
medianattrs=(color=green);
*xaxis display=(nolabel);
run;
I created the following graph using sgplot
proc sgplot data=Colordistricts;
hbar distrct/response=Percent
group= population;
run;
However, it seems that the individual population groups are arranged in alphabetical order in the graph (Asian followed by Black Color and White).
How do I create this same plot with the population groups in the descending order by percent?
In fact these are districts where the color population is highest. Basically I want to create a graph so that each bar begins with the color population
To force a specific group value to the first position you can map the desired group to a new value that will collate first. Sometimes this is easily done by placing a space character in front of an existing value.
If the group variable is a numeric ID custom formatted to display an associated group label you can create a new version of the custom format to include a 0 id that corresponds to the forced group. The forced group is mapped to the 0 id.
You would then sort the data in the particular way you need and use SGPLOT yaxis type=discrete discreteOrder=data; to force the hbar categories to appear in the particular order.
Here is some sample code to explore. The final SGPLOT uses the mapping technique to force a particular population segment to appear first.
ods html close;
%let path = %sysfunc(pathname(work));
ods html file="&path.\sgplot_hbar.html" gpath="&path.";
proc format;
value popId
0 = 'Color'
1 = 'Asian'
2 = 'Black'
3 = 'Color'
4 = 'White'
;
data have;
do _n_ = rank('A') to rank('P');
district = byte (_n_);
x = 0;
populationID = 2; percent = ceil(40*ranuni(123)); output;
x + percent;
populationID = 3; percent = ceil(40*ranuni(123)); output;
x + percent;
if (ranuni(123) < 0.10) then do;
populationID = 1; percent = ceil(10*ranuni(123)); output;
x + percent;
end;
percent = 100 - x;
populationID = 4;
output;
end;
keep district populationID percent;
label
percent = 'Percent of Total Frequency'
;
format
populationID popId.
;
run;
proc sgplot data=have;
hbar district
/ group = populationID
response = percent
;
title j=L 'default group order by populationID value';
title2 j=L 'districts (yaxis) also implicitly sorted by formatted value';
run;
proc sgplot data=have;
hbar district
/ group = populationID
response = percent
categoryOrder = respAsc
;
title j=L 'categoryOrder: ascending response';
title2 j=L 'districts (yaxis) also implicitly sorted by min(response)';
run;
proc sgplot data=have;
hbar district
/ group = populationID
response = percent
categoryOrder = respDesc
;
title j=L 'categoryOrder: descending response';
title2 j=L 'districts (yaxis) also implicitly sorted by descending max(response)';
run;
proc sql;
create table have2 as
select
case
when populationID = 3 then 0 else populationID
end as hbar_populationID format=popId.
, *
from have
order by
hbar_populationID, percent
;
quit;
proc sgplot data=have2;
yaxis type=discrete discreteOrder=data;
hbar district
/ group = hbar_populationID
response = percent
;
title j=L 'population seqment ordering is partially forced by tweaking populationID values';
title2 j=L 'districts in data order per yaxis statement';
run;
Forced groupOrder
SQL can sort the data in a particular order by using a case in the order by clause. You would then use groupOrder=data in SGPLOT.
proc sql;
create table have3 as
select *
from have
order by
district
, case
when populationID = 3 then 0
when populationID = 2 then 1
when populationID = 4 then 2
when populationID = 1 then 3
else 99
end
;
quit;
proc sgplot data=have3;
hbar district
/ group = populationID
groupOrder = data
response = percent
;
title j=L 'population seqment ordering is partially forced by tweaking populationID values';
title2 j=L 'districts in data order per yaxis statement';
run;
Forcing one segment to be first and then the other segments relying on response values
After mapping populationID 2 to 0 you could force the remaining population segments to be ordered similar to respAsc or respDesc. That process would require additional coding to determine new mappings for the other populationID values. This additional example shows how the global response sum is used to force a descending order on the remaining population segments within a district.
proc sql;
create table way as
select populationID, sum(percent) as allPct
from have
where populationID ne 3
group by populationID
order by allPct descending
;
data waySeq;
set way;
seq + 1;
run;
proc sql;
create table have3 as
select
have.*
, case
when have.populationID = 3 then 1000 else 1000+seq
end as hbar_populationID
from have
left join waySeq on have.populationID = waySeq.populationID
order by
hbar_populationID, percent
;
create table fmtdata as
select distinct
hbar_populationID as start
, put(populationID, popId.) as label
, 'mappedPopId' as fmtname
from have3;
quit;
proc format cntlin = fmtdata;
run;
%let syslast = have3;
proc sgplot data=have3;
yaxis type=discrete discreteOrder=data;
hbar district
/ group = hbar_populationID
response = percent
groupOrder = data
;
format hbar_populationID mappedPopId.;
title j=L 'population seqment ordering is partially forced by tweaking populationID values';
title2 j=L 'districts in data order per yaxis statement';
run;
title;
I want to create stlak bar charts, The column WORKSCOPE having HSB,OHS,RES. but I want to reorder the WORKSCOPE in the order of OHS,HSB,RES in the stalk bar. Usually default its taking in alphabetical order. how can I achieve it.
goptions reset=all;
goptions colors=(red blue green);
legend1 label=none
order=('OHS' 'HSB' 'RES');
proc gchart data=FINALREPV3;
vbar year / discrete type=sum sumvar=VALUE
subgroup=WORKSCOPE legend=legend1 ;
run;
You can create a sorting variable beforehand, use it to sort the input data and then plot using proc sgplot with xaxis discreteorder = data.
/* Create dummy sorting variable */
data want;
set finalrepv3;
if workscope = "OHS" then _sort = 1;
if workscope = "HBS" then _sort = 2;
if workscope = "RES" then _sort = 3;
run;
/* Put the data in the required order */
proc sort data = want;
by _sort;
run;
/* Create the plot */
proc sgplot data = want;
vbar year / group = workscope response = value stat = sum;
/* Request that the x axis respect the data's order */
xaxis discreteorder = data;
run;
I'm creating a VLine plot with Proc SgPlot, with 4 groups of data. Here is my code to create the plot. (I've genericized the variable names, of course.) This is wrapped in a macro, but I don't think that makes any difference.
proc sgplot data=mydata;
vline NominalTime / Response=responseVariable Group=MyGroups stat=mean markers;
format MyGroups MyGroups.;
Where scaleNum=&scaleNum;
run;
I'm using Proc Template to customize the line colors, LineStyles, and MarkerSymbols. The Template code is below:
proc template;
define style Styles.MyNewStyle;
parent = styles.HTMLBlue;
style GraphBackground /
backgroundcolor=white;
style GraphData1 from GraphData1 /
markersymbol = "circle"
color = CXFFB44B
contrastcolor = CXFFB44B
;
style GraphData2 from GraphData2 /
markersymbol = "circle"
color = white
contrastcolor=CX000000
;
style GraphData3 from GraphData3 /
markersymbol = "Square"
color = CXD33183
contrastcolor=CXD33183
;
style GraphData4 from GraphData4 /
markersymbol = 'Square'
linestyle = 2
color = white
contrastcolor=CX555555
;
style GraphFonts from GraphFonts /
'GraphDataFont' = ("Arial",11pt)
'GraphValueFont' = ("Arial",11pt)
'GraphLabelFont' = ("Arial",11pt,bold)
'GraphFootnoteFont' = ("Arial",8pt)
'GraphTitleFont' = ("Arial",12pt,bold);
end;
run;
Of course, I've used ods html to change the output style:
ods html style=MyNewStyle;
Here is the crux of the question: the plot is generated with the colors as specified, but with incorrect markersymbol and linestyle assignments for groups 2 to 4. Stated another way, whatever markersymbol and linestyle I specify in GraphData1 is used for all of the lines in the plot, regardless of later assignments. Why is this happening?
The key to understanding this behavior is that starting with SAS 9.3, the HTML destination uses the HTMLBlue style by default. This style is a "Color priority" style, where only the color is rotated first. There are 12 defined colors. So, the first 12 group values get one of these 12 colors, with the first marker symbol and line pattern. After the first 12 groups, the next 12 groups get the second marker symbol and line pattern, with the 12 colors again.
With HTMLBlue, the other markers and line patterns will only show up after the first 12 group values.
Most other styles have attribute priority of "None". This means the all three attributes are rotated at the same time. So first group value gets color 1, symbol 1, and pattern 1. Second group value gets color 2, symbol 2, and pattern 2 and so on.
With SAS 9.4, the option ATTRPRIORITY was introduced in the Style definition and on the ODS GRAPHICS statement to control this behavior. Now, you can use this option to make any style rotate the attributes the way you want. To make HTMLBlue behave like LISTING, use the ODS Graphics option ATTRPRIORITY=none.
This works as expected:
proc template;
define style Styles.MyNewStyle;
parent = styles.HTMLBlue;
style GraphBackground /
backgroundcolor=white;
style GraphData1 from GraphData1 /
markersymbol = "circle"
color = CXFFB44B
contrastcolor = CXFFB44B
;
style GraphData2 from GraphData2 /
markersymbol = "circle"
color = white
contrastcolor=CX000000
;
style GraphData3 from GraphData3 /
markersymbol = "Square"
color = CXD33183
contrastcolor=CXD33183
;
style GraphData4 from GraphData4 /
markersymbol = 'Square'
linestyle = 2
color = white
contrastcolor=CX555555
;
style GraphFonts from GraphFonts /
'GraphDataFont' = ("Arial",11pt)
'GraphValueFont' = ("Arial",11pt)
'GraphLabelFont' = ("Arial",11pt,bold)
'GraphFootnoteFont' = ("Arial",8pt)
'GraphTitleFont' = ("Arial",12pt,bold);
end;
run;
data mydata;
do mygroups = 1 to 4;
do nominaltime=1 to 20;
responsevariable = mygroups*nominaltime;
output;
end;
end;
run;
ods html style=mynewstyle;
proc sgplot data=mydata;
vline NominalTime / Response=responseVariable Group=MyGroups stat=mean markers;
* format MyGroups MyGroups.;
* Where scaleNum=&scaleNum;
run;
ods html close;
If that works on your system, then you have a problem with your MyGroups. format or values, I suspect. What values does MyGroups have natively - 1-4 or something else that MyGroups. format converts to 1-4?
If that doesn't work on your system, give more system details - 9.3, 9.2, etc.?