SAS:在 function/subroutine / return 数组中查找数据
SAS: lookup data inside a function/subroutine / return an array
假设我想做类似下面的事情(为了更好的可读性使用示例变量名):
取一个参数InParameter
并将其与数据集MyData
中的变量MyVar1
匹配
return 变量的所有值 MyVar2
过滤后的观测值
来自 subroutine/function
我可以在 proc 中使用 sql/datastep
这是我目前得到的结果(显然没有用):
proc fcmp outlib=work.funcs.MyFunction;
function MyFunction(InParameter $);
array MyArray ... ; /* Here: Create an array with something like SELECT MyVar2 FROM MyData WHERE MyVar1 = Inparameter */
return(MyArray{});
endsub;
;
quit;
options cmplib=work.funcs;
data MyOutput;
set Somedata;
if MyVar2 in MyFunction("H20") then output;
run;
简而言之:
- 可以从 function/subroutine 内部访问数据集中的数据吗?
- 可以 function/subroutine return 数组吗?
感谢您的帮助!
你最好用宏。
%macro subset(inParameter, indata, outdata);
proc sql noprint;
create table &outdata as
select * from &indata
where myVar2 in (select distinct myVar2 from myData where myVar1 = "&inParameter);
quit;
%mend;
%subst(H20,Somedata,MyOutput);
不确定某个函数是否适用于 IN
运算符。您可能需要用宏包装函数调用以生成正确的语法。在这种情况下,为什么不直接制作一个宏呢?
这是从数据集中的变量中提取值的通用宏。
%macro varlist
/*----------------------------------------------------------------------
Generate list of values from dataset
----------------------------------------------------------------------*/
(dataset /* Input dataset */
,variable /* Variable Name */
,quote=1 /* Add quotes around values? 1=Single 2=Double */
,comma=1 /* Add comma between values? */
,paren=1 /* Add parentheses around results? */
);
%local did sep &variable ;
%if &paren=1 %then (;
%let did=%sysfunc(open(&dataset));
%syscall set(did);
%do %while(0=%sysfunc(fetch(&did)));
%let &variable=%qsysfunc(trim(%superq(&variable)));
%if "e=1 %then &sep.%sysfunc(quote(&&&variable,%str(%')));
%else %if "e=2 %then &sep.%sysfunc(quote(&&&variable));
%else &sep.&&&variable;
%if &comma=1 %then %let sep=,;
%end;
%let did=%sysfunc(close(&did));
%if &paren=1 %then );
%mend varlist;
调用示例:
%put %varlist(sashelp.class,name);
%put %varlist(sashelp.class(where=(sex='M')),age,quote=0,comma=0);
所以在你的情况下你可以这样使用它:
data MyOutput;
set Somedata;
where MyVar2 in %varlist(Mydata(where=(MyVar1="H20")),MyVar2) ;
run;
我们创建了一个名为 %ds2list()
的实用程序宏,它将执行您想要的过程。它不使用数组语句,但它实现了相同的结果。
宏只是 return 列表格式的数据集中的值。这是调用它的示例:
%put %ds2list(iDs=sashelp.class, iField=name, iQuote=1);
这会 return:
'Alfred','Alice','Barbara','Carol','Henry','James','Jane','Janet','Jeffrey','John','Joyce','Judy','Louise','Mary','Philip','Robert','Ronald','Thomas','William'
%ds2list()
的默认行为是用逗号分隔 returned 值,但它非常灵活。您可以将分隔符更改为您选择的值(或没有分隔符),您可以打开或关闭引号,或将它们从单引号更改为双引号,并且您可以提供通常在 set
语句,例如 where=()
语句。
此外,因为该宏是纯宏代码,您可以在 SAS 中的任何地方使用它。在任何你喜欢的 proc/data/macro 中。当我们有大量要 returned 的 ID 时,我们会广泛使用它来调用 ODBC 直通。
这是一个如何使用它的示例。首先创建一个 table,其中将包含要与列表值进行比较的值:
data keep;
input name $;
datalines;
Alfred
Carol
Janet
run;
迭代我们要对照列表检查的值:
data want;
set keep;
if name in (%ds2list(iDs=sashelp.class, iField=name, iQuote=1, iDsOptions=where=(sex='F'))) then do;
output;
end;
run;
Returns:
Obs name
=== =====
1 Carol
2 Janet
您可以看到 Alfred 被排除在结果之外,因为他被 where=()
子句过滤掉了。
这是宏,我建议把它放在你的宏自动调用库中:
/***************************************************************************
** PROGRAM: MACRO.DS2LIST.SAS
**
** UTILITY PROGRAM THAT DETECTS RETURNS A LIST OF FIELD VALUES FROM A
** DATASET IN DELIMITED FORMAT.
**
** PARAMETERS:
** iDs : THE LIBNAME.DATASET NAME THAT YOU WANT TO CHECK.
** iField : THE FIELD THAT CONTAINS THE VALUES YOU WANT RETURNED IN A
** DELIMITED FORMAT.
** iDelimiter: DEFAULT IS A COMMA. THE DELIMITER TO USE FOR THE RETURNED LIST.
** iDsOptions: ANY STANDARD DATASET OPTIONS THAT YOU WOULD LIKE TO APPLY SUCH
** AS A WHERE STATEMENT.
** iQuote : (0=NO,1=YES). DEFAULT=0/NO. DETERMINES WHETHER THE RETURNED
** LIST IS QUOTED OR NOT.
** iQuoteChar: (SINGLE,DOUBLE) DEFAULT=SINGLE. SPECIFIES WHETHER SINGLE
** OR DOUBLE QUOTES ARE USED WHEN QUOTING THE RETURNED LIST
**
*****************************************************************************/
%macro ds2list(iDs=, iField=, iDsOptions=, iDelimiter=%str(,), iQuote=0, iQuoteChar=single);
%local dsid pos rc result cnt quotechar value;
%let result=;
%let cnt=0;
%if &iQuote %then %do;
%if "%upcase(&iQuoteChar)" eq "DOUBLE" %then %do;
%let quotechar = %nrstr(%");
%end;
%else %if "%upcase(&iQuoteChar)" eq "SINGLE" %then %do;
%let quotechar = %nrstr(%');
%end;
%else %do;
%let quotechar = %nrstr(%");
%put WARNING: MACRO.DS2LIST.SAS: PARAMETER IQUOTECHAR INCORRECT. DEFAULTED TO DOUBLE;
%end;
%end;
%else %do;
%let quotechar = ;
%end;
/*
** ENSURE ALL THE REQUIRED PARAMETERS WERE PASSED IN.
*/
%if "&iDs" ne "" and "&iField" ne "" %then %do;
%let dsid=%sysfunc(open(&iDs(&iDsOptions),i));
%if &dsid %then %do;
%let pos=%sysfunc(varnum(&dsid,&iField));
%if &pos %then %do;
%let rc=%sysfunc(fetch(&dsid));
%do %while (&rc eq 0);
%if "%sysfunc(vartype(&dsid,&pos))" = "C" %then %do;
%let value = %qsysfunc(getvarc(&dsid,&pos));
%if "%trim(&value)" ne "" %then %do;
%let value = %qtrim(&value);
%end;
%end;
%else %do;
%let value = %sysfunc(getvarn(&dsid,&pos));
%end;
/* WHITESPACE/CARRIAGE RETURNS REMOVED IN THE BELOW LINE */
/* TO ENSURE NO WHITESPACE IS RETURNED IN THE OUTPUT. */
%if &cnt ne 0 %then %do;%unquote(&iDelimiter)%end;%unquote("echar&value"echar.)
%let cnt = %eval(&cnt + 1);
%let rc = %sysfunc(fetch(&dsid));
%end;
%if &rc ne -1 %then %do;
%put WARNING: MACRO.DS2LIST.SAS: %sysfunc(sysmsg());
%end;
%end;
%else %do;
%put ERROR: MACRO.DS2LIST.SAS: FIELD &iField NOT FOUND IN DATASET %upcase(&iDs).;
%end;
%end;
%else %do;
%put ERROR: MACRO.DS2LIST.SAS: DATASET %upcase(&iDs) COULD NOT BE OPENED.;
%end;
%let rc=%sysfunc(close(&dsid));
%end;
%else %do;
%put ERROR: MACRO.DS2LIST.SAS: YOU MUST SPECIFY BOTH THE IDS AND IFIELD PARAMETERS TO CALL THIS MACRO.;
%end;
%mend;
假设我想做类似下面的事情(为了更好的可读性使用示例变量名):
取一个参数
InParameter
并将其与数据集MyData
中的变量return 变量的所有值
MyVar2
过滤后的观测值来自 subroutine/function
我可以在 proc 中使用 sql/datastep
MyVar1
匹配
这是我目前得到的结果(显然没有用):
proc fcmp outlib=work.funcs.MyFunction;
function MyFunction(InParameter $);
array MyArray ... ; /* Here: Create an array with something like SELECT MyVar2 FROM MyData WHERE MyVar1 = Inparameter */
return(MyArray{});
endsub;
;
quit;
options cmplib=work.funcs;
data MyOutput;
set Somedata;
if MyVar2 in MyFunction("H20") then output;
run;
简而言之:
- 可以从 function/subroutine 内部访问数据集中的数据吗?
- 可以 function/subroutine return 数组吗?
感谢您的帮助!
你最好用宏。
%macro subset(inParameter, indata, outdata);
proc sql noprint;
create table &outdata as
select * from &indata
where myVar2 in (select distinct myVar2 from myData where myVar1 = "&inParameter);
quit;
%mend;
%subst(H20,Somedata,MyOutput);
不确定某个函数是否适用于 IN
运算符。您可能需要用宏包装函数调用以生成正确的语法。在这种情况下,为什么不直接制作一个宏呢?
这是从数据集中的变量中提取值的通用宏。
%macro varlist
/*----------------------------------------------------------------------
Generate list of values from dataset
----------------------------------------------------------------------*/
(dataset /* Input dataset */
,variable /* Variable Name */
,quote=1 /* Add quotes around values? 1=Single 2=Double */
,comma=1 /* Add comma between values? */
,paren=1 /* Add parentheses around results? */
);
%local did sep &variable ;
%if &paren=1 %then (;
%let did=%sysfunc(open(&dataset));
%syscall set(did);
%do %while(0=%sysfunc(fetch(&did)));
%let &variable=%qsysfunc(trim(%superq(&variable)));
%if "e=1 %then &sep.%sysfunc(quote(&&&variable,%str(%')));
%else %if "e=2 %then &sep.%sysfunc(quote(&&&variable));
%else &sep.&&&variable;
%if &comma=1 %then %let sep=,;
%end;
%let did=%sysfunc(close(&did));
%if &paren=1 %then );
%mend varlist;
调用示例:
%put %varlist(sashelp.class,name);
%put %varlist(sashelp.class(where=(sex='M')),age,quote=0,comma=0);
所以在你的情况下你可以这样使用它:
data MyOutput;
set Somedata;
where MyVar2 in %varlist(Mydata(where=(MyVar1="H20")),MyVar2) ;
run;
我们创建了一个名为 %ds2list()
的实用程序宏,它将执行您想要的过程。它不使用数组语句,但它实现了相同的结果。
宏只是 return 列表格式的数据集中的值。这是调用它的示例:
%put %ds2list(iDs=sashelp.class, iField=name, iQuote=1);
这会 return:
'Alfred','Alice','Barbara','Carol','Henry','James','Jane','Janet','Jeffrey','John','Joyce','Judy','Louise','Mary','Philip','Robert','Ronald','Thomas','William'
%ds2list()
的默认行为是用逗号分隔 returned 值,但它非常灵活。您可以将分隔符更改为您选择的值(或没有分隔符),您可以打开或关闭引号,或将它们从单引号更改为双引号,并且您可以提供通常在 set
语句,例如 where=()
语句。
此外,因为该宏是纯宏代码,您可以在 SAS 中的任何地方使用它。在任何你喜欢的 proc/data/macro 中。当我们有大量要 returned 的 ID 时,我们会广泛使用它来调用 ODBC 直通。
这是一个如何使用它的示例。首先创建一个 table,其中将包含要与列表值进行比较的值:
data keep;
input name $;
datalines;
Alfred
Carol
Janet
run;
迭代我们要对照列表检查的值:
data want;
set keep;
if name in (%ds2list(iDs=sashelp.class, iField=name, iQuote=1, iDsOptions=where=(sex='F'))) then do;
output;
end;
run;
Returns:
Obs name
=== =====
1 Carol
2 Janet
您可以看到 Alfred 被排除在结果之外,因为他被 where=()
子句过滤掉了。
这是宏,我建议把它放在你的宏自动调用库中:
/***************************************************************************
** PROGRAM: MACRO.DS2LIST.SAS
**
** UTILITY PROGRAM THAT DETECTS RETURNS A LIST OF FIELD VALUES FROM A
** DATASET IN DELIMITED FORMAT.
**
** PARAMETERS:
** iDs : THE LIBNAME.DATASET NAME THAT YOU WANT TO CHECK.
** iField : THE FIELD THAT CONTAINS THE VALUES YOU WANT RETURNED IN A
** DELIMITED FORMAT.
** iDelimiter: DEFAULT IS A COMMA. THE DELIMITER TO USE FOR THE RETURNED LIST.
** iDsOptions: ANY STANDARD DATASET OPTIONS THAT YOU WOULD LIKE TO APPLY SUCH
** AS A WHERE STATEMENT.
** iQuote : (0=NO,1=YES). DEFAULT=0/NO. DETERMINES WHETHER THE RETURNED
** LIST IS QUOTED OR NOT.
** iQuoteChar: (SINGLE,DOUBLE) DEFAULT=SINGLE. SPECIFIES WHETHER SINGLE
** OR DOUBLE QUOTES ARE USED WHEN QUOTING THE RETURNED LIST
**
*****************************************************************************/
%macro ds2list(iDs=, iField=, iDsOptions=, iDelimiter=%str(,), iQuote=0, iQuoteChar=single);
%local dsid pos rc result cnt quotechar value;
%let result=;
%let cnt=0;
%if &iQuote %then %do;
%if "%upcase(&iQuoteChar)" eq "DOUBLE" %then %do;
%let quotechar = %nrstr(%");
%end;
%else %if "%upcase(&iQuoteChar)" eq "SINGLE" %then %do;
%let quotechar = %nrstr(%');
%end;
%else %do;
%let quotechar = %nrstr(%");
%put WARNING: MACRO.DS2LIST.SAS: PARAMETER IQUOTECHAR INCORRECT. DEFAULTED TO DOUBLE;
%end;
%end;
%else %do;
%let quotechar = ;
%end;
/*
** ENSURE ALL THE REQUIRED PARAMETERS WERE PASSED IN.
*/
%if "&iDs" ne "" and "&iField" ne "" %then %do;
%let dsid=%sysfunc(open(&iDs(&iDsOptions),i));
%if &dsid %then %do;
%let pos=%sysfunc(varnum(&dsid,&iField));
%if &pos %then %do;
%let rc=%sysfunc(fetch(&dsid));
%do %while (&rc eq 0);
%if "%sysfunc(vartype(&dsid,&pos))" = "C" %then %do;
%let value = %qsysfunc(getvarc(&dsid,&pos));
%if "%trim(&value)" ne "" %then %do;
%let value = %qtrim(&value);
%end;
%end;
%else %do;
%let value = %sysfunc(getvarn(&dsid,&pos));
%end;
/* WHITESPACE/CARRIAGE RETURNS REMOVED IN THE BELOW LINE */
/* TO ENSURE NO WHITESPACE IS RETURNED IN THE OUTPUT. */
%if &cnt ne 0 %then %do;%unquote(&iDelimiter)%end;%unquote("echar&value"echar.)
%let cnt = %eval(&cnt + 1);
%let rc = %sysfunc(fetch(&dsid));
%end;
%if &rc ne -1 %then %do;
%put WARNING: MACRO.DS2LIST.SAS: %sysfunc(sysmsg());
%end;
%end;
%else %do;
%put ERROR: MACRO.DS2LIST.SAS: FIELD &iField NOT FOUND IN DATASET %upcase(&iDs).;
%end;
%end;
%else %do;
%put ERROR: MACRO.DS2LIST.SAS: DATASET %upcase(&iDs) COULD NOT BE OPENED.;
%end;
%let rc=%sysfunc(close(&dsid));
%end;
%else %do;
%put ERROR: MACRO.DS2LIST.SAS: YOU MUST SPECIFY BOTH THE IDS AND IFIELD PARAMETERS TO CALL THIS MACRO.;
%end;
%mend;