BookmarkSubscribeRSS Feed
🔒 This topic is solved and locked. Need further help from the community? Please sign in and ask a new question.
Cingchih
Quartz | Level 8

Hi all,

 

I try to use these code, including ods layout, proc greplay and proc template, to combine two graphs made by proc sgplot into a png file. However, I couldn't reach this aim (see photo).

Any help appreciated.

 

S__17752083.jpg

 

data First_image;
input age sensitivity_2   specificity_2   youden_2;
datalines;
21	1	0	0
25	1	0.0625	0.0625
25	1	0.1875	0.1875
28	1	0.25	0.25
30	1	0.3125	0.3125
33	1	0.375	0.375
35	1	0.4375	0.4375
36	1	0.5	0.5
37	1	0.5625	0.5625
39	0.9375	0.625	0.5625
39	0.9375	0.75	0.6875
41	0.9375	0.8125	0.75
41	0.875	0.8125	0.6875
43	0.8125	0.8125	0.625
47	0.8125	0.875	0.6875
49	0.75	0.875	0.625
50	0.6875	0.875	0.5625
52	0.625	0.875	0.5
55	0.5	0.9375	0.4375
57	0.375	1	0.375
61	0.3125	1	0.3125
61	0.25	1	0.25
61	0.1875	1	0.1875
62	0.125	1	0.125
62	0.0625	1	0.0625
62	0	1	0
64	0	1	0
70	0	1	0
75	0	1	0
77	0	1	0
79	0	1	0
81	0	1	0
;run;

data Second_image;
input disease age;
datalines;
0	50
0	39
0	21
0	61
0	30
0	35
0	25
0	41
0	43
0	36
0	37
0	25
0	41
0	62
0	28
0	33
1	52
1	49
1	47
1	62
1	55
1	70
1	75
1	77
1	81
1	64
1	62
1	39
1	61
1	61
1	57
1	79
;run;

ods graphics / reset=index  imagefmt=png width=800px height=350px  imagename="First_image";
proc sgplot data=First_image ;
series x=age y=sensitivity_2 /  lineattrs = (color=black  pattern=1     thickness = 2 ) name="s1" legendlabel = 'Sensitivity' ;
series x=age y=specificity_2 /  lineattrs = (color=black  pattern=26  thickness = 2) name="s2" legendlabel = "Specificity";
series x=age y=youden_2  /   lineattrs = (color=red  pattern=41  thickness = 2) name="s3" legendlabel = "Youden's index";
xaxis display=(nolabel) valueattrs=(size=12) fitpolicy=rotate  labelattrs=(size=14) values=(21 to 81 by 3);
yaxis label="Proportion"   labelattrs=(size=12)  valueattrs=(size=14)  values=(0 to 1.0 by 0.1);
keylegend "s1" "s2"  "s3" / title="Association of sensitivity with specificity as a function of age" titleattrs=(color=black size=12)   valueattrs=(size=10);
refline 45.671 / axis=x lineattrs=(pattern=33 color=gray  thickness=2) label="Cut Point = 45.671" labelattrs=(size=11) labelloc=inside labelpos=min ;
run;
ods graphics off; footnote1;footnote2;footnote3;

ods graphics/ reset=index imagefmt=png width=800px height=150px imagename="Second_image" ;
proc sgplot data=Second_image noautolegend;
hbox age/ category=disease boxwidth=0.4 nooutliers; 
scatter  x=age  y=disease / jitter transparency=0.5  
         markerattrs=(color=red  symbol=circlefilled);
		 xaxis display=(nolabel) discreteorder=data;
xaxis label="Age" labelattrs=(size=15)   valueattrs=(size=12)  values=(21 to 81 by 3) ;     
yaxis label='Y axis name' labelattrs=(size=15)  valueattrs=(size=12) discreteorder=data reverse; 
format disease diseasefmt.;
refline 45.671 / axis=x lineattrs=(pattern=33 color=gray  thickness=2) label="Cut Point = 45.671" labelattrs=(size=11) labelloc=inside labelpos=min ; 
run;
ods graphics off;

