Chris Hemedinger

11月 022016
 

Fun with ODS GraphicsSAS Community member @tc (a.k.a. Ted Conway) has found a new toy: ODS Graphics. Using PROC SGPLOT and GTL (Graph Template Language), along with some creative data prep steps, Ted has created several fun examples that show off what you can do with a bit of creativity, some math knowledge, and open data.

And bonus -- since most of his examples work with SAS University Edition, it's easy for you to try them yourself. Here are some of my favorites.

Learn to draw a Jack-O-Lantern

Using the GIF output device and free data from Math-Aids.com, Ted shows how to use GTL (PROC TEMPLATE and PROC SGRENDER) to animate this Halloween icon.

learn to draw a Jack-O-Lantern

The United Polygons of America

Usually map charts with SAS require specialized procedures and map data, but here's a technique that can plot a stylized version of the USA and convey some interesting data. (You might have seen this one featured in a SAS Tech Report newsletter. Do you subscribe?)

United Polygons of America

A look at Katie Ledecky's dominance

Using a vector plot, Ted shows how this championship swimmer dominated her event during the summer games in Rio. This example contains a lot of text information too; and that's a cool trick in PROC SGPLOT with the AXISTABLE statement. Click on the image for a closer look.

Katie Ledecky dominates

Demonstrating the Bublé Sort

This example is nerdy on so many levels. It's a take on the Computer Science 101 concept of "bubble sort," an algorithm for placing a collection of items in a desired order. In this case, the items consist of Christmas songs recorded by Michael Bublé, that dreamy crooner from Canada.

See the songs sort things out
Ted posts these examples (and more) in the SAS/GRAPH and ODS Graphics section of SAS Support Communities. That's a great place to learn SAS graphing techniques, from simple to advanced, and to see what other practitioners are doing. Experts like Ted hang out there, and the SAS visualization developers often post answers to the tricky questions.

More from @tc

In addition to his community posts, Ted is an award-winning contributor to SAS Global Forum with some very popular presentations. Here are a few of his papers.

tags: ODS Graphics, SAS Communities, SGPLOT

The post Binge on this series: Fun with ODS Graphics appeared first on The SAS Dummy.

10月 182016
 

Have you seen this error when running a program in SAS Enterprise Guide?

ERROR: You cannot open WORK.YOURDATA.DATA for output access with member-level 
control because WORK.YOURDATA.DATA is in use by you in resource environment IOM 
ROOT COMP ENV.

It has a simple cause: the data set that your program is trying to write (or rewrite) is open in the data viewer. With regard to this data file, your program is in contention with the SAS Enterprise Guide application.

Usually SAS Enterprise Guide closes all open data sets before running a program or task, and that's meant to help you avoid this error. But sometimes a data set file remains open for one reason or another, and the conflict results in the error message. Fortunately, there is a simple fix.

Close All data sets window

Select Tools->View Open Data Sets. The View Open Data Sets window shows the names of the data files that SAS Enterprise Guide has open. And it offers a convenient Close All button to clear the list. Closing the data doesn't affect the contents of the file or its place in your project. It simply removes the lock that SAS Enterprise Guide is holding on the file.

If you are running multiple SAS Enterprise Guide sessions, it's possible for one session to have a lock on a file that you're trying to update in another session. The View Open Data Sets window shows only those data sets from your current session, so be sure to check your other projects if you're multitasking.

The default behavior -- close all data before running SAS programs -- is controlled in Tools->Options->SAS Programs. If you don't want SAS Enterprise Guide to close your data windows, clear that checkbox. (It's difficult for me to imagine why you would do that...but hey, we have options for everything.)

tags: SAS Enterprise Guide

The post Tip: How to close all data sets in SAS Enterprise Guide appeared first on The SAS Dummy.

10月 172016
 

SAS programmers often resort to using the X command to list the contents of file directories and to process the contents of ZIP files (or gz files on UNIX). In centralized SAS environments, the X command is unavailable to most programmers. NOXCMD is the default setting for these environments (disallowing shell commands), and SAS admins are reluctant to change it.

