Hi! Hopefully my formating works this time! I'm creating a mirror butterfly plot and would like to change the color of the values in the bar segments to white for the segments with a black or maroon background. The remaining segments (VLIGB, red, and blue), I'd like to keep the text in black. I'd also like to change the fonts for the entire plot to Arial. The figure itself is working as intended just not these two aspects. Thank you in advance for your help!
Sample Data:
data WORK.DIAGNOSES; infile datalines dsd truncover; input diagnosis:$17. _2:32. _1:32. group_cat:32. group:$7.; label diagnosis="diagnosis" _2="_2" _1="_1" group_cat="group_cat" group="group"; datalines; ALL 52 -52 1 Group 1 ALL 2 -2 2 Group 2 AML 10 -20 1 Group 1 AML 6 -10 2 Group 2 Breast Cancer 20 -13 2 Group 2 Breast Cancer 20 -9 1 Group 1 Breast Cancer 8 -3 3 Group 3 Colon Cancer 20 -30 1 Group 1 Lung Cancer 24 -17 1 Group 1 Stomach Cancer 7 -7 4 Group 4 Stomach Cancer 4 -4 3 Group 3 Skin Cancer 1 -1 4 Group 4 Skin Cancer 1 0 3 Group 3 Pancreatic Cancer 2 -2 3 Group 3 Prostate Cancer 2 0 5 Group 5
Sample Code:
proc format;
picture positive
low -< 0 = "0000"
0 <- high = "0000";
run;
proc sgplot data = work.diagnoses noborder nowall;
styleattrs datacontrastcolors=(black) datacolors=(VLIGB red blue black maroon);
format _1 positive.;
format _2 positive.;
hbar diagnosis / response = _1 group = group groupdisplay = stack barwidth=0.9 grouporder=ascending
attrid = myid seglabel
seglabelfitpolicy = thin
seglabelattrs =(size = 9 color = black weight = bold)
name = "c1";
hbar diagnosis / response = _2 group = group groupdisplay = stack barwidth=0.9 grouporder=ascending
seglabel
seglabelfitpolicy = thin
seglabelattrs = (size = 9 color = black weight = bold);
xaxis /*grid gridattrs = (pattern = solid) valueattrs = (size=10 weight = bold) */
labelattrs = (weight = bold) display=(noticks)
label = "Patients (%)" values = (-80 to 80 by 20);
yaxis
display = (noline nolabel noticks) discreteorder = data
valueattrs = (weight=bold color=black family="Arial" size=10)label="Patients";
keylegend "c1"/ across = 10 noborder title = '' valueattrs=(size=11) sortorder=ascending;
inset "Insert" / position = bottomleft
textattrs = (color = black size = 10 family="Arial"
style = normal);
inset "Insert" / position = bottomright
textattrs = (color = black size = 10 weight = bold family="Arial"
style = normal);
run;
/*
That is really uneasy.
I think you could use TEXT to replace SEGLABEL.
Maybe PROC TEMPLATE coould make solution easier.
*/
data WORK.DIAGNOSES;
input diagnosis & $17. _2 _1 group_cat group $20.;
label diagnosis="diagnosis" _2="_2" _1="_1" group_cat="group_cat" group="group";
datalines;
ALL 52 -52 1 Group 1
ALL 2 -2 2 Group 2
AML 10 -20 1 Group 1
AML 6 -10 2 Group 2
Breast Cancer 20 -13 2 Group 2
Breast Cancer 20 -9 1 Group 1
Breast Cancer 8 -3 3 Group 3
Colon Cancer 20 -30 1 Group 1
Lung Cancer 24 -17 1 Group 1
Stomach Cancer 7 -7 4 Group 4
Stomach Cancer 4 -4 3 Group 3
Skin Cancer 1 -1 4 Group 4
Skin Cancer 1 0 3 Group 3
Pancreatic Cancer 2 -2 3 Group 3
Prostate Cancer 2 0 5 Group 5
;
proc sort data=DIAGNOSES out=have;
by diagnosis group;
run;
data have2;
set have;
by diagnosis;
if first.diagnosis then call missing(cum1,cum2);
mean1=-sum(cum1,-_1/2);
mean2=sum(cum2,_2/2);
cum1+(-_1);
cum2+_2;
drop cum1 cum2;
run;
proc format;
picture positive
low -< 0 = "0000"
0 <- high = "0000";
run;
proc template;
define style styles.garamond;
parent=styles.listing; /* Or your favorite style */
style graphfonts from graphfonts /
'GraphDataFont' = ("Arial, <MTsans-serif>",7pt)
'GraphUnicodeFont' = ("<MTsans-serif-unicode>",9pt)
'GraphValueFont' = ("Arial, <MTsans-serif>",9pt)
'GraphLabel2Font' = ("Arial, <MTsans-serif>",10pt)
'GraphLabelFont' = ("Arial, <MTsans-serif>",10pt)
'GraphFootnoteFont' = ("Arial, <MTsans-serif>",10pt)
'GraphTitleFont' = ("Arial, <MTsans-serif>",11pt,bold)
'GraphTitle1Font' = ("Arial, <MTsans-serif>",14pt)
'GraphAnnoFont' = ("Arial, <MTsans-serif>",10pt);
end;
run;
ods listing style=garamond;
ods html style=garamond;
proc sgplot data = have2 noborder nowall ;
styleattrs datacontrastcolors=(black black white white white) datacolors=(VLIGB red blue black maroon);
format _1 positive.;
format _2 positive.;
hbarparm category=diagnosis response = _1/ group = group groupdisplay = stack barwidth=0.9 grouporder=ascending nooutline name="c1";
hbarparm category=diagnosis response = _2/ group = group groupdisplay = stack barwidth=0.9 grouporder=ascending nooutline ;
text x=mean1 y=diagnosis text=_1 /contributeoffsets=none strip group = group textattrs=(size = 9 weight = bold);
text x=mean2 y=diagnosis text=_2 /contributeoffsets=none strip group = group textattrs=(size = 9 weight = bold);
xaxis labelattrs = (weight = bold) display=(noticks) label = "Patients (%)" values = (-80 to 80 by 20);
yaxis display = (noline nolabel noticks) discreteorder = data valueattrs = (weight=bold color=black size=10) label="Patients";
keylegend "c1"/ across = 10 noborder title = '' valueattrs=(size=11) sortorder=ascending;
inset "Insert" / position = bottomleft textattrs = (color = black size = 10 weight = bold style = normal);
inset "Insert" / position = bottomright textattrs = (color = black size = 10 weight = bold style = normal);
run;
/*
That is really uneasy.
I think you could use TEXT to replace SEGLABEL.
Maybe PROC TEMPLATE coould make solution easier.
*/
data WORK.DIAGNOSES;
input diagnosis & $17. _2 _1 group_cat group $20.;
label diagnosis="diagnosis" _2="_2" _1="_1" group_cat="group_cat" group="group";
datalines;
ALL 52 -52 1 Group 1
ALL 2 -2 2 Group 2
AML 10 -20 1 Group 1
AML 6 -10 2 Group 2
Breast Cancer 20 -13 2 Group 2
Breast Cancer 20 -9 1 Group 1
Breast Cancer 8 -3 3 Group 3
Colon Cancer 20 -30 1 Group 1
Lung Cancer 24 -17 1 Group 1
Stomach Cancer 7 -7 4 Group 4
Stomach Cancer 4 -4 3 Group 3
Skin Cancer 1 -1 4 Group 4
Skin Cancer 1 0 3 Group 3
Pancreatic Cancer 2 -2 3 Group 3
Prostate Cancer 2 0 5 Group 5
;
proc sort data=DIAGNOSES out=have;
by diagnosis group;
run;
data have2;
set have;
by diagnosis;
if first.diagnosis then call missing(cum1,cum2);
mean1=-sum(cum1,-_1/2);
mean2=sum(cum2,_2/2);
cum1+(-_1);
cum2+_2;
drop cum1 cum2;
run;
proc format;
picture positive
low -< 0 = "0000"
0 <- high = "0000";
run;
proc template;
define style styles.garamond;
parent=styles.listing; /* Or your favorite style */
style graphfonts from graphfonts /
'GraphDataFont' = ("Arial, <MTsans-serif>",7pt)
'GraphUnicodeFont' = ("<MTsans-serif-unicode>",9pt)
'GraphValueFont' = ("Arial, <MTsans-serif>",9pt)
'GraphLabel2Font' = ("Arial, <MTsans-serif>",10pt)
'GraphLabelFont' = ("Arial, <MTsans-serif>",10pt)
'GraphFootnoteFont' = ("Arial, <MTsans-serif>",10pt)
'GraphTitleFont' = ("Arial, <MTsans-serif>",11pt,bold)
'GraphTitle1Font' = ("Arial, <MTsans-serif>",14pt)
'GraphAnnoFont' = ("Arial, <MTsans-serif>",10pt);
end;
run;
ods listing style=garamond;
ods html style=garamond;
proc sgplot data = have2 noborder nowall ;
styleattrs datacontrastcolors=(black black white white white) datacolors=(VLIGB red blue black maroon);
format _1 positive.;
format _2 positive.;
hbarparm category=diagnosis response = _1/ group = group groupdisplay = stack barwidth=0.9 grouporder=ascending nooutline name="c1";
hbarparm category=diagnosis response = _2/ group = group groupdisplay = stack barwidth=0.9 grouporder=ascending nooutline ;
text x=mean1 y=diagnosis text=_1 /contributeoffsets=none strip group = group textattrs=(size = 9 weight = bold);
text x=mean2 y=diagnosis text=_2 /contributeoffsets=none strip group = group textattrs=(size = 9 weight = bold);
xaxis labelattrs = (weight = bold) display=(noticks) label = "Patients (%)" values = (-80 to 80 by 20);
yaxis display = (noline nolabel noticks) discreteorder = data valueattrs = (weight=bold color=black size=10) label="Patients";
keylegend "c1"/ across = 10 noborder title = '' valueattrs=(size=11) sortorder=ascending;
inset "Insert" / position = bottomleft textattrs = (color = black size = 10 weight = bold style = normal);
inset "Insert" / position = bottomright textattrs = (color = black size = 10 weight = bold style = normal);
run;
Thank you! This mostly worked! A few additional questions:
1. Is there a way to order the bars (can be based on the right or left side) by total bar length? Initially, I ordered my data in excel but this changed the arrangement.
2. Can you rerun the code with a more unique font? Maybe "brush". For me it seems as though the font is still not changing. I changed the "Arial" in your code to brush and the font didn't change.
3. In my dataset, several rows had zeros for the "_1" variable so I removed them from the file, leaving blank cells. One of the bars now says "dot" instead of the zero. Do you know how I can remove this? Below is a screenshot.
Thank you!
/*OK. Try this code.*/
data WORK.DIAGNOSES;
input diagnosis & $17. _2 _1 group_cat group $20.;
label diagnosis="diagnosis" _2="_2" _1="_1" group_cat="group_cat" group="group";
datalines;
ALL 52 -52 1 Group 1
ALL 2 -2 2 Group 2
AML 10 -20 1 Group 1
AML 6 -10 2 Group 2
Breast Cancer 20 -13 2 Group 2
Breast Cancer 20 -9 1 Group 1
Breast Cancer 8 -3 3 Group 3
Colon Cancer 20 -30 1 Group 1
Lung Cancer 24 -17 1 Group 1
Stomach Cancer 7 -7 4 Group 4
Stomach Cancer 4 -4 3 Group 3
Skin Cancer 1 -1 4 Group 4
Skin Cancer 1 0 3 Group 3
Pancreatic Cancer 2 -2 3 Group 3
Prostate Cancer 2 0 5 Group 5
;
proc sort data=DIAGNOSES out=have;
by diagnosis group;
run;
data have2;
set have;
by diagnosis;
if first.diagnosis then call missing(cum1,cum2);
mean1=-sum(cum1,-_1/2);
mean2=sum(cum2,_2/2);
cum1+(-_1);
cum2+_2;
drop cum1 cum2;
run;
proc sql;
create table have3 as
select *,sum(sum(_2,-_1)) as total
from have2
group by diagnosis
order by total desc,diagnosis;
quit;
proc format;
picture positive
low -< 0 = "0000"
0 <- high = "0000";
run;
ods path work.tmp(update) sasuser.templat(update) sashelp.tmplmst(read);
proc template;
define style styles.garamond;
parent=styles.htmlblue; /* Or your favorite style */
style graphfonts from graphfonts /
'GraphDataFont' = ("Arial, <MTsans-serif>",7pt)
'GraphUnicodeFont' = ("<MTsans-serif-unicode>",9pt)
'GraphValueFont' = ("Arial, <MTsans-serif>",9pt)
'GraphLabel2Font' = ("Arial, <MTsans-serif>",10pt)
'GraphLabelFont' = ("Arial, <MTsans-serif>",10pt)
'GraphFootnoteFont' = ("Arial, <MTsans-serif>",10pt)
'GraphTitleFont' = ("Arial, <MTsans-serif>",11pt,bold)
'GraphTitle1Font' = ("Arial, <MTsans-serif>",14pt)
'GraphAnnoFont' = ("Arial, <MTsans-serif>",10pt);
end;
run;
ods _all_ close;
ods listing style=garamond gpath='c:\temp\';
ods graphics /outputfmt=png reset=index imagename='want';
proc sgplot data = have3 noborder nowall ;
styleattrs datacontrastcolors=(black black white white white) datacolors=(VLIGB red blue black maroon);
format _1 positive.;
format _2 positive.;
hbarparm category=diagnosis response = _1/ group = group groupdisplay = stack barwidth=0.9 grouporder=ascending nooutline name="c1" nozerobars;
hbarparm category=diagnosis response = _2/ group = group groupdisplay = stack barwidth=0.9 grouporder=ascending nooutline nozerobars ;
text x=mean1 y=diagnosis text=_1 /contributeoffsets=none strip group = group textattrs=(size = 9 weight = bold);
text x=mean2 y=diagnosis text=_2 /contributeoffsets=none strip group = group textattrs=(size = 9 weight = bold);
xaxis labelattrs = (weight = bold) display=(noticks) label = "Patients (%)" values = (-80 to 80 by 20);
yaxis display = (noline nolabel noticks) discreteorder = data valueattrs = (weight=bold color=black size=10) label="Patients";
keylegend "c1"/ across = 10 noborder title = '' valueattrs=(size=11) sortorder=ascending;
inset "Insert" / position = bottomleft textattrs = (color = black size = 10 weight = bold style = normal);
inset "Insert" / position = bottomright textattrs = (color = black size = 10 weight = bold style = normal);
run;
1. Is there a way to order the bars (can be based on the right or left side) by total bar length? Initially, I ordered my data in excel but this changed the arrangement.
2. Can you rerun the code with a more unique font? Maybe "brush". For me it seems as though the font is still not changing. I changed the "Arial" in your code to brush and the font didn't change.
3. In my dataset, several rows had zeros for the "_1" variable so I removed them from the file, leaving blank cells. One of the bars now says "dot" instead of the zero. Do you know how I can remove this? Below is a screenshot.
After I used Chinese FONT (宋体) , I did get different result.
Thank you! I was able to arrange the bars by length! For some reason the font and "nozerobars" isn't working still but I'll troubleshoot that another time. For now, I'm happy with the figure. One last question. I'd like to export as a high-resolution figure into ppt (prior this this my figure was grainy when expanded). Do you know the best way to do this? When I use "ods powerpoint file='ppt path';" and close with "ods powerpoint close; it opens the ppt file but there's no image.
1) First way:
2) Second way:
Using PDF destination :
ods pdf file='......' ;
proc sgplot.........
run;
ods pdf close;
3)Third way:
Try SVG file :
https://blogs.sas.com/content/sgf/2014/12/19/have-you-created-scalable-vector-graphics-with-sas/
https://blogs.sas.com/content/sgf/2019/02/15/scalable-svg-html5/
4)Fourth way:
Calling @GraphGuy (a.k.a Robert.Allision)
I didn't even need to go past step 1. It worked perfectly. I genuinely cannot thank you enough for all your help! I hope to get to your level of expertise one day.
FYI...BACKLIGHT option is available on TEXT plot for such use cases. The label will be back lit with a contrasting color so the label will show up even if the bar and text colors match. This could simplify the solution.
Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 16. Read more here about why you should contribute and what is in it for you!
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.
Select SAS Training centers are offering in-person courses. View upcoming courses for: