Showing posts with label macro. Show all posts
Showing posts with label macro. Show all posts

Thursday, June 2, 2011

Using Proc IML for credit risk validation


Validation step is crucial for a scorecard in credit risk industry. Gunter and Peter mentioned in their fantastic book [Ref. 1] that cumulative accuracy profile (CAP) and receiver operating characteristic (ROC) are two popular methods. Thus, the values of accuracy ratio from CAP (or I refer it as Gini coefficient) and area under curve(AUC) from ROC would be important metrics to evaluate the discriminatory power of the scorecard. And actually they can be derived from each other by their linear relationship.

In the latest post of his blog, Rick Wicklin introduced how to implement the trapezoidal rule or calculate trapezoid areas under curve by a function of Proc IML [Ref. 2]. Although the same methodology can be realized by Data Step array or Proc FCMP, the beauty of Rick’s method is that it avoids the loops through IML’s matrix operation and therefore is more efficient and scalable. In the example below, I built Rick’s function into a macro to calculate AUC and accuracy ratio for a tiny testing dataset. The ‘TrapIntegral’ function can be further applied for validation of large-scale credit risk records.

References:
1. Gunter Löeffler and Peter Posch. ‘Credit Risk Modeling using Excel and VBA’. The 2nd edition. Wiley.
2. Rick Wicklin. ‘The Trapezoidal Rule of Integration’. The Do Loop. 01JUN2011.

/*******************READ ME*********************************************
* - Using Proc IML for credit risk validation -
*
* SAS VERSION: 9.2.2
* EXCEL VERSION: 2007
* DATE: 02jun2011
* AUTHOR: hchao8@gmail.com
*
****************END OF READ ME******************************************/

****************(1) MODULE-BUILDING STEP********************************;
%macro auc(data =, path =, filename =);
/*****************************************************************
* MACRO: auc()
* GOAL: calcuate auc and accuracy ratio for default risk
* PARAMETERS: data = dataset to use
* path = output path
* filename = name for validation card
*****************************************************************/
options mprint mlogic;
ods listing close;
proc sql;
select sum(default) into :totaldef
from &data;
quit;

data _tmp01;
set &data nobs = nobs;
xratio = ifn(default = 0, 1, 0)/(nobs - &totaldef);
yratio = default/&totaldef;
run;
proc sort data = _tmp01 out = _tmp02;
by descending rating descending default;
run;
data _tmp03;
set _tmp02;
by descending rating;
retain x y;
if _n_ = 1 then do; x = 0; y = 0; end;
x + xratio;
y + yratio;
if last.rating;
run;
data _tmp04;
if _n_ = 1 then do; x = 0; y = 0; end; output;
set _tmp03(keep = x y);
run;