In this article, I'll share a SAS program that can retrieve the contents of a file directory (all of the file names), and then also report on the contents of every ZIP file within that directory -- without using any shell commands. The program uses two lesser-known tricks to retrieve the information:

  1. The FILENAME statement can be applied to a directory, and then the DOPEN, DNUM, DREAD, and DCLOSE functions can be used to retrieve information about that directory. (Check SAS Note 45805 for a better example of just this - click the Full Code tab.)
  2. The FILENAME ZIP method (added in SAS 9.4) can retrieve the names of the files within a compressed archive (ZIP or gz files). For more information, see all of my previous articles about the FILENAME ZIP access method.

I wrote the program as a SAS macro so that it should be easy to reuse. And I tried to be liberal with the comments, providing a view into my thinking and maybe some opportunities for improvement.

%macro listzipcontents (targdir=, outlist=);
  filename targdir "&targdir";
 
  /* Gather all ZIP files in a given folder                */
  /* Searches just one folder, not subfolders              */
  /* for a fancier example see                             */
  /* http://support.sas.com/kb/45/805.html (Full Code tab) */
  data _zipfiles;
    length fid 8;
    fid=dopen('targdir');
 
    if fid=0 then
      stop;
    memcount=dnum(fid);
 
    /* Save just the names ending in ZIP*/
    do i=1 to memcount;
      memname=dread(fid,i);
      /* combo of reverse and =: to match ending string */
      /* Looking for *.zip and *.gz files */
      if (reverse(lowcase(trim(memname))) =: 'piz.') OR
         (reverse(lowcase(trim(memname))) =: 'zg.') then
        output;
    end;
 
    rc=dclose(fid);
  run;
 
  filename targdir clear;
 
  /* get the memnames into macro vars */ 
  proc sql noprint;
    select memname into: zname1- from _zipfiles;
    %let zipcount=&sqlobs;
  quit;
 
  /* for all ZIP files, gather the members */
  %do i = 1 %to &zipcount;
    %put &targdir/&&zname&i;
    filename targzip ZIP "&targdir/&&zname&i";
 
    data _contents&i.(keep=zip memname);
      length zip $200 memname $200;
      zip="&targdir/&&zname&i";
      fid=dopen("targzip");
 
      if fid=0 then
        stop;
      memcount=dnum(fid);
 
      do i=1 to memcount;
        memname=dread(fid,i);
 
        /* save only full file names, not directory names */
        if (first(reverse(trim(memname))) ^='/') then
          output;
      end;
 
      rc=dclose(fid);
    run;
 
    filename targzip clear;
  %end;
 
  /* Combine the member names into a single data set        */
  /* the colon notation matches all files with "_contents" prefix */
  data &outlist.;
    set _contents:;
  run;
 
  /* cleanup temp files */
  proc datasets lib=work nodetails nolist;
    delete _contents:;
    delete _zipfiles;
  run;
 
%mend;

Use the macro like this:

%listzipcontents(targdir=c:temp, 
 outlist=work.allfiles);

Here's an example of the output.
zip file contents within the target directory

Experience has taught me that savvy SAS programmers will scrutinize my example code and offer improvements. For example, they might notice my creative use of the REVERSE function and "=:" operator to simulate and "ends with" comparison function -- and then suggest something better. If I don't receive at least a few suggestions for improvements, I'll know that no one has read the post. I hope I'm not disappointed!

tags: FILENAME ZIP, SAS programming, xcmd, ZIP files

The post List the contents of your ZIP and gz files using SAS appeared first on The SAS Dummy.

9月 132016
 

SAS releases regular updates to software products in the form of hot fixes and maintenance releases. Hot fixes are SAS' timely response to customer-reported problems, as well as a way to deliver occasional security-related updates that can affect any software product.

