BookmarkSubscribeRSS Feed
tc
Lapis Lazuli | Level 10 tc
Lapis Lazuli | Level 10

nytpage.png

 

Here's a SAS ODS Graphics take on a neat New York Times tile grid U.S. map that graced nearly a full page of the Jan 12th paper and was much-liked on Twitter. No details, unfortunately, on what the talented NYT folks used to prep the data, generate the chart and add the text, so I took a crack at the whole soup-to-nuts process with SAS code.

 

Note: To view a larger image, right-click above image and select Open image in new tab. To view a full-resolution image, visit Twitter post, click on thumbnail, then right-click next image and select Open image in new tab.

 

* Creating A New York Times-Like Tile Grid U.S. Map With SAS ODS Graphics
  Attempt to knockoff full-page graphic in 1-12-2022 NYT - see twitter.com/AlbertoCairo/status/1481289889421398026;
 
data NytMap;                                        * Generate coordinates for NYT tile map of U.S.; 
do r=1 to 8;                                        * Eight rows;
  input; 
  do c=1 to 11;                                     * Eleven columns;
    p=r*100+c;                                      * Assign 4-digit # to 88 cells of panel chart; 
    st=input(substr(_infile_,(c-1)*3+1,2),$2.);     * Grab 2-character state abbreviations;  
    output;  
  end; 
end;
datalines;                                          * NYT tile grid map "template";                                      
                              ME
                           VT NH
WA ID MT ND MN IL WI MI NY RI MA
OR NV WY SD IA IN OH PA NJ CT
CA UT CO NE MO KY WV VA MD DE
   AZ NM KS AR TN NC SC DC
         OK LA MS AL GA
AK HI    TX             FL
;                                                 * Read NYT COVID-19 state-level daily data;
filename nytdata url 'https://raw.githubusercontent.com/nytimes/covid-19-data/master/rolling-averages/us-states.csv'; 
proc import out=NYTdata(where=(date<='10jan2022'd)) file=nytdata dbms=csv replace; * NYT chart we're trying to replicate was thru Jan 10;

proc sql;                                         * Merge COVID-19 data with map coordinates and derive several fields;
create table NytDataMap as
select d.st, d.state, d.date, d.cases_avg_per_100k, m.p, md.maxdate,
       case when date=md.maxdate then d.cases_avg_per_100k end as EndingCases,
       case when date=md.maxdate then put(d.cases_avg_per_100k, 4.) end as EndingCasesX,
       case when pcm.prevMonthsMaxCases<currMonthMaxCases then 'Record Cases in January' else 'Cases Rising' end as color
from NytMap m 
left join (select fipstate(input(scan(geoid,2),2.)) as st, state, date, cases_avg_per_100k from nytdata) d on d.st=m.st
left join (select state, max(date) as maxdate from nytdata group by 1) md on d.state=md.state
left join (select state, max(case when date < '01jan2022'd then cases_avg_per_100k end) as prevMonthsMaxCases,
                         max(case when date between '01jan2022'd and '31jan2022'd then cases_avg_per_100k end) as currMonthMaxCases
           from nytdata group by 1) pcm on pcm.state=d.state
order by m.p, d.st, d.date;

data years;                                       * Show years the data covers (but only on WA cells);
st='WA'; p=3*100+1; yrY=.; 
do yrX='01jan2020'd, '01jan2021'd, '01jan2022'd; 
  yr="'"||substr(put(yrX,year4.),3); 
  output; 
end;

data NytDataMapYears; set nytdatamap years;       * Merge the NYT data and the year annotations;    
proc sort data=nytdatamapyears; by p st date;

data attrmap;                                     * Create an attribute to assign colors based on cases in January; 
ID="id"; value="Record Cases in January"; markercolor="red "; linecolor="red "; output;
ID="id"; value="Cases Rising           "; markercolor="gray"; linecolor="gray"; output;

*==> Use PROC SGPANEL with series and scatter plots to create the U.S. tile map;

ods listing image_dpi=200 gpath='/home/ted.conway/';
ods graphics / border=off reset antialias height=8.5in width=11in imagename="nytmap" antialiasmax=35000;
proc sgpanel data=nytdatamapyears(where=(p^=.)) noautolegend subpixel dattrmap=attrmap;
panelby p / start=topleft sort=(ascending descending) noborder novarname nowall columns=11 rows=9 missing noheader onepanel;
inset st / nolabel position=topleft textattrs=(color=black size=9.5pt weight=bold); * Show 2-digit state codes in insets;
series x=date y=cases_avg_per_100k / group=color attrid=id smoothconnect lineattrs=(pattern=solid thickness=1.5pt); * Show cases by day trend lines;
scatter x=date y=EndingCases / labelstrip datalabel datalabelattrs=(color=black weight=bold) datalabelpos=top markerattrs=(symbol=circlefilled size=11pt color=white) markeroutlineattrs=(color=white); * Create marker separated from line for ending date;  
scatter x=date y=EndingCases / group=color attrid=id markerattrs=(symbol=circlefilled) markeroutlineattrs=(color=white);
refline yrx / axis=x label=yr labelpos=min labelattrs=(color=black size=7pt weight=bold) transparency=.8 lineattrs=(color=black pattern=shortdash thickness=1.5pt); * Reference lines showing years (WA only);
colaxis display=none offsetmin=0 offsetmax=0;
rowaxis display=none offsetmin=0;
format EndingCases 4.;

