SAS Coding: Using the DS2 Procedure
01/22/2016 by Maria Nicholson
As I recall when I first started using SAS, and I’m sure most programmers new to SAS would agree, the proprietary DATA step was a foreign and non-standard concept. I grew up on top-down structured programming and picked up objected-oriented design on-the-job along the way (who remembers SAS/AF and SCL?).
OOP has been an industry mainstay for many years, so I was pleased to learn about the new SAS 9.4 DS2 language. DS2 provides an object-oriented environment for the DATA step, which will likely lead to a more natural transition for traditional programmers delving into SAS.
DS2 is a new SAS proprietary programming language that is appropriate for advanced data manipulation. DS2 is included with Base SAS and intersects with the SAS DATA step. It also includes additional data types, ANSI SQL types, programming structure elements, and user-defined methods and packages. – SAS® 9.4 DS2 Language Reference
There is a lot to discover about DS2 — threaded processing and the predefined packages are particularly intriguing — but “user-defined methods and packages” is the phrase that initially caught my eye. That sounded to me like a great way to define, for example, a reusable package (class) of standard business rules (methods) for use in ETL or reporting. I decided to try it out on a straight forward product warranty and refund calculation.
The hypothetical warranty works like this: If you’ve owned an appliance for 30 days or less, then you can return it for a full refund of the purchase price. From 31 to 365 days, you can return it, but your refund amount will be the purchase price less $10.00 times the number of days you’ve owned it or $0, whichever is higher. After 365 days you receive no refund.
My goal was to build a reusable module that could be called many times by other programmers without their needing to know the particulars of the warranty, but giving them the freedom to make some changes to the assumptions. To that end, this is what I hoped DS2 would allow me to do:
Let’s take a look at how the warranty package can be defined to meet these requirements. If you are unfamiliar with the basics of DS2, pause here for a nice overview provided by SAS Global Forum paper DS2 with Both Hands on the Wheel.
Now that the warranty package is defined, let’s see how it computes refunds using the data set example.products.
Below is a DS2 code block that creates the data set work.refunds. The warranty package default constructor is used to instantiate the object w. Two new variables, refund and elapsedDays, are declared globally so that they can be included as columns in work.refunds. For each row in example.products, the refund method is called. It computes refund and elapsedDays using the purchase_price and date_of_purchase. Records are implicitly output to work.refunds.
libname example 'C:ProjectsZencosblogds2'; /* reference the library where the warranty package is stored */proc ds2;data work.refunds / overwrite=yes; /* create w as an instance of the warranty package with default constructor */ dcl package example.warranty w(); /* create global instances of two variables, refund and elapsedDays. These new variables are output to work.refunds */ dcl double refund having format dollar20.2; dcl double elapsedDays; method run();set example.products;/* use the refund method to compute elapsedDays and refund using the purchase_price and date_of_purchase *//* convert SAS date to double type */w.refund(purchase_price,to_double(date_of_purchase),elapsedDays,refund); end;run;quit;
The work.refunds DS2 code block has been modified to demonstrate how you can override the default property values by passing user-defined values to the constructor. Alternatively, you could change the value of a property through an assignment statement prior to the refund method call.
proc ds2;data work.refunds / overwrite=yes; /* use constructor to override default values for the dailyRate and returnDate properties */ dcl package example.warranty w(20.00,to_double(date'2015-12-31')); dcl double refund having format dollar20.2; dcl double elapsedDays; method run();set example.products;w.maxDays=100; /* you can also change a property value this way too */w.refund(purchase_price,to_double(date_of_purchase),elapsedDays,refund); end;run;
I am excited about discovering ways to use Proc DS2, its object-oriented design, and promising data processing performance improvements in my SAS 9.4 applications. I also look forward to sharing more DS2 insights and examples on the Zencos blog.