At SAS we call them "hot fixes." Other companies might call these "updates," "patches" or (the old-timey term) "zaps." I like the term "hot fix," because it connotes 1) a timely release of code, and 2) something that you apply to an established production system (while it's running "hot").

An important part of the hot fix process is learning when there is a new fix available. Beginning this month, we now have a new SAS Hot Fix Announcements board -- on SAS Support Communities -- where you can learn about newly available fixes. If you're a SAS administrator, these notices provide more complete and detailed information about available hot fixes. And if you are a business user, it behooves you to stay informed about fixes so you can pass information on to your IT department.

Find your fixes: by e-mail, RSS feeds or search

Subscribe optionBy using the community board to host hot fix notices, we've now provided you with many options to subscribe to this content. Like any community board, you can click Subscribe and have the notifications e-mailed to you as soon as they are posted. Or you can use your favorite RSS reader to peruse the latest entries when the timing is right for you. Finally, you can use the Search field on the hot fix board to find just the fixes you need. Simply type a SAS product name, a version number, or text from a SAS Note to find the matching hot fixes. Visit this topic on the SAS Support Communities to learn more about how to pull the information you need, or to have it pushed to you automatically.

Here's a picture of the Hot Fix RSS feed in my instance of Microsoft Outlook:

Hot fix RSS feed in Microsoft Outlook

Hot fix RSS feed in Microsoft Outlook

TSNEWS-L: it has served us well

For decades, SAS Technical Support has used the TSNEWS-L mailing list to send brief, weekly summaries of the recent hot fixes. While this is good for raising awareness that "a fix is available" for a product you use, the information in the e-mail contains no details about what issues each hot fix addresses. Nor does it include a direct link to download the fix. For now, this information will still be delivered via TSNEWS-L -- but we hope that you find the community board to be more flexible.

A peek behind the hot fix curtain

It shouldn't surprise you that we use SAS software to help bring you the details about SAS fixes.

To build these improved notfications, the SAS Communities team and SAS Technical Support worked together to automate the publishing process for hot fix bulletins. All of the hot fix details were already tracked within internal-only operational databases and displayed on the Technical Support Hot Fix download site. Now, a SAS-based process assembles this data, creates a hot fix bulletin entry for each fix and publishes the entry automatically to SAS Support Communities. It's a wonderful mix of SAS data management, REST APIs, DATA step and more. It's a 300-line SAS program (including plenty of comments) that runs every weekday morning -- and it completes in about 15 seconds. It's time well-spent to keep SAS users in the know!

tags: SAS Administrators, sas communities, SAS Technical Support

How to stay informed about SAS hot fixes was published on SAS Users.

8月 252016
 

First, if you landed on this topic because you encountered this SAS message:

ERROR 180-322: Statement is not valid or it is used out of proper order.

...then I'll tell you right now: you've probably left off a semicolon in one of your SAS statements. If you're lucky, the SAS log will show you where it happened by highlighting the offending line with the "180" error number:

create table f as select * from sashelp.class;
    ______
    180

Punctuation is important in any language. (Recommended reading: Eats, Shoots & Leaves: The Zero Tolerance Approach to Punctuation.) It's especially important in a programming language like SAS, where semicolons are the only way that SAS can determine where one instruction ends and the next one begins.

So why doesn't SAS just say, "Hey buddy -- you're missing a semicolon!" ? The reason is that there are other potential causes for this message. For example, if you drop in a statement that SAS just doesn't understand -- or doesn't understand in the current context -- then the error message "Statement is not valid or it is used out of proper order" pretty much says it all.

But I was curious about the error number: 180-322. Where did that come from? To find out, I had to step into the SAS Wayback Machine.

Error numbers are historical

At SAS, the "Wayback Machine" is personified in Rick Langston, a 35+ year SAS employee who is the main steward of the SAS programming language. I asked Rick about the origin of the error number 180-322, and he immediately logged in to SAS 82.4 to check the behavior.

That's right: He ran SAS 82.4 -- as in, the version of SAS that was available in 1982. He didn't even have to climb into his DeLorean and drive 88MPH. We actually have SAS 82.4 running on a mainframe at SAS. Here's the log from Rick's syntax error test:

1       S A S   L O G    OS SAS 82.4         MVS/XA JOB SAS824   STEP SAS      
NOTE: THE JOB SAS824 HAS BEEN RUN UNDER RELEASE 82.4 OF SAS AT SAS INSTITUTE DEV
NOTE: CPUID   VERSION = 00  SERIAL = 035EA6  MODEL = 2964 .                     
NOTE: SAS OPTIONS SPECIFIED ARE:                                                
       SORT=4                                                                   
1          DATA TEMP; X=1; BLAH;                                                
                           ____                                                 
ERROR:                     180                                                  
180:  STATEMENT IS NOT VALID OR IT IS USED OUT OF PROPER ORDER.                 

While Rick couldn't tell me why the number was set to 180 originally, it's clear why it's there today: legacy. Automated processes depend on error codes, and you can't go changing those error codes after a world of SAS users begin to rely on them. Maybe the "180" is a reference to "180 degrees," as in "turn around and look behind you: you forgot a semicolon!"

The second part of the error code, "322", indicates a grouping of related syntax error messages. Here is a sample of other messages that you'll encounter with the -322 suffix:

75-322 Syntax error, expecting one of the following: <expected keywords>
76-322 Syntax error, statement will be ignored.
77-322 The statement is being ignored.
181-322 Procedure name misspelled.
216-322 No simple repair for the syntax error. The statement is being ignored.

That last one is my favorite but I've never seen it in action. I wonder what sequence of statements would coax the "No simple repair" message into your SAS log? If you can make it happen, let me know in the comments. (It sounds like my approach when my family asks me to fix something around the house. "No simple repair -- so IGNORE." Not recommended, by the way.)

Where to learn more about ERROR and WARNING messages

If you're just getting started with SAS programming, it's a good idea to learn how to interpret the SAS log messages. Here are some papers that help:

tags: ERROR 180-322, SAS programming, SAS wayback machine

The post ERROR 180-322: The story of an error message appeared first on The SAS Dummy.

8月 172016
 

My computer geek colleagues are boasting about their humble beginnings by sharing lists of their first seven programming languages. You can find these under the hashtag #FirstSevenLanguages.

From what I've seen of these lists, the programming languages that appear are very much a function of age -- not the age of the language, but of the person sharing the list. It's also a function of industry. For people of a certain age who first worked at a bank, COBOL appears early on the list. Did you work in the defense industry? Ada is probably on your list.

Of course, the SAS programming language features prominently among my colleagues. I have argued that listing SAS is a bit of a cheat, since SAS actually comprises several different programming languages: DATA step, SQL, DS2, SAS macro, IML, GTL, SCL, and more. SAS also contains hooks into other languages like Lua and Groovy. Some SAS analytical procedures are programming languages in their own right, like PROC OPTMODEL.

I have several friends who have built their entire careers on SAS programming. There is little risk of boredom, as the SAS language evolves with each release and is used in virtually every industry. It's like a huge mansion of a programming language -- we all have our favorite rooms where we spend most of our time, but there are always new additions to discover and explore.

I've said that I don't identify myself as a programmer, even though programming is an activity that occupies lots of my time. Here's my #FirstSevenLanguages list. It's not exactly in chronological order, and like other folks I'm cheating by grouping some languages together into eras.

  • Extended basic on the TI99/4A (high school, in my parent's basement)
  • Turbo Pascal and Turbo C and Assembly (school and internships)
  • REXX and Perl (two different jobs, but used both to automate tedious tasks)
  • C++ (our first versions of SAS Enterprise Guide)
  • Java (various projects)
  • C# and .NET (SAS Enterprise Guide since the mid 2000s)
  • SAS - (first learned in a SAS education class in 1993, and still learning it)

Unlike some of my more distinguished colleagues, there are no "punch cards" languages on my list. Nostalgia is sometimes fun, but I don't believe anyone who says that the era of punch cards, 16K RAM, and 8-inch floppy disks was "the good old days." Instead, I prefer to look forward to my #NextSevenLanguages. In my current role with SAS Support Communities, I get to dabble in JavaScript, FreeMarker, and Python. But I use SAS every day and for so many tasks, it remains high on my list of languages to learn!

tags: computer science education, csedweek, stem

The post What were your #FirstSevenLanguages? appeared first on The SAS Dummy.

8月 122016
 

A colleague approached me with this very important business problem:

Every Friday at SAS HQ, SAS cafe staff provides a breakfast goodie in our breakrooms. Often the supplied goodie is delicious, but sometimes it's more...well...healthy. I want to know whether I should eat my breakfast before I leave home on Friday morning, or if it's better to save my calories for the breakfast goodie at SAS. Can I write a SAS program to send me a text message on Friday morning with the "goodie news" so I can make an informed decision?

Find the data source

bgoodieSAS HQ publishes the cafeteria menus on our intranet each day, and on Thursday the published menu includes an entry for the planned Friday goodie. Thus, we know that this "data" is available somewhere. My colleague had the idea of using FILENAME URL or PROC HTTP to have SAS read the cafe's menu web page and parse the details about the breakfast goodie. That's one way to do it, but here's something that I know about web pages: they all have source files somewhere that are used to generate the content you see in the browser. If you can gain access to that source file instead of going through the web server, you'll have a much simpler process.

With a little spelunking on the SAS network, I found the source files that feed the cafe menus. ("Feed" -- see what I did there?) The file with the goodie news is an HTML file that is named with a predictable date stamp. Here's the name for today: "../menus/DailyMenus/BreakfastWeb20160812.html". The source file looks like this:

<!-- Published on Thursday, August 11, 2016 01:02 PM-->
<h2 class="menuHeader">Friday Breakfast Goodie</h2>
<div class="entry fridayGoodie"><div class="menuLeft"><div class="menuLeftContent">  
<span class="entryName">Blueberry Almond Crunch Teacake</span>
</div></div></div>

We want just the goodie name. With a file reference to the menu file, we can easily parse this out with SAS. After this step, the goodie text is stored in the GOODIE macro variable.

/* Assuming this is run on Friday */
%let day = %sysfunc(compress(%sysfunc(today(),yymmdd10.),'-'));
filename bg "../menus/DailyMenus/BreakfastWeb&day..html";
data _null_;
  infile bg dsd;
  length line $ 80 goodie $ 40;
  input line;
  /* Line with the goods */
  if (find(line,'span class="entryName"') >0) then do;
    startpos = find(line, '>');
	endpos=find(line, '</');
	goodie = substr(line,startpos+1,endpos-startpos-1);
	call symput('GOODIE',goodie);
  end;
run;

Sending the SMS message with SAS

Finally, we're getting to the part of this article that you probably came to read: how to send the text message with SAS. This feels a bit like cheating, but there isn't any magic function in SAS that sends SMS messages. Instead, we're going to rely on a mobile phone service trick. Most phone service providers allow you to send text messages via e-mail by using a special address scheme for the text message recipient. Each carrier is a little different, but you can find the details with a simple internet search: "sms via email".

My current carrier is AT&T, and so to receive a text message as e-mail I would send it to the address my_number@txt.att.net. With the FILENAME EMAIL method, it's easy to send an e-mail using SAS. The trickiest part might be to find your SMTP server host name and other details. Using a service like Gmail? I've written a blog post about how to send e-mail with that method. Note: as with all phone text messages, text-message charges from your carrier may apply.

options emailhost='mailserver.company.com' emailsys=smtp;
 
/* NOT my real phone number */
filename msg email to="9198675309@txt.att.net" 
  FROM = "Cafe Bot <youremail@company.com>"
  subject="Breakfast goodie: &Goodie.";
 
data _null_;
 file msg;
 put 'Bon appetit!';
run;

Here's an example of the text message from my phone:

bgoodiesms
"Blueberry Almond Crunch Teacake" - yum! I'm skipping the Cheerios this morning and taking breakfast at work instead.

Scheduling the SAS job to run automatically

This entire process is valuable only if we can run it unattended, without having to remember to trigger it every Friday morning. After all, if you can log in to run a SAS job that sends yourself a text message, you can (probably more easily) just check the breakfast menu for the day. So my colleague scheduled this program to run early every Friday morning at SAS using a cron job, the ubiquitous scheduler on UNIX. I imagine that his crontab -l output looks something like:

00 05 * * 5 /usr/local/bin/sas -nodms -sysin '/u/userid/food/breakfastgoodie.sas'

That sets up the job to run at 05:00 on day 5 (Friday), running SAS with this program as input. In the immortal words of Ron Popeil: Set it, and forget it!

tags: FILENAME EMAIL, SAS programming, SMS

The post How to send a text message with SAS appeared first on The SAS Dummy.

8月 072016
 

A few months ago I shared the news about Jupyter notebook support for SAS. If you have SAS for Linux, you can install a free open-source project called sas-kernel and begin running SAS code within your Jupyter notebooks. In my post, I hinted that support for this might be coming in the SAS University Edition. I'm pleased to say that this is one time where my crystal ball actually worked -- Jupyter support has arrived!

(Need to learn more about SAS and Jupyter? Watch this 7-minute video from SAS Global Forum.)

Start coding in the notebook format

If you download or update your instance of SAS University Edition, you'll be able to point your browser to a slightly different URL and begin running SAS programs in Jupyter. Of course, you can continue to use SAS Studio to learn SAS programming skills. Having trouble deciding which to use? You don't have to choose: you can use both!

jupyter_ue
If you've started SAS University Edition within Oracle Virtual Box, you can find SAS Studio at its familiar address: http://localhost:10080/. And you can find the Jupyter notebook environment at: http://localhost:18888/. (If you're using VMWare, the URLs are slightly different. Check the documentation.)

Why did SAS add support for Jupyter notebooks? The answer is simple: you asked for it. While we believe that SAS Studio provides a better environment for producing and managing SAS code, Jupyter notebooks are widely used by students and data scientists who want to package their code, results, and documentation in the convenient notebook format. Notebook files (*.ipynb format) are even supported on GitHub, easily shareable and viewable by others.

Now, what are the limitations?

jupyter_uemenuWithin SAS University Edition, the Jupyter environment supports only SAS programs. The Jupyter project can support other languages, including Python, Julia, and R (the namesake languages) and dozens of others with published language kernels. However, because of the virtual-machine core of the SAS University Edition, those other languages are not available.

Support for other languages (as well as for the Jupyter console) is available when you use Jupyter in a standalone SAS environment. In fact, the sas_kernel project recently received some exciting updates. You can now host the Jupyter environment on a different machine than your SAS server (although Linux is still the only supported SAS host), and the installation process has been streamlined. See more on the sassoftware GitHub home for the sas_kernel project.

Where can you learn more about Jupyter in SAS University Edition?

Check out the help topics for SAS University Edition, beginning with this one: What is Jupyter Notebook in SAS University Edition?

And if you need help or advice about how to make the best use of SAS University Edition, check out the SAS Analytics U community. There are plenty of experts in the forum who would love to help you learn!

tags: github, Jupyter, SAS University Edition

The post Using Jupyter and SAS together with SAS University Edition appeared first on The SAS Dummy.

7月 232016
 

Slack is a tremendously popular app for team collaboration. At its core, it's an open messaging app that allows team members to communicate with each other in real time. It works well with "startup"-style teamwork, which requires constant communication among people who aren't always located together. I've heard a lot about Slack from colleagues and from podcasts (Slack is a frequent advertiser on tech shows), but I'd never tried it myself until recently.

I was most curious about their APIs, which allow you to integrate other applications into the Slack messaging platform. Months ago, a colleague had asked me whether it was possible to integrate SAS output with Slack. I suspected that we could do something with PROC HTTP, but didn't get around to trying until today. I can now answer for certain: Yes! It's possible...and it's sort of fun!

Get started with a Slack "webhook"

I won't go into how you get started with Slack, except to say that it's really easy (and free) to create an account and a "channel" (your message space) for a small team. Assuming that you have that going, I'm going to show you how to use SAS to publish to your channel.

Slack supports a feature called "Incoming Webhooks," which is basically a simple endpoint URL that you can send messages to. From your Slack channel, you can select to Add Configuration, which takes you to the option to add a new Incoming Webhook:

slackwebhook
Click the button and Slack will provision a cryptic URL that's your unique endpoint. Any process that uses HTTP calls to POST content to that URL can publish content. The content itself (called the payload) is delivered in JSON format to the API.

Example 1: Simple in-line message with PROC HTTP

The Slack documentation supplies examples that use curl (command-line URL tool), but PROC HTTP is the SAS equivalent. Here's a simple example:

%let webhookUrl = https://hooks.slack.com/services/<your Webhook URL>;
 
/*
  Simple example with direct text 
*/
 
filename resp temp;
proc http
 url="&webhookUrl"
 method="POST"
 /* IN= supports text in SAS 9.4m3.  Earlier release? Use fileref with content */
 in='payload={"channel": "#fromsas", 
     "username": "sasprogram", 
     "text": "Created using PROC HTTP!"}'
 out=resp
 ;