proc iml;
use _tmp04;
read all var{x y};
start TrapIntegral(x,y);
N = nrow(x);
dx = x[2:N] - x[1:N-1];
meanY = (y[2:N] + y[1:N-1])/2;
return( dx` * meanY );
finish;
area = TrapIntegral(x,y);
acuratio = 2*area - 1;
call symput('area', left(char(area)));
call symput('acuratio', left(char(acuratio)));
quit;

ods html file = "&path&filename..xls" gpath = "&path" style = harvest;
title; footnote;
proc print data = &data label noobs;
run;

proc sgplot data = _tmp04;
series x = x y = y ;
scatter x = x y = y;
band x =x upper = y lower = 0 / transparency=.5;
xaxis grid;
yaxis grid;
inset "AUC is: %sysfunc(putn(&area, 8.4));
Accuracy Ratio is: %sysfunc(putn(&acuratio, 8.4))"
/ position = bottomright border;
keylegend "scatter";
run;
ods html close;

proc datasets;
delete _:;
quit;
ods listing;
%mend auc;

****************(2) TESTING STEP****************************************;
data test;
input Observation Rating $ Default;
label rating = 'Rating(A is best)'
default = 'Default(1=default)';
cards;
1 A 0
2 A 0
3 A 0
4 B 1
5 B 0
6 B 0
7 C 1
8 C 1
9 C 1
10 C 0
;;;
run;

%auc(data = test, path = h:\, filename = valid);
****************END OF ALL CODING***************************************;

Wednesday, May 18, 2011

A macro calls random forest in SAS




SASHELP.CARS, with 428 observations and 15 variables, is a free dataset in SAS for me to exercise any classification methods. I always have the fantasy to predict which country a random car is manufactured by, such as US, Japan or Europe. After trying many methods in SAS, including decision tree, logistic regression, k-NN and SVM, I eventually found that random forest, an ensemble classifier of many decision trees [Ref. 1], can slash the overall misclassification rate to around 25%. The SAS code is powered by R’s package ‘randomForest’. In my tiny experiment, it seems that the ensemble of 100 trees would achieve optimum effect.

The concept of random forest was first raised by Leo Breiman and Adele Cutler [Ref. 2]. They also developed elegant Fortran codes for it. Andy Liaw in Merck did a fantastic job to port those Fortran codes into R [Ref. 3]. Now everybody with a computer can use this state of the art classification method for fun or work.

Reference:
1. Albert Montillo. ‘Random Forest’. http://www.ist.temple.edu/
2. Leo Breiman and Adele Cutler. http://stat-www.berkeley.edu/users/breiman/RandomForests/
3. Andy Liaw. ‘randomForest: Breiman and Cutler's random forests for classification and regression’. http://cran.r-project.org/web/packages/randomForest/index.html

/*******************READ ME*********************************************
* - A macro calls random forest in SAS by R -
*
* SAS VERSION: 9.1.3
* R VERSION: 2.13.0 (library: 'randomForest', 'foreign')
* DATE: 18may2011
* AUTHOR: hchao8@gmail.com
*
****************END OF READ ME******************************************/

****************(1) MODULE-BUILDING STEP********************************;
%macro rf(train = , validate = , result = , targetvar = , ntree = ,
tmppath = , rpath = );
/*****************************************************************
* MACRO: rf()
* GOAL: invoke randomForest in R to perform random forest
* classification in SAS
* PARAMETERS: train = dataset for training
* validate = dataset for validation
* result = dataset after prediction
* ntree = number of trees specified
* targetvar = target variable
* tmppath = temporary path for exchagne files
* rpath = installation path for R
*****************************************************************/
proc export data = &train outfile = "&tmppath\sas2r_train.csv" replace;
run;
proc export data = &validate outfile = "&tmppath\sas2r_validate.csv" replace;
run;
proc sql;
create table _tmp0 (string char(200));
insert into _tmp0
set string = 'train=read.csv("sas_path/sas2r_train.csv",header=T)'
set string = 'validate=read.csv("sas_path/sas2r_validate.csv",header=T)'
set string = 'sink("sas_path/result.txt", append=T, split=F)'
set string = 'require(randomForest,quietly=T)'
set string = 'model=randomForest(sas_targetvar~ .,data=train,'
set string = 'do.trace=10,ntree=sas_treenumber,importance=T)'
set string = 'predicted = predict(model,newdata=validate,type="class")'
set string = 'result=as.data.frame(predicted)'
set string = 'importance(model)'
set string = 'table(validate$sas_targetvar, predicted)'
set string = 'require(foreign, quietly=T)'
set string = 'write.foreign(result,"sas_path/r2sas_tmp.dat",'
set string = '"sas_path/r2sas_tmp.sas",package="SAS")';
quit;
data _tmp1;
set _tmp0;
string = tranwrd(string, "sas_treenumber", "&ntree");
string = tranwrd(string, "sas_targetvar", propcase("&targetvar"));
string = tranwrd(string, "sas_path", translate("&tmppath", "/", "\"));
run;
data _null_;
set _tmp1;
file "&tmppath\sas_r.r";
put string;
run;

options xsync xwait;
x "cd &rpath";
x "R.exe CMD BATCH --vanilla --slave &tmppath\sas_r.r";

data _null_;
infile "&tmppath\result.txt";
input;
if _n_ = 1 then put "NOTE: Statistics by R";
put _infile_;
run;

%include "&tmppath\r2sas_tmp.sas";
data &result;
set &validate;
set rdata;
run;
%mend rf;

****************(2) TESTING STEP****************************************;
%rf(train = cars_train, validate = cars_validate, result = cars_result,
targetvar = origin, ntree = 100, tmppath = c:\tmp,
rpath = D:\Program Files\R\R-2.13.0\bin);

****************END OF ALL CODING***************************************;

Saturday, May 14, 2011

Macros communicate SQLite and SAS without ODBC


SQLite is an open-source relationship database management system with full functionality [Ref.1]. The light-weight (300k+ size) and zero configuration features distinguish it from its’ 800-pound counterparts like Oracle or MySQL. Thanks to the rise of mobile devices (plus SQLite-embedded Firefox), SQLite will probably be seen everywhere pretty soon.

I just love SQLite, since SQLite helped me learn not only writing SQL codes on Windows and Linux, but also managing complicated databases. Both Python and R have nice support for SQLite. And I always expect to implement SQLite as a frontend or backup for SAS. The shortcut is to apply some 3rd-party SQLite’s ODBC drivers [Ref. 2]. However, those drivers never worked very well on my workstations. To bypass the ODBC method, Wensui designed a macro to use SQLite’s ‘.dump’ operator to generate SQL file for SAS [Ref. 3]. To establish a two-way communication, I wrote two macros below to export SQLite’s table to SAS, and vice versa. The macros utilized tab-delimited text as medium, and SQLite’s batch mode to execute the script on a PC.

Reference:
1. Grant Allen, Mike Owens. ‘The Definitive Guide to SQLite’. Apress Publishing.
2. SQLite ODBC Driver. http://www.ch-werner.de/sqliteodbc/
3. Wensui Liu. ‘Sas Macro Importing Sqlite Data Table Without Odbc’.

/*******************READ ME*********************************************
* - Macros communicate SQLite and SAS without ODBC -
*
* SAS VERSION: 9.1.3
* SQLITE VERSION: 3.7.4
* DATE: 14may2011
* AUTHOR: hchao8@gmail.com
*
****************END OF READ ME******************************************/

****************(1) MODULE-BUILDING STEP********************************;
******(1.1) BUILD A MACRO FROM SAS TO SQLITE****************************;
%macro sas2sqlite(sastable = , path = , database = );
/*****************************************************************
* MACRO: sas2sqlite()
* GOAL: output a dataset in SAS to a table in SQLite
* PARAMETERS: sastable = dataset in SAS for SQLite
* path = destinate file path for SQLite database
* database = name of SQLite database
*****************************************************************/
proc export data = &sastable outfile = "&path\sas_2_sqlite.txt" dbms = tab
repalce;
putnames = no;
run;

ods listing close;
ods output variables = _varlist;
proc contents data = &sastable;
run;
proc sort data = _varlist;
by num;
run;

data _tmp01;
set _varlist;
if lowcase(type) = 'num' then vartype = 'real';
else if lowcase(type) = 'char' then vartype = 'text';
run;
proc sql noprint;
select trim(variable) ||' '|| trim(vartype)
into: table_value separated by ', '
from _tmp01
;quit;

proc sql;
create table _tmp02 (string char(800));
insert into _tmp02
set string = '.stats on'
set string = 'create table sas_table(sas_table_value);'
set string = '.separator "\t"'
set string = ".import 'sas_path\sas_2_sqlite.txt' sas_table"
;quit;

data _tmp03;
set _tmp02;
string = tranwrd(string, "sas_table_value", "&table_value");
string = tranwrd(string, "sas_table", "&sastable");
string = tranwrd(string, "sas_path", "&path");
run;
data _null_;
set _tmp03;
file "&path\sas_2_sqlite.sql";
put string;
run;
options noxsync noxwait;
x "sqlite3 -init &path\sas_2_sqlite.sql &path\&database";

proc datasets;
delete _:;
quit;
ods listing;
%mend;

******(1.2) BUILD A MACRO FROM SQLITE TO SAS***************************;
%macro sqlite2sas(sqlitetable = , path = , database = );
/*****************************************************************
* MACRO: sqlite2sas()
* GOAL: output a table in SQLite to a dataset in SAS
* PARAMETERS: sqlitetable = table in SQLite for SAS
* path = target file path for SQLite database
* database = name of SQLite database
*****************************************************************/
proc sql;
create table _tmp0 (string char(800));
insert into _tmp0
set string = ".output 'output_path\sqlite_2_sas.txt' "
set string = '.separator "\t" '
set string = '.headers on'
set string = 'select * from sqlite_table;'
set string = '.output stdout'
;quit;
data _tmp1;
set _tmp0;
string = tranwrd(string, "sqlite_table", "&sqlitetable");
string = tranwrd(string, "output_path", "&path");
run;
data _null_;
set _tmp1;
file "&path\sas_2_sqlite.sql";
put string;
run;

options noxsync noxwait;
x "sqlite3 -init &path\sas_2_sqlite.sql &path\&database ";

proc import datafile = "&path\sqlite_2_sas.txt" out = &sqlitetable
dbms = dlm replace;
delimiter = '09'x;
guessingrows = 10000;
run;
proc datasets nolist;
delete _:;
run;
%mend;

****************(2) TESTING STEP****************************************;
******(2.1) TESTING THE FIRST MACRO*************************************;
data iris;
set sashelp.iris;
run;
%sas2sqlite(sastable = iris, path = c:\tmp, database = sas_sqlite.sqlite);

******(2.2) TESTING THE SECOND MACRO************************************;
proc datasets;
delete iris;
quit;
%sqlite2sas(sqlitetable = iris, path = c:\tmp, database = sas_sqlite.sqlite);

****************END OF ALL CODING***************************************;

Tuesday, May 3, 2011

Support vector machine in SAS by R


I just recently discovered endless fun to synchronize SAS and R to do something meaningful. Yep, I am a SAS programmer: during the day time, I use SAS for my work; at the evening, I use R for entertainment. It is always exciting to hook up them together. How about a SAS/R module, like SAS/STAT or SAS/BASE, in the future?

Some SAS programmers or SAS ‘developers’ already utilized coding to communicate SAS and R [Ref. 1 and 2] (thanks to Rick Wicklin’s mentioning). Since R can write dataset in SAS code (the ‘foreign’ package) and SAS can use call R in X command to do batch execution, so far I didn’t find much difficulty to use SAS as a GUI for R.

Support vector machine (SVM) is a cool and fancy classification method. It is said that a secret SVM procedure is already running under SAS Enterpriser Miner (I did not get a chance to try it yet). The package ‘e1071’ in R provides the state-of-art protocols for SVM classification. Then I made a small macro to call it in SAS, which performs like a typical SAS procedure. Hope everyone who is doing data could enjoy it.

Reference:
1.Philip R Holland ‘SAS to R to SAS’. Holland Numerics Limited.
2.Phil Rack. ‘A Bridge to R for SAS Users’. www.MineQuest.com

/*******************READ ME*********************************************
* - SUPPORT VECTOR MACHINE FOR CLASSIFICATION IN SAS BY R -
*
* SAS VERSION: SAS 9.1.3
* R VERSION: R 2.13.0 (library: 'e1071', 'foreign')
* DATE: 03may2011
* AUTHOR: hchao8@gmail.com
*
****************END OF READ ME******************************************/

****************(1) MODULE-BUILDING STEP******************;
%macro svm(train = , validate = , result = , targetvar = , tmppath = , rpath = );
/*****************************************************************
* MACRO: svm()
* GOAL: invoke e1071 in R to perform support vector machine
* classification in SAS
* PARAMETERS: train = dataset for training
* validate = dataset for validation
* result = dataset after prediction
* targetvar = target variable
* tmppath = temporary path for exchagne files
* rpath = installation path for R
*****************************************************************/
proc export data = &train outfile = "&tmppath\sas2r_train.csv" replace;
run;
proc export data = &validate outfile = "&tmppath\sas2r_validate.csv" replace;
run;

proc sql;
create table _tmp0 (string char(80));
insert into _tmp0
set string = 'train=read.csv("sas_path/sas2r_train.csv",header=T)'
set string = 'validate=read.csv("sas_path/sas2r_validate.csv",header=T)'
set string = 'require(e1071,quietly=T)'
set string = 'model=svm(sas_targetvar ~ . ,data=train)'
set string = 'predicted=predict(model,newdata=validate,type="class")'
set string = 'result=as.data.frame(predicted)'
set string = 'require(foreign, quietly=T)'
set string = 'write.foreign(result,"sas_path/r2sas_tmp.dat",'
set string = '"sas_path/r2sas_tmp.sas",package="SAS")';
quit;
data _tmp1;
set _tmp0;
string = tranwrd(string, "sas_targetvar", propcase("&targetvar"));
string = tranwrd(string, "sas_path", translate("&tmppath", "/", "\"));
run;
data _null_;
set _tmp1;
file "&tmppath\sas_r.r";
put string;
run;

options xsync xwait;
x "cd &rpath";
x "R.exe CMD BATCH --vanilla --slave &tmppath\sas_r.r";

data _null_;
infile "&tmppath\sas_r.r.rout";
input;
if _n_ = 1 then put "NOTE: Time used by R";
put _infile_;
run;

%include "&tmppath\r2sas_tmp.sas";
data &result;
set &validate;
set rdata;
run;

proc datasets nolist;
delete _: rdata;
quit;
%mend;

****************(2) TESTING STEP******************;
******(2.1) BUILD A PARTITION MACRO TO SEPARATE TESTING DATASET*************;
%macro partbyprop2(data = , targetvar = , samprate = , seed = , train = , validate = );
/**************************************************************
* MACRO: partbyprop2()
* GOAL: partition dataset by target variable's proportion
* and choose numerical variables for classification
* PARAMETERS: data = input dataset
* targetvar = target variable
* samprate = ratio of train v.s. validate datasets
* seed = random seed for sampling
**************************************************************/
ods listing close;
ods output variables = _varlist;
proc contents data = &data;
run;
proc sql;
select variable into: num_var separated by ' '
from _varlist
where lowcase(type) = "num";
quit;

proc sort data = &data out = _tmp1;
by &targetvar;
run;

proc surveyselect data = _tmp1 samprate = &samprate
out = _tmp2 seed = &seed outall;
strata &targetvar / alloc = prop;
run;

data &train &validate;
set _tmp2;
format _numeric_ best13.;
keep &num_var &targetvar;
if selected = 0 then output &train;
else output &validate;
run;

proc datasets;
delete _:;
quit;
ods listing;
%mend;

******(2.2) DIVIDE SASHELP.IRIS DATASET INTO TWO EQUAL PARTS*************;
%partbyprop2(data = sashelp.iris, targetvar = species, samprate = 0.5, seed = 20110503,
train = iris_train, validate = iris_validate);

******(2.3) USE THE SVM MACRO*************;
%svm(train = iris_train, validate = iris_validate, result = iris_result,
targetvar = species, tmppath = c:\tmp, rpath = c:\Program Files\R\R-2.13.0\bin);

****************(3) VISUALIZATION STEP******************;
data iris_visual;
set iris_result;
length color shape $8.;
predvalue = put(predicted, predictd.);
if species = "Setosa" then shape = "club";
if species = "Versicolor" then shape = "diamond";
if species = "Virginica" then shape = "spade";
if predvalue = "Setosa" then color = "blue";
if predvalue = "Versicolor" then color = "red";
if predvalue = "Virginica" then color = "green";
run;

ods html style = harvest
proc g3d data = iris_visual;
scatter PetalLength * PetalWidth = SepalLength /
color = color shape = shape;
run;quit;
ods html close;

****************END OF ALL CODING***************************************;
 
Link of r2sas_tmp.sas

Wednesday, April 27, 2011

A macro calls R in SAS for paneled 3d plotting


SAS and R could complement each other. SAS is a versatile ETL (extraction, transformation and loading) machine and its statistical procedures based on generalized linear model are impeccable. R would bring cutting-edge data mining and data visualization technologies at low cost (or no cost). Although the two packages dwell in distinctive ecosystems (for example: different OS/ETL/database/reporting layers) [Ref. 1], mixed programming by combining them together would make an analytics shop invincible.

Some SAS programmers like to use SAS/IML to call R’s functions [Ref. 2]. However, it seems that SAS/IML fails to work with the latest versions of R since 2.12 [Ref. 3]. Others tend to play tricks to call R into SAS’s data step to meet their daily needs [Ref. 4]. In this post, the macro below would call the ‘lattice’ package of R in SAS, on a PC platform, to draw paneled three dimension images, since currently SAS’s SG procedures don’t own such an option. The good thing is that there is no need to check the version of R installed before running it. And the modification of this macro can be extended to other applications to call R in SAS.

Reference:
1. ‘Keep an Eye on the emerging Open-Source Analytics Stack’. Revolution R Blog.
2. Zhengping Ma. ‘Data mining in SAS with open source software’. SAS Global 2011.
3. ‘SAS/IML incompatible with latest releases of R’. SAS-L. 11APR2011.
4. Liang Xie. ‘Regularized Discriminant Analysis’. www.sas-programming.com

/*******************READ ME*********************************************
* - A MACRO CALLS R IN SAS FOR PANELED 3D PLOTTING -
*
* VERSION: SAS 9.2(ts2m0), windows 64bit
* DATE: 25apr2011
* AUTHOR: hchao8@gmail.com
*
****************END OF READ ME*********************j********************/

****************(1) MODULE-BUILDING STEP******************;
%macro scatter3dpanel(data = , x = , y = , z = , factor = ,
width = , height = , outfile = );
/***********************************************************
* MACRO: scatter3dpanel()
* PARAMETERS: data = dataset for plotting
* x = x-axis variable
* y = y-axis variable
* z = z-axis variable
* factor = partition factor variable
* width = width of output graph
* height = height of output graph
* outfile= location of output image
***********************************************************/
proc export data = &data outfile = "d:\tmp.csv" replace;
run;

proc sql;
create table _tmp0 (string char(80));
insert into _tmp0
set string = 'tmp=read.csv("d:/tmp.csv", header=T)'
set string = 'attach(tmp)'
set string = 'library(lattice)'
set string = 'windows()'
set string = 'cloud(sas_zvar~sas_xvar+sas_yvar|as.factor(sas_factor), pretty=T)'
set string = 'dev.print(device=png, width=sas_width, height=sas_height, file="sas_file")';
quit;

data _tmp1;
set _tmp0;
string = tranwrd(string, "sas_xvar", propcase("&x"));
string = tranwrd(string, "sas_yvar", propcase("&y"));
string = tranwrd(string, "sas_zvar", propcase("&z"));
string = tranwrd(string, "sas_factor", propcase("&factor"));
string = tranwrd(string, "sas_width", "&width");
string = tranwrd(string, "sas_height", "&height");
string = tranwrd(string, "sas_file", translate("&outfile", "/", "\"));
run;

data _null_;
set _tmp1;
file "d:\callRinSAS.r";
put string;
run;

options noxsync noxwait;
x ' "d:\Program Files\R\R-2.12.1\bin\R.exe" CMD BATCH --vanilla --slave "d:\callRinSAS.r" ';
%mend;

****************(2) TESTING STEP******************;
%scatter3dpanel(data = sashelp.cars, x = length, y = wheelbase, z = horsepower,
factor = type, width = 1200, height = 600, outfile = d:\test1.png );

****************END OF ALL CODING***************************************;

Wednesday, April 14, 2010

Labeling variables by a macro in SAS

To rename the variables of a dataset in SAS is a daily routine. SAS or the programmer s would give an arbitrary name for any variable at the initial stage of data integration. Those names have to be modified afterward. Wensui [Ref.1] developed a macro to add prefixes to the variables . Vincent et al. [Ref. 2] extended his idea and added some parameters into the macros. However, giving a name to a variable in SAS has many restrictions regarding the length and the format. For better understanding and recognition, labeling variables instead of renaming them would be useful. In the example below, first comes with an integration of complicated text data. Proc Transpose generates a number of variables with the same prefix.  Then by invoking the label() macro, the dataset would be correctly labeled as desired.

References:
1. Wensui Liu. ‘How to rename many variables in SAS’. http://statcompute.blogspot.com/
2. Vincent Weng. Ying Feng. ‘Renaming in Batches’. SAS Global 2009.

****************(1) MODULE-BUILDING STEP******************;
%macro label(dsin = , dsout = , dslabel = );
/***********************************************************
* MACRO: label()
* GOAL: use a label dataset to label the variables
* of the target dataset
* PARAMETERS: dsin = input dataset
* dsout = output dataset
* dslabel = label dataset
*
***********************************************************/
data _tmp;
set &dslabel ;
num = _n_;
run;

ods listing close;
ods output variables = _varlist;
proc contents data = &dsin;
run;

proc sql;
select cats(a.variable, '="', b.labelname, '"')
into: labellist separated by ' '
from _varlist as a, _tmp as b
where a.num = b.num
;quit;

data &dsout;
set &dsin;
label &labellist;
run;

proc datasets;
delete _:;
quit;
ods listing;
%mend;

****************(2) TESTING STEP******************;
******(2.1) INTEGRATE COMPLICATED DATA*************;
data have;
infile datalines dlm = ',';
retain _row;
input _tmpvar $ @@ ;
if prxmatch("/10\d/", _tmpvar) ne 0 then _row + 1;
if missing(_tmpvar) then delete;
datalines;
100, Tom, 3,1,5,2,6
101, Marlene, 1,2,4
102, Jerry, 9,10,4,
5, 6
103, Jim,2 ,1, 2, 2,4
;
run;

proc transpose data=have out=want(drop = _:)
prefix = var;
by _row;
var _tmpvar;
run;

******(2.2) INPUT LABELS FOR USE*************;
data label;
input labelname $30.;
cards;
Patient ID
Patient last name
The 1st treatment
The 2nd treatment
The 3rd treatment
The 4th treatment
The 5th treatment
;
run;

******(2.3) INVOKE MACRO TO LABEL*************;
%label(dsin = want, dsout = want_labeled, dslabel = label);

****************END OF ALL CODING***************************************;

Monday, December 15, 2008

Vertical collapse by five methods

******************(1) INPUT STEP***********;
data have;
input id: $ string: $;
cards;
001 aaa
001 bbb
002 ccccc
002 dddd
002 eee
003 ffff
004 gggggg
;
run;

*******************(2) CONCATENATION STEP ***********;
***********(2.1) METHOD I: do-loop and substr()***********;
data want1(drop = string);
length newstring $50.;
do _n_ = 1 by 1 until(last.id);
set have;
by id notsorted;
substr(newstring,length(newstring) + 1) = string;
end;
run;

***********(2.2) METHOD II: Proc Transpose***********;
proc transpose data = have out = _tmp;
by id;
var string;
run;

data want2;
set _tmp;
newstring = cats(of col:);
drop _: col:;
run;

***********(2.3) METHOD III: retain statement***********;
data want3(drop = string);
set have;
by id notsorted;
length newstring $50.;
retain newstring ;
if first.id then newstring = string;
else newstring = cats(newstring, string);
if last.id;
run;

***********(2.4) METHOD IV: Hash table***********;
data _null_;
length newstring $50;
if _n_ =1 then do;
declare hash h();
h.defineKey('id');
h.defineData('id', 'newstring');
h.defineDone();
end;
set have end = eof ;
if h.find() ne 0 then do;
newstring = string;
h.add();
end;
else do;
newstring = cats(newstring, string);
h.replace();
end;
if eof then h.output(dataset: 'want4');
run;

***********(2.5) METHOD V: SQL and macro***********;
proc sql noprint;
select count(unique(id)) into: idnum
from have;
select distinct id into: allid separated by ', '
from have;
quit;

%macro concatenate();
%let id = scan("&allid", &i);
%do i = 1 %to &idnum;
proc sql noprint;
select string into: newstring separated by ''
from have
where id = &id;
quit;
%put &newstring;
%end;
%mend;
%concatenate();

*********************END OF ALL CODING******************************;
References:
1. Technique board. Mysas.net.