1
0
mirror of https://github.com/sasjs/core.git synced 2026-01-08 10:00:04 +00:00

Compare commits

..

3 Commits

Author SHA1 Message Date
Allan Bowe
4c333ae7b3 feat: mp_prevobs.sas macro 2020-12-23 00:33:59 +00:00
b3a8b4323e fix: adding new mp_ds2csv macro 2020-12-16 17:11:31 +01:00
0592206f2d patch: doxy formatting 2020-12-03 22:44:08 +01:00
10 changed files with 393 additions and 15 deletions

230
all.sas
View File

@@ -39,8 +39,8 @@ options noquotelenmax;
@version 9.2 @version 9.2
@author Allan Bowe @author Allan Bowe
@cond
**/ **/
/** @cond */
%macro mf_abort(mac=mf_abort.sas, type=, msg=, iftrue=%str(1=1) %macro mf_abort(mac=mf_abort.sas, type=, msg=, iftrue=%str(1=1)
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;
@@ -278,6 +278,7 @@ options noquotelenmax;
@version 9.2 @version 9.2
@author Allan Bowe @author Allan Bowe
@cond
**/ **/
%macro mf_existvarlist(libds, varlist %macro mf_existvarlist(libds, varlist
@@ -317,7 +318,9 @@ options noquotelenmax;
0 0
%put Vars not found: &found; %put Vars not found: &found;
%end; %end;
%mend;/** %mend;
/** @endcond *//**
@file @file
@brief Returns a character attribute of a dataset. @brief Returns a character attribute of a dataset.
@details Can be used in open code, eg as follows: @details Can be used in open code, eg as follows:
@@ -633,6 +636,7 @@ options noquotelenmax;
@version 9.2 @version 9.2
@author Allan Bowe @author Allan Bowe
@cond
**/ **/
%macro mf_getschema(libref %macro mf_getschema(libref
@@ -654,6 +658,8 @@ options noquotelenmax;
&schema &schema
%mend; %mend;
/** @endcond */
/** /**
@file @file
@brief Assigns and returns an unused fileref @brief Assigns and returns an unused fileref
@@ -1529,11 +1535,11 @@ Usage:
the particulars of an environment. For instance, can stream custom the particulars of an environment. For instance, can stream custom
results back to the client in an STP Web App context, or completely stop results back to the client in an STP Web App context, or completely stop
in the case of a batch run. in the case of a batch run.
Using SAS Abort Cancel mechanisms can cause hung sessions in some Stored Process Using SAS Abort Cancel mechanisms can cause hung sessions in some Stored Process
environments. This macro takes a unique approach - we set the SAS syscc to 0, environments. This macro takes a unique approach - we set the SAS syscc to 0,
run `stpsrvset('program error', 0)` (if SAS 9) and then - we open a macro run `stpsrvset('program error', 0)` (if SAS 9) and then - we open a macro
but don't close it! This provides a graceful abort for SAS web services in all but don't close it! This provides a graceful abort for SAS web services in all
web enabled environments. web enabled environments.
@param mac= to contain the name of the calling macro @param mac= to contain the name of the calling macro
@@ -1542,6 +1548,7 @@ Usage:
@version 9.4M3 @version 9.4M3
@author Allan Bowe @author Allan Bowe
@cond
**/ **/
%macro mp_abort(mac=mp_abort.sas, type=, msg=, iftrue=%str(1=1) %macro mp_abort(mac=mp_abort.sas, type=, msg=, iftrue=%str(1=1)
@@ -1554,7 +1561,7 @@ Usage:
%put NOTE - &msg; %put NOTE - &msg;
/* Stored Process Server web app context */ /* Stored Process Server web app context */
%if %symexist(_metaperson) %if %symexist(_metaperson)
or (%symexist(SYSPROCESSNAME) and "&SYSPROCESSNAME"="Compute Server" ) or (%symexist(SYSPROCESSNAME) and "&SYSPROCESSNAME"="Compute Server" )
%then %do; %then %do;
options obs=max replace nosyntaxcheck mprint; options obs=max replace nosyntaxcheck mprint;
@@ -1675,7 +1682,7 @@ Usage:
%end; %end;
%mend; %mend;
/** /** @endcond *//**
@file @file
@brief Copy any file using binary input / output streams @brief Copy any file using binary input / output streams
@details Reads in a file byte by byte and writes it back out. Is an @details Reads in a file byte by byte and writes it back out. Is an
@@ -1739,6 +1746,7 @@ Usage:
applying CRLF line endings and converting embedded cr and crlf to lf. applying CRLF line endings and converting embedded cr and crlf to lf.
usage: usage:
fileref mycsv "/path/your/csv"; fileref mycsv "/path/your/csv";
%mp_cleancsv(in=mycsv,out=/path/new.csv) %mp_cleancsv(in=mycsv,out=/path/new.csv)
@@ -1748,6 +1756,7 @@ Usage:
@version 9.2 @version 9.2
@author Allan Bowe @author Allan Bowe
@cond
**/ **/
%macro mp_cleancsv(in=NOTPROVIDED,out=NOTPROVIDED,qchar='22'x); %macro mp_cleancsv(in=NOTPROVIDED,out=NOTPROVIDED,qchar='22'x);
@@ -1796,7 +1805,8 @@ Usage:
else put inchar $char1.; else put inchar $char1.;
end; end;
run; run;
%mend;/** %mend;
/** @endcond *//**
@file mp_createconstraints.sas @file mp_createconstraints.sas
@brief Creates constraints @brief Creates constraints
@details Takes the output from mp_getconstraints.sas as input @details Takes the output from mp_getconstraints.sas as input
@@ -2635,6 +2645,63 @@ quit;
%put NOTE-;%put NOTE-; %put NOTE-;%put NOTE-;
%put NOTE- %sysfunc(dequote(&cards_file.)); %put NOTE- %sysfunc(dequote(&cards_file.));
%put NOTE-;%put NOTE-; %put NOTE-;%put NOTE-;
%mend;/**
@file
@brief Export a dataset to a CSV file
@details Export to a file or a fileref
Usage:
%mp_ds2csv(sashelp.class,outref="%sysfunc(pathname(work))/file.csv")
@param ds The dataset to be exported
@param outfile= The output filename - should be quoted.
@param outref= The output fileref (takes precedence if provided)
@param outencoding= The output encoding to use (unquoted)
@version 9.2
@author Allan Bowe (credit mjsq)
**/
%macro mp_ds2csv(ds, outref=0, outfile=, outencoding=0
)/*/STORE SOURCE*/;
%if not %sysfunc(exist(&ds)) %then %do;
%put WARNING: &ds does not exist;
%return;
%end;
%if %index(&ds,.)=0 %then %let ds=WORK.&ds;
%if &outencoding=0 %then %let outencoding=;
%else %let outencoding=encoding="&outencoding";
%local outloc;
%if &outref=0 %then %let outloc=&outfile;
%else %let outloc=&outref;
/* credit to mjsq - https://stackoverflow.com/a/55642267 */
/* first get headers */
data _null_;
file &outloc dlm=',' dsd &outencoding lrecl=32767;
length header $ 2000;
dsid=open("&ds.","i");
num=attrn(dsid,"nvars");
do i=1 to num;
header = trim(left(coalescec(varlabel(dsid,i),varname(dsid,i))));
put header @;
end;
rc=close(dsid);
run;
/* next, export data */
data _null_;
set &ds.;
file &outloc mod dlm=',' dsd &outencoding lrecl=32767;
put (_all_) (+0);
run;
%mend;/** %mend;/**
@file mp_getconstraints.sas @file mp_getconstraints.sas
@brief Get constraint details at column level @brief Get constraint details at column level
@@ -2712,6 +2779,8 @@ create table &outds as
You may need to adjust the rendered DBML to suit your needs. You may need to adjust the rendered DBML to suit your needs.
![dbml for sas](https://i.imgur.com/8T1tIZp.gif)
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_getquotedstr.sas @li mf_getquotedstr.sas
@@ -3997,6 +4066,93 @@ select distinct lowcase(memname)
,dttm=%sysfunc(datetime()); ,dttm=%sysfunc(datetime());
quit; quit;
%mend;/**
@file
@brief Enables previous observations to be re-instated
@details Remembers the last X observations by storing them in a hash table.
Is a convenience over the use of lag() or retain, when an entire observation
needs to be restored.
This macro will also restore automatic variables (such as _n_ and _error_).
Example Usage:
data example;
set sashelp.class;
calc_var=_n_*3;
* initialise hash and save from PDV ;
%mp_prevobs(INIT,history=2)
if _n_ =10 then do;
* fetch previous but 1 record;
%mp_prevobs(FETCH,-2)
put _n_= name= age= calc_var=;
* fetch previous record;
%mp_prevobs(FETCH,-1)
put _n_= name= age= calc_var=;
* reinstate current record ;
%mp_prevobs(FETCH,0)
put _n_= name= age= calc_var=;
end;
run;
Result:
<img src="https://imgur.com/PSjHoET.png" alt="mp_prevobs sas" width="400"/>
Credit is made to `data _null_` for authoring this very helpful paper:
https://www.lexjansen.com/pharmasug/2008/cc/CC08.pdf
@param action Either FETCH a current or previous record, or INITialise.
@param record The relative (to current) position of the previous observation
to return.
@param history= The number of records to retain in the hash table. Default=5
@param prefix= the prefix to give to the variables used to store the hash name
and index. Default=mp_prevobs
@version 9.2
@author Allan Bowe
**/
%macro mp_prevobs(action,record,history=5,prefix=mp_prevobs
)/*/STORE SOURCE*/;
%let action=%upcase(&action);
%let prefix=%upcase(&prefix);
%let record=%eval((&record+0) * -1);
%if &action=INIT %then %do;
if _n_ eq 1 then do;
attrib &prefix._VAR length=$64;
dcl hash &prefix._HASH(ordered:'Y');
&prefix._KEY=0;
&prefix._HASH.defineKey("&prefix._KEY");
do while(1);
call vnext(&prefix._VAR);
if &prefix._VAR='' then leave;
if &prefix._VAR eq "&prefix._VAR" then continue;
else if &prefix._VAR eq "&prefix._KEY" then continue;
&prefix._HASH.defineData(&prefix._VAR);
end;
&prefix._HASH.defineDone();
end;
/* this part has to happen before FETCHing */
&prefix._KEY+1;
&prefix._rc=&prefix._HASH.add();
if &prefix._rc then putlog 'adding' &prefix._rc=;
%if &history>0 %then %do;
if &prefix._key>&history+1 then
&prefix._HASH.remove(key: &prefix._KEY - &history - 1);
if &prefix._rc then putlog 'removing' &prefix._rc=;
%end;
%end;
%else %if &action=FETCH %then %do;
if &record > &prefix._key then putlog "Not enough records in &Prefix._hash yet";
else &prefix._rc=&prefix._HASH.find(key: &prefix._KEY - &record);
if &prefix._rc then putlog &prefix._rc= " when fetching " &prefix._KEY=
"with record &record and " _n_=;
%end;
%mend;/** %mend;/**
@file @file
@brief Returns all children from a hierarchy table for a specified parent @brief Returns all children from a hierarchy table for a specified parent
@@ -4598,6 +4754,64 @@ proc sql
%mp_binarycopy(inloc="&inloc",outref=_webout) %mp_binarycopy(inloc="&inloc",outref=_webout)
%end; %end;
%mend;/**
@file mp_testwritespeedlibrary.sas
@brief Tests the write speed of a new table in a SAS library
@details Will create a new table of a certain size in an
existing SAS library. The table will have one column,
and will be subsequently deleted.
%mp_testwritespeedlibrary(
lib=work
,size=0.5
,outds=work.results
)
@param lib= (WORK) The library in which to create the table
@param size= (0.1) The size in GB of the table to create
@param outds= (WORK.RESULTS) The output dataset to be created.
<h4> Dependencies </h4>
@li mf_getuniquename.sas
@li mf_existds.sas
@version 9.4
@author Allan Bowe
**/
%macro mp_testwritespeedlibrary(lib=WORK
,outds=work.results
,size=0.1
)/*/STORE SOURCE*/;
%local ds start;
/* find an unused, unique name for the new table */
%let ds=%mf_getuniquename();
%do %until(%mf_existds(&lib..&ds)=0);
%let ds=%mf_getuniquename();
%end;
%let start=%sysfunc(datetime());
data &lib..&ds(compress=no keep=x);
header=128*1024;
size=(1073741824/8 * &size) - header;
do x=1 to size;
output;
end;
run;
proc sql;
drop table &lib..&ds;
data &outds;
lib="&lib";
start_dttm=put(&start,datetime19.);
end_dttm=put(datetime(),datetime19.);
duration_seconds=end_dttm-start_dttm;
run;
%mend;/** %mend;/**
@file @file
@brief Recursively scans a directory tree to get all subfolders and content @brief Recursively scans a directory tree to get all subfolders and content

View File

@@ -21,8 +21,8 @@
@version 9.2 @version 9.2
@author Allan Bowe @author Allan Bowe
@cond
**/ **/
/** @cond */
%macro mf_abort(mac=mf_abort.sas, type=, msg=, iftrue=%str(1=1) %macro mf_abort(mac=mf_abort.sas, type=, msg=, iftrue=%str(1=1)
)/*/STORE SOURCE*/; )/*/STORE SOURCE*/;

View File

@@ -14,6 +14,7 @@
@version 9.2 @version 9.2
@author Allan Bowe @author Allan Bowe
@cond
**/ **/
%macro mf_existvarlist(libds, varlist %macro mf_existvarlist(libds, varlist
@@ -53,4 +54,6 @@
0 0
%put Vars not found: &found; %put Vars not found: &found;
%end; %end;
%mend; %mend;
/** @endcond */

View File

@@ -17,6 +17,7 @@
@version 9.2 @version 9.2
@author Allan Bowe @author Allan Bowe
@cond
**/ **/
%macro mf_getschema(libref %macro mf_getschema(libref
@@ -38,3 +39,5 @@
&schema &schema
%mend; %mend;
/** @endcond */

View File

@@ -5,11 +5,11 @@
the particulars of an environment. For instance, can stream custom the particulars of an environment. For instance, can stream custom
results back to the client in an STP Web App context, or completely stop results back to the client in an STP Web App context, or completely stop
in the case of a batch run. in the case of a batch run.
Using SAS Abort Cancel mechanisms can cause hung sessions in some Stored Process Using SAS Abort Cancel mechanisms can cause hung sessions in some Stored Process
environments. This macro takes a unique approach - we set the SAS syscc to 0, environments. This macro takes a unique approach - we set the SAS syscc to 0,
run `stpsrvset('program error', 0)` (if SAS 9) and then - we open a macro run `stpsrvset('program error', 0)` (if SAS 9) and then - we open a macro
but don't close it! This provides a graceful abort for SAS web services in all but don't close it! This provides a graceful abort for SAS web services in all
web enabled environments. web enabled environments.
@param mac= to contain the name of the calling macro @param mac= to contain the name of the calling macro
@@ -18,6 +18,7 @@
@version 9.4M3 @version 9.4M3
@author Allan Bowe @author Allan Bowe
@cond
**/ **/
%macro mp_abort(mac=mp_abort.sas, type=, msg=, iftrue=%str(1=1) %macro mp_abort(mac=mp_abort.sas, type=, msg=, iftrue=%str(1=1)
@@ -30,7 +31,7 @@
%put NOTE - &msg; %put NOTE - &msg;
/* Stored Process Server web app context */ /* Stored Process Server web app context */
%if %symexist(_metaperson) %if %symexist(_metaperson)
or (%symexist(SYSPROCESSNAME) and "&SYSPROCESSNAME"="Compute Server" ) or (%symexist(SYSPROCESSNAME) and "&SYSPROCESSNAME"="Compute Server" )
%then %do; %then %do;
options obs=max replace nosyntaxcheck mprint; options obs=max replace nosyntaxcheck mprint;
@@ -151,3 +152,4 @@
%end; %end;
%mend; %mend;
/** @endcond */

View File

@@ -8,6 +8,7 @@
applying CRLF line endings and converting embedded cr and crlf to lf. applying CRLF line endings and converting embedded cr and crlf to lf.
usage: usage:
fileref mycsv "/path/your/csv"; fileref mycsv "/path/your/csv";
%mp_cleancsv(in=mycsv,out=/path/new.csv) %mp_cleancsv(in=mycsv,out=/path/new.csv)
@@ -17,6 +18,7 @@
@version 9.2 @version 9.2
@author Allan Bowe @author Allan Bowe
@cond
**/ **/
%macro mp_cleancsv(in=NOTPROVIDED,out=NOTPROVIDED,qchar='22'x); %macro mp_cleancsv(in=NOTPROVIDED,out=NOTPROVIDED,qchar='22'x);
@@ -65,4 +67,5 @@
else put inchar $char1.; else put inchar $char1.;
end; end;
run; run;
%mend; %mend;
/** @endcond */

58
base/mp_ds2csv.sas Normal file
View File

@@ -0,0 +1,58 @@
/**
@file
@brief Export a dataset to a CSV file
@details Export to a file or a fileref
Usage:
%mp_ds2csv(sashelp.class,outref="%sysfunc(pathname(work))/file.csv")
@param ds The dataset to be exported
@param outfile= The output filename - should be quoted.
@param outref= The output fileref (takes precedence if provided)
@param outencoding= The output encoding to use (unquoted)
@version 9.2
@author Allan Bowe (credit mjsq)
**/
%macro mp_ds2csv(ds, outref=0, outfile=, outencoding=0
)/*/STORE SOURCE*/;
%if not %sysfunc(exist(&ds)) %then %do;
%put WARNING: &ds does not exist;
%return;
%end;
%if %index(&ds,.)=0 %then %let ds=WORK.&ds;
%if &outencoding=0 %then %let outencoding=;
%else %let outencoding=encoding="&outencoding";
%local outloc;
%if &outref=0 %then %let outloc=&outfile;
%else %let outloc=&outref;
/* credit to mjsq - https://stackoverflow.com/a/55642267 */
/* first get headers */
data _null_;
file &outloc dlm=',' dsd &outencoding lrecl=32767;
length header $ 2000;
dsid=open("&ds.","i");
num=attrn(dsid,"nvars");
do i=1 to num;
header = trim(left(coalescec(varlabel(dsid,i),varname(dsid,i))));
put header @;
end;
rc=close(dsid);
run;
/* next, export data */
data _null_;
set &ds.;
file &outloc mod dlm=',' dsd &outencoding lrecl=32767;
put (_all_) (+0);
run;
%mend;

View File

@@ -16,6 +16,8 @@
You may need to adjust the rendered DBML to suit your needs. You may need to adjust the rendered DBML to suit your needs.
![dbml for sas](https://i.imgur.com/8T1tIZp.gif)
<h4> SAS Macros </h4> <h4> SAS Macros </h4>
@li mf_getquotedstr.sas @li mf_getquotedstr.sas

88
base/mp_prevobs.sas Normal file
View File

@@ -0,0 +1,88 @@
/**
@file
@brief Enables previous observations to be re-instated
@details Remembers the last X observations by storing them in a hash table.
Is a convenience over the use of lag() or retain, when an entire observation
needs to be restored.
This macro will also restore automatic variables (such as _n_ and _error_).
Example Usage:
data example;
set sashelp.class;
calc_var=_n_*3;
* initialise hash and save from PDV ;
%mp_prevobs(INIT,history=2)
if _n_ =10 then do;
* fetch previous but 1 record;
%mp_prevobs(FETCH,-2)
put _n_= name= age= calc_var=;
* fetch previous record;
%mp_prevobs(FETCH,-1)
put _n_= name= age= calc_var=;
* reinstate current record ;
%mp_prevobs(FETCH,0)
put _n_= name= age= calc_var=;
end;
run;
Result:
<img src="https://imgur.com/PSjHoET.png" alt="mp_prevobs sas" width="400"/>
Credit is made to `data _null_` for authoring this very helpful paper:
https://www.lexjansen.com/pharmasug/2008/cc/CC08.pdf
@param action Either FETCH a current or previous record, or INITialise.
@param record The relative (to current) position of the previous observation
to return.
@param history= The number of records to retain in the hash table. Default=5
@param prefix= the prefix to give to the variables used to store the hash name
and index. Default=mp_prevobs
@version 9.2
@author Allan Bowe
**/
%macro mp_prevobs(action,record,history=5,prefix=mp_prevobs
)/*/STORE SOURCE*/;
%let action=%upcase(&action);
%let prefix=%upcase(&prefix);
%let record=%eval((&record+0) * -1);
%if &action=INIT %then %do;
if _n_ eq 1 then do;
attrib &prefix._VAR length=$64;
dcl hash &prefix._HASH(ordered:'Y');
&prefix._KEY=0;
&prefix._HASH.defineKey("&prefix._KEY");
do while(1);
call vnext(&prefix._VAR);
if &prefix._VAR='' then leave;
if &prefix._VAR eq "&prefix._VAR" then continue;
else if &prefix._VAR eq "&prefix._KEY" then continue;
&prefix._HASH.defineData(&prefix._VAR);
end;
&prefix._HASH.defineDone();
end;
/* this part has to happen before FETCHing */
&prefix._KEY+1;
&prefix._rc=&prefix._HASH.add();
if &prefix._rc then putlog 'adding' &prefix._rc=;
%if &history>0 %then %do;
if &prefix._key>&history+1 then
&prefix._HASH.remove(key: &prefix._KEY - &history - 1);
if &prefix._rc then putlog 'removing' &prefix._rc=;
%end;
%end;
%else %if &action=FETCH %then %do;
if &record > &prefix._key then putlog "Not enough records in &Prefix._hash yet";
else &prefix._rc=&prefix._HASH.find(key: &prefix._KEY - &record);
if &prefix._rc then putlog &prefix._rc= " when fetching " &prefix._KEY=
"with record &record and " _n_=;
%end;
%mend;

View File

@@ -13,6 +13,11 @@
* Not metadata aware * Not metadata aware
* No X command * No X command
* Prefixes: _mf_, _mp_ * Prefixes: _mf_, _mp_
Macros starting `mf_` are macro _functions_ and can be used in assignment
statements. Those starting `mp_` are macro _procedures_, which generate
SAS statements, and must therefore be applied accordingly.
*/ */
/*! \dir meta /*! \dir meta