run;

To try this, you'll have to first get your own Webhook URL and plug it into the program. I'd loan you mine, but you're not on my channel so you can't check the results...which look like this:

Slack simple

Example 2: Share rich messages with PROC HTTP and JSON

Slack also allows multipart messages with simple formatting, including some colors, custom icons, and working links. This requires a little bit more JSON content in the payload, including an array of attachments. Here's a more complex example:

/*
  More complex messages, with multiple parts.
  Use the attachments fields that Slack supports
*/
filename rich temp;
 
data _null_;
 file rich;
 infile datalines4;
 input;
 put _infile_;
datalines4;
payload=
   {   
    "channel": "#fromsas", 
    "username": "sasprogram",
    "icon_emoji": ":fax:",
   "attachments":[
      {
  "fallback":
   "New SAS Dummy post!: <http://blogs.sas.com/content/sasdummy|The SAS Dummy blog>",
 "pretext":
   "New SAS Dummy post!: <http://blogs.sas.com/content/sasdummy|The SAS Dummy blog>",
         "color":"#3030F0",
         "fields":[
            {
               "title":"Great news!",
               "value":"That Chris...he's done it again!",
               "short":false
            }
         ]
      }
   ]
  }	
;;;;
 
proc http
 url="&webhookUrl"
 method="POST"
 in=rich
 out=resp
 ;
