SAS:在 function/subroutine / return 数组中查找数据

SAS: lookup data inside a function/subroutine / return an array

假设我想做类似下面的事情(为了更好的可读性使用示例变量名):

  1. 取一个参数InParameter并将其与数据集MyData

  2. 中的变量MyVar1匹配
  3. return 变量的所有值 MyVar2 过滤后的观测值

  4. 来自 subroutine/function

  5. 我可以在 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;

简而言之:

感谢您的帮助!

你最好用宏。

%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 &quote=1 %then &sep.%sysfunc(quote(&&&variable,%str(%')));
  %else %if &quote=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(&quotechar&value&quotechar.)

          %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;