带有 PROC SQL 的 SAS 中的函数样式宏?
function style macro in SAS with PROC SQL?
是否可以使这种形式的宏起作用?
%macro tableMath(input1,input2);
%local result;
proc sql; ---some code here using inputs--- quit;
proc sql; ---more code here--- quit;
proc sql;
select something into: result
quit;
&result
%mend;
我想 运行 对数据集的每次观察进行一些相当复杂的逻辑,并且在我之前使用过的任何其他语言中,这样做的方法是将它封装在一个函数中 returns 每次被调用时都是一个结果——但是我不确定如何在 SAS 中执行此逻辑。
编辑:input1 和 input2 将是数据集的列,结果将用于在程序另一部分的某个其他宏中创建新列。我不需要特定的代码解决方案我只是不明白你应该如何在 SAS 中需要 return 值的地方执行传统函数逻辑...
你说你想:
run some fairly complicated logic on each observation of a dataset
为此,您应该使用 SAS 语言而不是宏处理器或 PROC SQL。您可以使用数据步骤。或者对于更复杂的逻辑,您应该查看 PROC DS2。
宏函数没有 return 值。一个宏函数可以'emit'源代码
- 这是一个或多个步骤,
- 那是代码被合并到语句中的片段,
- 这是一个或多个步骤的一部分的语句,
- 等等
对于 'do' SQL 中的内容的情况,您可以编写 SQL views 然后
- 以
%sysfunc(open())
和 打开
- 已处理
%sysfunc(set())
和
%sysfunc(getvarn())
和
%sysfunc(getvarc())
。
并非所有 SQL 功能都可以通过此技术使用 -- select something into :result
必须是具有 select something
的视图,而宏将 getvarc
用于读取结果。
以 open/set/get 方式完成的访问不会导致出现步骤边界,因此宏处理可以继续其逻辑并最终发出源代码以供片段级别使用。 (消费者是处理宏代码的SAS执行者,隐式编译运行SAS步骤)
正如 Richard 所写,函数式宏发出 SAS 代码。开发函数式宏的一般规则是它们只包含宏语言语句。将发出它们包含的任何 SAS 代码。从历史上看,这使得 difficult/annoying 编写函数式宏来像使用 DATA 步一样处理数据。幸运的是,SAS 添加了一个函数 DOSUBL,这使得编写在 "side session" 中执行 SAS 代码并发出结果的函数式宏变得更加容易。参见 Rick Langston's paper。
这是一个函数式宏的示例,它使用 DOSUBL 计算 table 中的记录数并发出计数。 (这是一种非常低效的获取记录计数的方法,只是在SQL中做某事的一个例子)。
%macro SQLcount(table);
%local rc emit;
%let rc=%sysfunc(dosubl(%nrstr(
proc sql noprint;
select count(*) into :emit trimmed
from &table
quit;
)));
&emit
%mend ;
它可以像这样使用:
proc sql ;
select name
,%SQLcount(sashelp.shoes) as ShoeCount /*emits 395*/
from sashelp.class
;
quit ;
以上步骤运行时,会return19行来自sashelp.class的名字,每行ShoeCount的值为395。注意宏 SQLcount 只执行了一次。当 PROC SQL 步骤正在 compiled/interpreted 时,可以看到对 SQLcount 的调用,并且执行宏并发出 395。该步骤变为:
proc sql ;
select name
,395 as ShoeCount /*emits 395*/
from sashelp.class
;
quit ;
DOSUBL 使用 "side session" 来执行代码,这允许您在主会话解释 PROC SQL 步骤时在副会话中执行 PROC SQL 步骤。
我无法从你的问题中判断出那种用例是否是你想要的。您可能需要一个函数样式的宏,您可以在其中将值从 table 传递给它,并让宏在每个值和 return 上执行。假设您有一个 table,它是 table 名称的列表,并且想使用 SQL 来获取每个 table:
中的记录数
data mytables ;
input table . ;
cards ;
sashelp.shoes
sashelp.class
sashelp.prdsale
;
quit ;
您可以通过使用 resolve() 函数从数据构建宏调用,延迟宏的执行直到 SELECT 语句执行:
proc sql ;
select table
,resolve('%SQLcount('||table||')') as count
from mytables
;
quit ;
这样,SQLcount 将被调用 3 次,并将 return 每个数据集中的记录数。
table count
---------------------------
sashelp.shoes 395
sashelp.class 19
sashelp.prdsale 1440
解释 PROC SQL 步骤时看不到宏调用,因为它被单引号隐藏了。解析函数然后在 SELECT 语句执行时调用宏,将 table
的值作为参数值传递,宏发出记录计数。这类似于使用数据驱动宏调用的 CALL EXECUTE 方法。
听起来您可能想使用 proc fcmp
创建 FCMP 函数。这基本上是一种创建您自己的 SAS 函数的方法,可以在 proc sql
和 data
步骤中使用。例如:
/******************************************************************************
** PROGRAM: COMMON.FCMP_DIV.SAS
**
** DESCRIPTION: PERFORMS A MATHEMATICAL DIVISION BUT WILL RETURN NULL IF THE
** NUMERATOR OR DENOMINATOR IS MISSING (OR IF THE DIVISOR IS 0).
**
******************************************************************************/
proc fcmp outlib=common.funcs.funcs;
function div(numerator, denominator);
if numerator eq . or denominator in (0,.) then do;
return(.);
end;
else do;
return(numerator / denominator);
end;
endsub;
run;
示例用法(示例是数据步骤,但在 SQL 中同样有效):
data x;
x1 = div(1,0);
x2 = div(1,.);
x3 = div(1,1);
x4 = div(0,0);
x5 = div(0,.);
x6 = div(0,1);
x7 = div(.,0);
x8 = div(.,.);
x9 = div(.,1);
put _all_;
run;
是否可以使这种形式的宏起作用?
%macro tableMath(input1,input2);
%local result;
proc sql; ---some code here using inputs--- quit;
proc sql; ---more code here--- quit;
proc sql;
select something into: result
quit;
&result
%mend;
我想 运行 对数据集的每次观察进行一些相当复杂的逻辑,并且在我之前使用过的任何其他语言中,这样做的方法是将它封装在一个函数中 returns 每次被调用时都是一个结果——但是我不确定如何在 SAS 中执行此逻辑。
编辑:input1 和 input2 将是数据集的列,结果将用于在程序另一部分的某个其他宏中创建新列。我不需要特定的代码解决方案我只是不明白你应该如何在 SAS 中需要 return 值的地方执行传统函数逻辑...
你说你想:
run some fairly complicated logic on each observation of a dataset
为此,您应该使用 SAS 语言而不是宏处理器或 PROC SQL。您可以使用数据步骤。或者对于更复杂的逻辑,您应该查看 PROC DS2。
宏函数没有 return 值。一个宏函数可以'emit'源代码
- 这是一个或多个步骤,
- 那是代码被合并到语句中的片段,
- 这是一个或多个步骤的一部分的语句,
- 等等
对于 'do' SQL 中的内容的情况,您可以编写 SQL views 然后
- 以
%sysfunc(open())
和 打开
- 已处理
%sysfunc(set())
和%sysfunc(getvarn())
和%sysfunc(getvarc())
。
并非所有 SQL 功能都可以通过此技术使用 -- select something into :result
必须是具有 select something
的视图,而宏将 getvarc
用于读取结果。
以 open/set/get 方式完成的访问不会导致出现步骤边界,因此宏处理可以继续其逻辑并最终发出源代码以供片段级别使用。 (消费者是处理宏代码的SAS执行者,隐式编译运行SAS步骤)
正如 Richard 所写,函数式宏发出 SAS 代码。开发函数式宏的一般规则是它们只包含宏语言语句。将发出它们包含的任何 SAS 代码。从历史上看,这使得 difficult/annoying 编写函数式宏来像使用 DATA 步一样处理数据。幸运的是,SAS 添加了一个函数 DOSUBL,这使得编写在 "side session" 中执行 SAS 代码并发出结果的函数式宏变得更加容易。参见 Rick Langston's paper。
这是一个函数式宏的示例,它使用 DOSUBL 计算 table 中的记录数并发出计数。 (这是一种非常低效的获取记录计数的方法,只是在SQL中做某事的一个例子)。
%macro SQLcount(table);
%local rc emit;
%let rc=%sysfunc(dosubl(%nrstr(
proc sql noprint;
select count(*) into :emit trimmed
from &table
quit;
)));
&emit
%mend ;
它可以像这样使用:
proc sql ;
select name
,%SQLcount(sashelp.shoes) as ShoeCount /*emits 395*/
from sashelp.class
;
quit ;
以上步骤运行时,会return19行来自sashelp.class的名字,每行ShoeCount的值为395。注意宏 SQLcount 只执行了一次。当 PROC SQL 步骤正在 compiled/interpreted 时,可以看到对 SQLcount 的调用,并且执行宏并发出 395。该步骤变为:
proc sql ;
select name
,395 as ShoeCount /*emits 395*/
from sashelp.class
;
quit ;
DOSUBL 使用 "side session" 来执行代码,这允许您在主会话解释 PROC SQL 步骤时在副会话中执行 PROC SQL 步骤。
我无法从你的问题中判断出那种用例是否是你想要的。您可能需要一个函数样式的宏,您可以在其中将值从 table 传递给它,并让宏在每个值和 return 上执行。假设您有一个 table,它是 table 名称的列表,并且想使用 SQL 来获取每个 table:
中的记录数data mytables ;
input table . ;
cards ;
sashelp.shoes
sashelp.class
sashelp.prdsale
;
quit ;
您可以通过使用 resolve() 函数从数据构建宏调用,延迟宏的执行直到 SELECT 语句执行:
proc sql ;
select table
,resolve('%SQLcount('||table||')') as count
from mytables
;
quit ;
这样,SQLcount 将被调用 3 次,并将 return 每个数据集中的记录数。
table count
---------------------------
sashelp.shoes 395
sashelp.class 19
sashelp.prdsale 1440
解释 PROC SQL 步骤时看不到宏调用,因为它被单引号隐藏了。解析函数然后在 SELECT 语句执行时调用宏,将 table
的值作为参数值传递,宏发出记录计数。这类似于使用数据驱动宏调用的 CALL EXECUTE 方法。
听起来您可能想使用 proc fcmp
创建 FCMP 函数。这基本上是一种创建您自己的 SAS 函数的方法,可以在 proc sql
和 data
步骤中使用。例如:
/******************************************************************************
** PROGRAM: COMMON.FCMP_DIV.SAS
**
** DESCRIPTION: PERFORMS A MATHEMATICAL DIVISION BUT WILL RETURN NULL IF THE
** NUMERATOR OR DENOMINATOR IS MISSING (OR IF THE DIVISOR IS 0).
**
******************************************************************************/
proc fcmp outlib=common.funcs.funcs;
function div(numerator, denominator);
if numerator eq . or denominator in (0,.) then do;
return(.);
end;
else do;
return(numerator / denominator);
end;
endsub;
run;
示例用法(示例是数据步骤,但在 SQL 中同样有效):
data x;
x1 = div(1,0);
x2 = div(1,.);
x3 = div(1,1);
x4 = div(0,0);
x5 = div(0,.);
x6 = div(0,1);
x7 = div(.,0);
x8 = div(.,.);
x9 = div(.,1);
put _all_;
run;