run;

Here's the result. See how I selected a nice modern emoji as the account icon? Slack has hundreds of these available.

Slack message

Example 3: Data-driven JSON payload published to Slack using PROC HTTP

But the real power of this integration from SAS is the ability to push dynamic, data-driven content to your Slack channel. To accomplish that, you need to dynamically generate your JSON content with the fields that you want to share. Here's an example that publishes the output of a SAS procedure (read from a data set) to the channel:

/*
 And finally an example that publishes values from data!
*/
 
/* Calculate some data */
proc means data=sashelp.class noprint;
var age;
output out=stats;
run;
 
/* file to hold the JSON payload */
filename msg temp;
 
/* Create the start of the JSON payload */
data _null_;
 file msg ;
 infile datalines4;
 input;
 put _infile_;
datalines4;
payload=
   {   
    "channel": "#fromsas", 
    "username": "sasprogram",
    "icon_emoji": ":fax:",
   "attachments":[
      {
         "fallback":"Latest Stats for AGE in SASHELP.CLASS",
         "pretext":"Latest Stats for AGE in SASHELP.CLASS",
         "color":"#D000FF",
         "fields":[
;;;;
 
/* fill in the data fields in the middle */
data _null_;
 file msg mod;
 set stats end=eof;
 put '{ "short":false, "title": "' _stat_ '",';
 put '"value": "' age '" }';
 /* separate values with commas, except the last */
 if not eof then put ",";
run;
 
/*
  And finish with the tail end of the payload
*/
 
data _null_;
 file msg mod;
 infile datalines4;
 input;
 put _infile_;
datalines4;
         ]
      }
   ]
  }	