*==> Use GTL to overlay text atop tile map image (one might just do this instead with editing software!);
                                                   
data dummy;                                        * Need "dummy" data for plot (one point); 
retain x y .5;

proc template;                                     * Specify text to overlay on map background image;
define statgraph image;                            
begingraph / subpixel=on border=false opaque=false;
layout overlay / xaxisopts=(display=none) yaxisopts=(display=none) walldisplay=none pad=0 outerpad=(top=0pt bottom=0pt left=0pt right=0pt);
scatterplot x=x y=y / markerattrs=(size=0);        * Dummy scatter plot (invisible marker);
                                                   * Set background to U.S. tile map created in prior step;
drawimage "/home/ted.conway/nytmap.png" / border=false height=100 layer=back heightunit=percent drawspace=graphpercent;
                                                   * Put headings + narrative + legend in space at top left of map image;
Layout Gridded / rows=10 order=columnmajor border=false autoalign=(topleft) pad=0 outerpad=0;
entry halign=left textattrs=(size=19pt weight=bold) "What to Make of Those Soaring Covid Counts " / pad=0 outerpad=0 ;
entry halign=left textattrs=(size=9pt weight=bold style=italic) halign=left "Note:" textattrs=(size=9pt style=italic weight=normal) " This is a knockoff of a 1-12-2022 New York Times graphic  " / pad=0 outerpad=0 ;
entry halign=left textattrs=(size=8.5pt) "Coronavirus case counts have reached record highs in the United States and continue to climb. Hospitalizations have surpassed last winter's  " / pad=0 outerpad=0 ; 
entry halign=left textattrs=(size=8.5pt) "wave. Deaths are also beginning to rise. The overall pattern is familiar, but a fresh perspective on how to interpret these metrics may be  " / pad=0 outerpad=0; 
entry halign=left textattrs=(size=8.5pt) "necessary as a faster but less severe variant tears through the country. Here's how to interpret the data in the coming days and weeks.  " / pad=0 outerpad=0 ; 
entry halign=left " " / pad=0 outerpad=0;
entry halign=left textattrs=(size=13pt weight=bold) "Average daily cases per 100,000 people  " / pad=0 outerpad=0 ; 
entry halign=left textattrs=(size=16pt color=red) {unicode '25cf'x} textattrs=(size=11pt color=black) " Record cases in January   " textattrs=(size=16pt color=gray) {unicode '25cf'x} textattrs=(size=11pt color=black) " Cases rising  " / pad=0 outerpad=0 ; 
Endlayout;
                                                     * Put notes in space at bottom right of map image;
Layout Gridded / rows=5 order=columnmajor border=false autoalign=(bottomright) pad=0 outerpad=0;
entry halign=left textattrs=(size=7.5pt) "  Note: All figures are 7-day " / pad=0 outerpad=0 ;
entry halign=left textattrs=(size=7.5pt) "  rolling averages as of Jan 10. " / pad=0 outerpad=0 ;
entry halign=left textattrs=(size=7.5pt) "  Source: New York Times  " / pad=0 outerpad=0 ;
entry halign=left textattrs=(size=7.5pt) "  database of reports from state " / pad=0 outerpad=0 ;
entry halign=left textattrs=(size=7.5pt) "  and local health agencies. " / pad=0 outerpad=0 ;
Endlayout;
endlayout;
endgraph;
end;
run; 
                                                      * Create the full map + text image!; 
ods listing image_dpi=200 gpath='/home/ted.conway/';
ods graphics on / reset antialias width=11in height=8.5in imagename="nytpage" border=off;
proc sgrender data=dummy template=image;
run;

 

Knockoff v. OriginalKnockoff v. Original

 

1 REPLY 1
GraphGuy
Meteorite | Level 14

Although I don't really like this kind of map, good job with the imitation (and subtle improvements) 🙂

 

Ready to join fellow brilliant minds for the SAS Hackathon?

Build your skills. Make connections. Enjoy creative freedom. Maybe change the world. Registration is now open through August 30th. Visit the SAS Hackathon homepage.

Register today!
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
  • 1 reply
  • 1215 views
  • 6 likes
  • 2 in conversation