SAS Graph has never been my forte, but here is at least an attempt that even newbies can replicate:
data need (drop=t); do t=0 to 2*constant("pi") by 0.1; x=16*sin(t)**3; y =13*cos(t) - 5*cos(2*t) - 2*cos(3*t) - cos(4*t); output; end; x=16.6; y=16.5; output; run; proc sgplot noautolegend; title 'Be my valentine'; scatter y=y x=x / markerattrs=(symbol=squarefilled color=red); lineparm x=0 y=0 slope=1 /LINEATTRS=(pattern=MediumDashShortDash thickness=5 color=red); run;
Not as nice or fancy as the others, but even graphic newbies can have/show love on Valentine's Day:
Art, CEO, AnalystFinder.com
Nice work @art297! One of the beauties with SAS... there is always something to learn!
data carsheart;
set sashelp.cars;
heartsymbol='Heart';
run;
proc sgplot data=carsheart noautolegend;
symbolimage name=Heart image="C:\Users\ptimusk\Dropbox\SASUniversityEdition\myfolders\symbolheartlarge.png" ;
styleattrs datasymbols=(Heart);
reg x=EngineSize y=mpg_city / group=heartsymbol lineattrs=(color=black pattern=dash)
;;
run;
C:\Users\ptimusk\Dropbox\SASUniversityEdition\myfolders\symbolheartlarge.png
I borrowed code from Sanjay Matange 's blog and used a creative commons cupid heart symbol
https://blogs.sas.com/content/graphicallyspeaking/2015/01/14/marker-symbols/
/* Love is often unnecessarily complicated, just like this code! */
data _null_;
retain SheLovesMe Valentine;
input Kiss @@;
length Valentine $20.;
do Enamored = 1 to Kiss;
Cupid + 1;
Valentine = Cats( Valentine, SheLovesMe, SheLovesMe*3 );
if not mod( Cupid, 10 ) then do;
call execute('%put ERROR-' || translate( Valentine, " < ", "01." ) || ';');
Valentine = "";
end;
end;
SheLovesMe = not SheLovesMe;
cards;
1 3 2 3 1 30 1 8 3 6 5 4 7 2 14
;
run;
Oh @Cowboy_Coffee your poetic SAS code is just as lovely (or perhaps more) than the output... charming choice of variable names
Cheers,
Michelle
P.S. Welcome to the SAS Community too - a beautiful first post!
Well done @Cowboy_Coffee! I like how this entry relies on errors to express its message of love -- just like real life.
Also, I learned something new! I didn't know that you could use the minus symbol to suppress the prefix ERROR in your custom error messages!
%put ERROR: This is an error that includes the prefix "ERROR";
%put ERROR- This is an error that suppresses the prefix "ERROR";
Back to the serious datavizzes tomorrow. But first, a Wi-Fi inspired SAS ODS Graphics animated GIF valentine.
* Fun w/SAS ODS Graphics: Radiate Love Valentine (inspired by WiFi-like heart at bestanimations.com);
data heart; * Unicode heart at bottom of valentine;
xH=3; yH=2; txtH=unicode('\u2764');
data mask; * Points for polygon points used to mask parts of circles to create arcs;
id=1; input xP yP@@;
cards;
3 2 -1 6 -1 0 7 0 7 6 3 2
;
data valentine; set heart mask; * Merge points;
ods _all_ close; * Plot valentine (animated GIF with 4 frames);
options papersize=('5 in', '5 in') printerpath=gif animation=start animloop=YES NOANIMOVERLAY nodate nonumber;
ods printer file='/folders/myfolders/RadiateLoveValentine.gif';
ods graphics / width=5in height=5in imagefmt=GIF antialias;
%macro valentine;
%do frame=1 %to 4;
%if &frame=1 | &frame=4 %then options animduration=.4; %else options animduration=.15;;
proc sgplot data=valentine noautolegend noborder pad=0 aspect=1 nowall subpixel;
styleattrs backcolor=cxffbcd9; /* Cotton candy pink */
%if &frame>=2 %then /* Different-sized circles centered at 3,2 create wifi-like signals */
ellipseparm semimajor=1.25 semiminor=1.25 / xorigin=3 yorigin=2 clip outline lineattrs=(color=white thickness=30pt);;
%if &frame>=3 %then
ellipseparm semimajor=2 semiminor=2 / xorigin=3 yorigin=2 clip outline lineattrs=(color=white thickness=30pt);;
%if &frame>=4 %then
ellipseparm semimajor=2.75 semiminor=2.75 / xorigin=3 yorigin=2 clip outline lineattrs=(color=white thickness=30pt);;
polygon x=xP y=yP id=id / fill fillattrs=(color=cxffbcd9); /* Mask circles at 45 degree angles to create wifi arcs */
text x=xH y=yH text=txtH / textattrs=(family="Arial Unicode MS" size=108pt color=red) position=center vcenter=bbox contributeoffsets=none strip;
xaxis display=none offsetmin=0 offsetmax=0 min=.4 max=5.6 values=(.4 5.6); * Suppress labels/ticks on axes, set bounds;
yaxis display=none offsetmin=0 offsetmax=0 min=.4 max=5.6 values=(.4 5.6);
run;
%end;
%mend;
%valentine;
options printerpath=gif animation=stop;
ods printer close;
"The signal strength of my love for you is strong. Connect to my network with the password B3-M1ne."
/*
Canadian Census Population Census Data (1971-2017)
Inverted pyramid plotted to see which
year yeilds the ideal heart shape.
(Code was initially written for just one year plot)
Data and output file attached
Author: Aroop Ghosh
Date: Feb 13, 2018
*/
libname dat '~/dat/';
options mprint;
/* Age Interval */
%macro agecat(n);
proc format;
value agef
%do i=0 %to 99 %by &n;
%let j= %eval(&i+(&n-1));
&i - &j = "&i - &j"
%end;
;
run;
%mend;
%agecat(5);
%macro period(yr);
data selyr;
set dat.pop
(rename=('Age group (5,6)'n=Age_c sex = gender "&yr"n=y&yr));
keep Age_c gender y&yr;
run;
data x&yr (drop=age_c First_Pos First_Length);
set selyr;
length Age 8;
call scan(Age_c, 1, First_Pos, First_Length);
Age=input(compress(substrn(Age_c, First_Pos, First_Length)),3.0);
run;
proc sort;
by age gender;
run;
proc transpose data=x&yr out=t&yr
(drop=_name_ _label_ rename=( 'Both sexes'n = Total));
id gender;
by age;
var y&yr;
run;
Proc summary data=t&yr;
* nway;
class Age;
var Total Males Females;
output out=S&yr (drop=_:)
sum=;
format Age agef.;
run;
data _null_;
set s&yr;
where age=.;
call symput("Mtot",Males);
call symput("Ftot",Females);
run;
%put Males total - &mtot;
%put Females total - &ftot;
Data p&yr;
set s&yr;
where age^=.;
P_Male=Males/&mtot*100;
P_Female=(Females/&ftot)*-100;
run;
proc format;
picture ve
low - < 0 = '09%'
0 < - high = '09%';
run;
proc sgplot data=p&yr;
format P_Male P_Female ve. age agef.;
hbar Age / response=P_Male
fillattrs=(color=blue) transparency=.8
legendlabel="Population Male &yr" name="Males";
hbar Age / response=P_Female
fillattrs=(color=red) transparency=.8
legendlabel="Population Female &yr" name="Females";
keylegend "Males" "Females";
xaxis display=(nolabel) grid;
yaxis display=all discreteorder=data;
label Age ='Age Distribution';
title "Population Pyramid Canada: &yr";
title2 '(Age Category Shown As a Percentage of Total for Each Gender)';
footnote justify=left 'Source: Statistics Canada, CANSIM';
run;
%mend;
%macro cycle;
%do yr=1971 %to 2017;
%period(&yr);
%end;
%mend;
%cycle;
I guess 1973 was a good year for Love in Canada?
SAS Blog just published a new post using my idea and technique:
Aroop
*/ Happy Valentines day - Be loved;
proc format;
value x 1='d' .5='e' 0='v' -.5='o' -1='L' other=' ';
value y -2='*' -1='e' 0='B' other=' ';
run;
data h;
pi = constant("PI");
do r = 0 to pi by .02;
x = cos(r)*.5+.5;y1 = sin(r);y0 = 2.5*sin(r/2+pi);output;
x=-x;output;
end;
run;
proc sort ;by x;run;
proc sgplot data=h;
band x=x upper=y1 lower=y0/fillattrs=(color=CXFF0000);
format x x. y1 y0 y.;
xaxis valueattrs=(color=CXFF0000 weight=bold size=30 family="Brush Script MT");
yaxis valueattrs=(color=CXFF0000 weight=bold size=30 family="Brush Script MT");
run;
Thanks to all for the wonderful contributions! You all have demonstrated your romantic nerd skills admirably.
Thanks especially to @tc, @Estelalutero, @MichelleHomes, @ghosh, @Cowboy_Coffee, @art297, @ptimusk, @jl3, @OliviaWright, and @chinki.
I've captured all of the entries in a gallery on the blog post.
%let k = 6;
data Rose;
do theta = 0 to 2*constant("pi") by 0.05;
r = cos(&k * theta);
x = r*cos(theta);
y = r*sin(theta);
output;
end;
run;
ods graphics / imagefmt = GIF width=4in height= 3in ;
ods printer file="C:\Users\rbemile\Documents\SASStuff\Output\rose_card.gif";
ods html select none;
title color=red "I wish you A Happy Valentine's Day";
footnote color = red "From Me";
proc sgplot data=Rose aspect=1;
series x=x y=y / lineattrs= graphfit;
xaxis min=-1 max=1;
yaxis min=-1 max=1;
run;
options printerpath=gif;
ods printer close;
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 25. 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.