;;;;
 
proc http
 url="&webhookUrl"
 method="POST"
 in=msg
 out=resp
 ;
run;

Here's the result -- the latest figures from SASHELP.CLASS!
Slack data

I've shared the complete example code on a public Gist on GitHub. Remember, to try it yourself you'll need to:

  • Create a Slack account, if you don't have one. Along with a new Slack channel.
  • Use the Slack site to add a new Incoming Webhook for your channel
  • Replace the webhookURL macro value in my example code with your specific Webhook URL.

Have fun! And if you create anything really interesting, I hope you'll invite me to your Slack channel!

tags: PROC HTTP, REST API, Slack

The post How to publish to a Slack channel with SAS appeared first on The SAS Dummy.

7月 142016
 

Definitely NOT a copyrighted Pokémon

Definitely NOT a copyrighted Pokémon

Today is #EmbraceYourGeekness day, and you are either reveling in this new crazy town inhabited by Pokémon GO, or you are hiding in your house trying to avoid all of the Pokémon GO zombies wandering around.

But since I'm living in SAS these days -- not just the place (at SAS headquarters), but the software -- I decided to see if I could use my SAS tools to "find" some Pokémon in my work. Thanks to PROC HTTP and fantastic service called the Pokéapi, I've managed some success.

Calling the Pokéapi REST API with SAS