/*=========================================*/


ods _all_ close;
ods listing gpath="c:\sasabc" dpi=300;
ods graphics on/ reset=index  imagefmt=png imagename="me";
ods layout  start width=840px height=550px ;
     ods region x=20px y=20px width=800px height=350px;
proc sgplot data=First_image;
series x=age y=sensitivity_2 /  lineattrs = (color=black  pattern=1     thickness = 2 ) name="s1" legendlabel = 'Sensitivity' ;
series x=age y=specificity_2 /  lineattrs = (color=black  pattern=26  thickness = 2) name="s2" legendlabel = "Specificity";
series x=age y=youden_2  /   lineattrs = (color=red  pattern=41  thickness = 2) name="s3" legendlabel = "Youden's index";
xaxis display=(nolabel) valueattrs=(size=12) fitpolicy=rotate  labelattrs=(size=14) values=(21 to 81 by 3);
yaxis label="Proportion"   labelattrs=(size=12)  valueattrs=(size=14)  values=(0 to 1.0 by 0.1);
keylegend "s1" "s2"  "s3" / title="Association of sensitivity with specificity as a function of age" titleattrs=(color=black size=12)   valueattrs=(size=10);
refline 45.671 / axis=x lineattrs=(pattern=33 color=gray  thickness=2) label="Cut Point = 45.671" labelattrs=(size=11) labelloc=inside labelpos=min ; 
run;
	 ods region x=20px y=380px width=800px height=150px;

proc sgplot data=Second_image noautolegend ;
hbox age/ category=disease boxwidth=0.4 nooutliers;   
scatter  x=age  y=disease / jitter transparency=0.5  
         markerattrs=(color=red  symbol=circlefilled);
		 xaxis display=(nolabel) discreteorder=data;
xaxis label="Age" labelattrs=(size=15)   valueattrs=(size=12)  values=(21 to 81 by 3) ;     
yaxis label='Y axis' labelattrs=(size=15)  valueattrs=(size=12) discreteorder=data reverse;
refline 45.671 / axis=x lineattrs=(pattern=33 color=gray  thickness=2) label="Cut Point = 45.671" labelattrs=(size=11) labelloc=inside labelpos=min ; 
run;

ods layout end;
title;
ods graphics off;ods listing close; 

 

1 ACCEPTED SOLUTION

Accepted Solutions
GraphGuy
Meteorite | Level 14

One way to do it would be via "brute force" by annotating the 2 graphs onto a third (blank) graph...