PROC HTTP is the the SAS procedure that you can use to call REST APIs. And the Pokéapi site is a REST API that yields on-demand information about our new favorite creatures. Here's a quick example:

/* utility macro to put file contents to SAS log */
%macro echoResp(fn=);
data _null_;
 infile &fn;
 input;
 put _infile_;
run;
%mend;
 
filename resp temp;
 
/* Call the Pokeapi to list all available Pokemon */
proc http 
  url="http://pokeapi.co/api/v2/pokemon/?limit=1000"
  out=resp
  method="GET";
run;
 
%echoResp(fn=resp);

Here's a snippet of my "Pokémon log":

pokelog
I need a DATA step to read and parse some of the API response, which is in JSON. I'm using a simple INFILE with SCANOVER to parse out just a few bits and create a data set of all the character names (811 of them). The API response is basically one huge line of text, so I'm using the @@ directive to keep the INPUT statement working on the same "record."

data pokemon;
 infile resp lrecl=65635 scanover truncover;
 length name $ 20;
 input @'"name":' name $quote20. @@;
run;

If you're using the free SAS University Edition, this code should work there too! The Pokéapi site is accessed using HTTP and not HTTPS. (HTTPS doesn't work from SAS University Edition because the secure/encryption components are not included.)

pokenames
I can also use PROC HTTP and the API to gather an incredible amount of detail about each character. I found Jigglypuff at record 39, so here's my code to retrieve and parse some more details. Note that there are hundreds of attributes available for each character, and I'm pulling just a couple of them.

proc http 
  url="http://pokeapi.co/api/v2/pokemon/39"
  out=resp
  method="GET";
run;
 
data jiggly;
 infile resp lrecl=500000 scanover truncover;
 length weight 8 base_experience 8;
 input @'"weight":' weight 2. @@;
 input  @'"base_experience":' base_experience 2. @@;
run;

And the results:

jiggly

Going to "the source" for raw Pokémon data

Parsing JSON using SAS is fun and all, but sometimes you just want access to the raw data. And it turns out that the Pokéapi folks have a project on GitHub with everything we need. We can use PROC HTTP to get to that too! And then use SAS to join and analyze/visualize the results! These calls are to the GitHub site to access the "raw" view of data files in the repository. Note: GitHub does use HTTPS (sorry, SAS University Edition users...).

filename pk_csv "%sysfunc(getoption(WORK))/pokemon.csv";
 
proc http
 url="https://raw.githubusercontent.com/PokeAPI/pokeapi/master/data/v2/csv/pokemon.csv"
 method="GET"
 out=pk_csv;
run;
 
proc import file=pk_csv out=pokemon dbms=csv replace;
run;
 
filename pk_ab "%sysfunc(getoption(WORK))/pokemon_ab.csv";
 
proc http
 url="https://raw.githubusercontent.com/PokeAPI/pokeapi/master/data/v2/csv/pokemon_abilities.csv"
 method="GET"
 out=pk_ab;
run;
 
proc import file=pk_ab out=abilities dbms=csv replace;
run;
 
filename pk_abn "%sysfunc(getoption(WORK))/pokemon_abnames.csv";
 
proc http
 url="https://raw.githubusercontent.com/PokeAPI/pokeapi/master/data/v2/csv/abilities.csv"
 method="GET"
 out=pk_abn;
run;
 
proc import file=pk_abn out=abnames dbms=csv replace;
run;
 
/* Join the Pokemon with their abilities */
proc sql;
   create table work.withabilities as 
   select t3.identifier as pokemon, 
          t1.identifier as ability
      from work.abilities t2, work.pokemon t3, work.abnames t1
      where (t2.pokemon_id = t3.id and t2.ability_id = t1.id);
quit;
 
ods graphics on / height=1000 width=600;
proc freq data=work.withabilities
	order=freq
;
	tables ability / nocum  
            scores=table plots(only orient=horizontal)=freq;
run;

Here's what PROC FREQ shows about how common some of the abilities are among the Pokémon. "Levitate" appears to be common (good thing, because I'm not sure that they all have legs), and "slow start" is less common (but that's an ability that I think I can claim for myself...).

img0

Full code: I placed all code presented here in a public Gist on GitHub. Enjoy!

tags: Pokémon GO, PROC HTTP, SAS University Edition

The post Build your Pokémon library using SAS and the Pokéapi appeared first on The SAS Dummy.