(my code below is very 'rough' - you'd probably want to refine it a bit)

 

/* 
Set your current-working-directory (to read/write files), if you need to ...
%let rc=%sysfunc(dlgcdir('c:\someplace\public_html')); 
*/
filename odsout '.';

data First_image;
infile datalines pad truncover dlm='09'x;
input age sensitivity_2   specificity_2   youden_2;
datalines;
21	1	0	0
25	1	0.0625	0.0625
25	1	0.1875	0.1875
28	1	0.25	0.25
30	1	0.3125	0.3125
33	1	0.375	0.375
35	1	0.4375	0.4375
36	1	0.5	0.5
37	1	0.5625	0.5625
39	0.9375	0.625	0.5625
39	0.9375	0.75	0.6875
41	0.9375	0.8125	0.75
41	0.875	0.8125	0.6875
43	0.8125	0.8125	0.625
47	0.8125	0.875	0.6875
49	0.75	0.875	0.625
50	0.6875	0.875	0.5625
52	0.625	0.875	0.5
55	0.5	0.9375	0.4375
57	0.375	1	0.375
61	0.3125	1	0.3125
61	0.25	1	0.25
61	0.1875	1	0.1875
62	0.125	1	0.125
62	0.0625	1	0.0625
62	0	1	0
64	0	1	0
70	0	1	0
75	0	1	0
77	0	1	0
79	0	1	0
81	0	1	0
;
run;

data Second_image;
infile datalines pad truncover dlm='09'x;
input disease age;
datalines;
0	50
0	39
0	21
0	61
0	30
0	35
0	25
0	41
0	43
0	36
0	37
0	25
0	41
0	62
0	28
0	33
1	52
1	49
1	47
1	62
1	55
1	70
1	75
1	77
1	81
1	64
1	62
1	39
1	61
1	61
1	57
1	79
;
run;


ODS LISTING CLOSE;
ODS HTML path=odsout body="First_Image.htm" style=htmlblue;
ods graphics / imagefmt=png imagename="First_Image" width=800px height=350px noborder;

proc sgplot data=First_image ;
series x=age y=sensitivity_2 /  lineattrs = (color=black  pattern=1     thickness = 2 ) name="s1" legendlabel = 'Sensitivity' ;
series x=age y=specificity_2 /  lineattrs = (color=black  pattern=26  thickness = 2) name="s2" legendlabel = "Specificity";
series x=age y=youden_2  /   lineattrs = (color=red  pattern=41  thickness = 2) name="s3" legendlabel = "Youden's index";
xaxis display=(nolabel) valueattrs=(size=12) fitpolicy=rotate  labelattrs=(size=14) values=(21 to 81 by 3);
yaxis label="Proportion"   labelattrs=(size=12)  valueattrs=(size=14)  values=(0 to 1.0 by 0.1);
keylegend "s1" "s2"  "s3" / title="Association of sensitivity with specificity as a function of age" titleattrs=(color=black size=12)   valueattrs=(size=10);
refline 45.671 / axis=x lineattrs=(pattern=33 color=gray  thickness=2) label="Cut Point = 45.671" labelattrs=(size=11) labelloc=inside labelpos=min ;
run;

quit;
ODS _all_ CLOSE;
ODS LISTING;


ODS LISTING CLOSE;
ODS HTML path=odsout body="Second_Image.htm" style=htmlblue;
ods graphics / imagefmt=png imagename="Second_Image" width=800px height=150px noborder;

proc sgplot data=Second_image noautolegend;
hbox age/ category=disease boxwidth=0.4 nooutliers; 
scatter  x=age  y=disease / jitter transparency=0.5  
         markerattrs=(color=red  symbol=circlefilled);
		 xaxis display=(nolabel) discreteorder=data;
xaxis label="Age" labelattrs=(size=15)   valueattrs=(size=12)  values=(21 to 81 by 3) ;     
yaxis label='Y axis name' labelattrs=(size=15)  valueattrs=(size=12) discreteorder=data reverse; 
/*format disease diseasefmt.;*/
refline 45.671 / axis=x lineattrs=(pattern=33 color=gray  thickness=2) label="Cut Point = 45.671" labelattrs=(size=11) labelloc=inside labelpos=min ; 
run;

quit;
ODS _all_ CLOSE;
ODS LISTING;



data anno1;
length function widthunit image $20;
layer='front';
function='image'; 
anchor='bottomleft'; 
x1space='graphpercent';
y1space='graphpercent';
x1=(20/840)*100; y1=(180/550)*100;
width=(800/840)*100; widthunit='percent'; 
height=(350/550)*100; heightunit='percent'; 
imagescale='tile';
image='First_Image.png';
run;

data anno2;
length function widthunit image $20;
layer='front';
function='image';
anchor='bottomleft'; 
x1space='graphpercent';
y1space='graphpercent';
x1=(20/840)*100; y1=(20/550)*100;
width=(800/840)*100; widthunit='percent';
height=(150/550)*100; heightunit='percent';
imagescale='tile';
image='Second_Image.png';
run;

data anno_all; set anno1 anno2;
run;

ODS LISTING CLOSE;
ODS HTML path=odsout body="combined.htm" style=htmlblue;
ods graphics / imagefmt=png imagename="combined" width=840px height=550px noborder;

/* generate a blank graph, and annotate the two graphs in it */
data fake_data;
x=50; y=50;
run;
proc sgplot data=fake_data noborder sganno=anno_all;
scatter x=x y=y / markerattrs=(color=white);
xaxis display=(nolabel novalues noticks noline) values=(0 to 100 by 20);
yaxis display=(nolabel novalues noticks noline) values=(0 to 100 by 20);
run;

quit;
ODS _all_ CLOSE;
ODS LISTING;

 

combined.png 

View solution in original post

8 REPLIES 8
ballardw
Super User

GREPLAY is only for device based graphics such as generated by Procs Gplot or Gchart. If the Proc Template code you mention was just to generate GREPLAY areas then that would not help.

 

Proc Template is also used for the Graphics Template Language. Which has an extremely large number of options and ways to combine graphs.

http://support.sas.com/kb/35/177.html

 

Has an example of using Proc Template GTL code to create three different types of graphs into a single image.

 

The full code tab has the program code and is commented to show which bits of code generate with parts of the graph as shown on the results tab of the link.

 

HOWEVER: You must place all of the data into a single data set.

 

Reeza
Super User
If you end up need GTL, which it seems like you do, PROC SGPLOT has a TMPLOUT (sp?) that will generate the GTL code for you behind so you can more easily migrate it to GTL.
GraphGuy
Meteorite | Level 14

One way to do it would be via "brute force" by annotating the 2 graphs onto a third (blank) graph...

(my code below is very 'rough' - you'd probably want to refine it a bit)

 

/* 
Set your current-working-directory (to read/write files), if you need to ...
%let rc=%sysfunc(dlgcdir('c:\someplace\public_html')); 
*/
filename odsout '.';

data First_image;
infile datalines pad truncover dlm='09'x;
input age sensitivity_2   specificity_2   youden_2;
datalines;
21	1	0	0
25	1	0.0625	0.0625
25	1	0.1875	0.1875
28	1	0.25	0.25
30	1	0.3125	0.3125
33	1	0.375	0.375
35	1	0.4375	0.4375
36	1	0.5	0.5
37	1	0.5625	0.5625
39	0.9375	0.625	0.5625
39	0.9375	0.75	0.6875
41	0.9375	0.8125	0.75
41	0.875	0.8125	0.6875
43	0.8125	0.8125	0.625
47	0.8125	0.875	0.6875
49	0.75	0.875	0.625
50	0.6875	0.875	0.5625
52	0.625	0.875	0.5
55	0.5	0.9375	0.4375
57	0.375	1	0.375
61	0.3125	1	0.3125
61	0.25	1	0.25
61	0.1875	1	0.1875
62	0.125	1	0.125
62	0.0625	1	0.0625
62	0	1	0
64	0	1	0
70	0	1	0
75	0	1	0
77	0	1	0
79	0	1	0
81	0	1	0
;
run;

data Second_image;
infile datalines pad truncover dlm='09'x;
input disease age;
datalines;
0	50
0	39
0	21
0	61
0	30
0	35
0	25
0	41
0	43
0	36
0	37
0	25
0	41
0	62
0	28
0	33
1	52
1	49
1	47
1	62
1	55
1	70
1	75
1	77
1	81
1	64
1	62
1	39
1	61
1	61
1	57
1	79
;
run;


ODS LISTING CLOSE;
ODS HTML path=odsout body="First_Image.htm" style=htmlblue;
ods graphics / imagefmt=png imagename="First_Image" width=800px height=350px noborder;

proc sgplot data=First_image ;
series x=age y=sensitivity_2 /  lineattrs = (color=black  pattern=1     thickness = 2 ) name="s1" legendlabel = 'Sensitivity' ;
series x=age y=specificity_2 /  lineattrs = (color=black  pattern=26  thickness = 2) name="s2" legendlabel = "Specificity";
series x=age y=youden_2  /   lineattrs = (color=red  pattern=41  thickness = 2) name="s3" legendlabel = "Youden's index";
xaxis display=(nolabel) valueattrs=(size=12) fitpolicy=rotate  labelattrs=(size=14) values=(21 to 81 by 3);
yaxis label="Proportion"   labelattrs=(size=12)  valueattrs=(size=14)  values=(0 to 1.0 by 0.1);
keylegend "s1" "s2"  "s3" / title="Association of sensitivity with specificity as a function of age" titleattrs=(color=black size=12)   valueattrs=(size=10);
refline 45.671 / axis=x lineattrs=(pattern=33 color=gray  thickness=2) label="Cut Point = 45.671" labelattrs=(size=11) labelloc=inside labelpos=min ;
run;

quit;
ODS _all_ CLOSE;
ODS LISTING;


ODS LISTING CLOSE;
ODS HTML path=odsout body="Second_Image.htm" style=htmlblue;
ods graphics / imagefmt=png imagename="Second_Image" width=800px height=150px noborder;

proc sgplot data=Second_image noautolegend;
hbox age/ category=disease boxwidth=0.4 nooutliers; 
scatter  x=age  y=disease / jitter transparency=0.5  
         markerattrs=(color=red  symbol=circlefilled);
		 xaxis display=(nolabel) discreteorder=data;
xaxis label="Age" labelattrs=(size=15)   valueattrs=(size=12)  values=(21 to 81 by 3) ;     
yaxis label='Y axis name' labelattrs=(size=15)  valueattrs=(size=12) discreteorder=data reverse; 
/*format disease diseasefmt.;*/
refline 45.671 / axis=x lineattrs=(pattern=33 color=gray  thickness=2) label="Cut Point = 45.671" labelattrs=(size=11) labelloc=inside labelpos=min ; 
run;

quit;
ODS _all_ CLOSE;
ODS LISTING;



data anno1;
length function widthunit image $20;
layer='front';
function='image'; 
anchor='bottomleft'; 
x1space='graphpercent';
y1space='graphpercent';
x1=(20/840)*100; y1=(180/550)*100;
width=(800/840)*100; widthunit='percent'; 
height=(350/550)*100; heightunit='percent'; 
imagescale='tile';
image='First_Image.png';
run;

data anno2;
length function widthunit image $20;
layer='front';
function='image';
anchor='bottomleft'; 
x1space='graphpercent';
y1space='graphpercent';
x1=(20/840)*100; y1=(20/550)*100;
width=(800/840)*100; widthunit='percent';
height=(150/550)*100; heightunit='percent';
imagescale='tile';
image='Second_Image.png';
run;

data anno_all; set anno1 anno2;
run;

ODS LISTING CLOSE;
ODS HTML path=odsout body="combined.htm" style=htmlblue;
ods graphics / imagefmt=png imagename="combined" width=840px height=550px noborder;

/* generate a blank graph, and annotate the two graphs in it */
data fake_data;
x=50; y=50;
run;
proc sgplot data=fake_data noborder sganno=anno_all;
scatter x=x y=y / markerattrs=(color=white);
xaxis display=(nolabel novalues noticks noline) values=(0 to 100 by 20);
yaxis display=(nolabel novalues noticks noline) values=(0 to 100 by 20);
run;

quit;
ODS _all_ CLOSE;
ODS LISTING;

 

combined.png 

Cingchih
Quartz | Level 8

Thank you for your reply. I still can't draw the merged picture. I will use photoshop for the time being. In addition,  I will find time to study proc template and proc sgrender.

GraphGuy
Meteorite | Level 14

Are you getting errors when you try to run the code?

Cingchih
Quartz | Level 8

I tried to draw the combined image via the code again, and I finally succeed tonight. Thank you.

I have two new questions described in the picture below. Thanks in advance!

 

Image 011.png

 

GraphGuy
Meteorite | Level 14

x1, y1 is the location of the bottom/left corner of the annotated image, in the final (combined) png ... as a percentage.

 

If the bottom annotated graph is 20 pixels from the bottom edge, and the final png is 550 pixels tall, then y1 is (20/550)*100 ... which is 3.63%

Cingchih
Quartz | Level 8
I understand it. Thank you very much. ^____^

sas-innovate-2024.png

Available on demand!

Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.

 

Register now!

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.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 8 replies
  • 6538 views
  • 2 likes
  • 4 in conversation