宏变量在 DATA 步中具有不同的值。为什么?
Macro variable has different value within DATA Step. Why?
我有理由使用重复过程处理给定数据集的不同变量。为了解决这个问题,我编写了一个宏,其输入是感兴趣的特定变量。然后宏将只处理该变量。然而,事实证明其中一个变量需要稍微不同地处理。我的快速修复是应用条件;如果变量是异常,则执行与其他变量不同的操作。问题解决了吧?没有。
我发现宏变量的值会根据它是否在数据步骤中使用而变化。
请考虑,
data example;
length dataset_var1 $ 6 dataset_var2 $ 6;
input dataset_var1 $ dataset_var2;
datalines;
value1 value2
value3 value4
;
run;
宏及其调用:
%macro NoQuotes(macro_var);
%put ¯o_var. ;
data _null;
set example;
put ¯o_var. ;
if ¯o_var. = 'dataset_var1' then do;
put "The IF evaluated";
end;
else do;
put "The ELSE evaluated";
end;
run;
%put ¯o_var. ;
%mend;
%NoQuotes(dataset_var1);
这会产生以下日志条目:
dataset_var1
value1
The ELSE evaluated
value3
The ELSE evaluated
NOTE: There were 2 observations read from the data set WORK.EXAMPLE.
NOTE: The data set WORK._NULL has 2 observations and 2 variables.
NOTE: DATA statement used (Total process time):
real time 0.01 seconds
cpu time 0.00 seconds
dataset_var1
注意 macro_var
的值如何根据它是否在 DATA 步内而变化。在 DATA 步中,macro_var
取 dataset_var1
的值,即 value1
和 value3
,而不是像您期望的那样保留名称 dataset_var1
。一旦在 DATA 步之外,macro_var
的值神奇地 returns 到它的正确值。
在同事的建议下,我将宏变量的名称放在条件语句中的引号中。这使得条件行为符合预期。
%macro WithQuotes(macro_var);
%put ¯o_var. ;
data _null_;
set example;
put ¯o_var. ;
if "¯o_var." = 'dataset_var1' then do;
put "The IF evaluated";
end;
else do;
put "The ELSE evaluated";
end;
run;
%put ¯o_var. ;
%mend;
%WithQuotes(dataset_var1);
这会产生以下日志条目:
dataset_var1
value1
The IF evaluated
value3
The IF evaluated
NOTE: There were 2 observations read from the data set WORK.EXAMPLE.
NOTE: DATA statement used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
dataset_var1
虽然条件现在按预期执行,但我们再次看到宏变量采用 value1
和 value3
的值。
宏变量的行为似乎 运行 与我从 BASIC、C++、Java、C#、VBA、Python、Lisp 和 R.
有人可以向我解释一下这是怎么回事吗?我已经阅读了大部分 Macro Language Reference,但不确定在哪里可以找到对这种行为的解释。
宏语言生成代码。简单来说,它会进行文本替换。
因此您的第一个宏调用会创建以下代码
%put dataset_var1 ;
data _null;
set example;
put dataset_var1 ;
if dataset_var1 = 'dataset_var1' then do;
put "The IF evaluated";
end;
else do;
put "The ELSE evaluated";
end;
run;
%put dataset_var1 ;
因为变量存在于数据集上,所以它引用了变量值,而您看到的输出正是我所期望的。
添加引号时,您不再引用变量,而是创建一个与 IF 条件中的值匹配的文本字符串。
这是字符串与字符串的比较:
if "dataset_var1" = 'dataset_var1' then do;
这引用了一个变量 dataset_var1,它包含两个值,value1/value3。
if dataset_var1 = 'dataset_var1' then do;
如果您使用 OPTIONS MPRINT SYMBOLGEN;
,您将看到 SAS 在每个步骤中将变量解析为什么以及逻辑是如何计算的。
the macro variable takes on the values of value1 and value3
否:宏变量是一串字符dataset_var1
。宏变量不变。宏变量与数据步骤中同名的变量不同。每次 SAS 宏处理器看到 &dataset_var1
时,它都会用字符串 dataset_var1
替换它。 SAS 宏处理器通过字符串替换生成代码,而不是计算代码。
提示:如果您打开 options mprint;
,您可以看到宏处理器生成的代码。
%put ¯o_var;
将 ¯o_var
替换为字符串 dataset_var1
并将字符串 dataset_var1
放入日志中,就像您写了 %put dataset_var1;
在数据步骤中,宏处理器重写了以下语句:
put ¯o_var
;变成 put dataset_var1;
put
(不是%put
),将数据集变量的值写入日志,所以这把变量的值dataset_var1
到日志。此变量是 'value1' 或 'value3'.
if "¯o_var." = 'dataset_var1'
变成 if dataset_var1 = 'dataset_var1'
它不是比较 ¯o_var
和 'dataset_var1`,而是比较 'value1' = 'dataset_var1'第一行,'value3' = 'dataset_var1'。这对两行都是错误的。
最后的 %put ¯o_var;
与第一个相同,因为 ¯o_var
没有改变。
The behavior of macro variables seems to run contrary to everything
I've ever known about the concept of variables from BASIC, C++, Java,
C#, VBA, Python, Lisp, and R.
那是因为SAS宏变量不是SAS变量! SAS 宏更像是 C pre-processor statements,而不是 C 代码。 SAS 宏变量就像编译器 #define
指令。根据我的经验,它们是 SAS 程序中最常见的混乱和错误原因之一,应该尽可能消除!
简答:
尝试使用宏 %IF 语句而不是数据步语言 IF 语句。
长答案:
宏语言是一种预处理器,它处理文本以生成代码。一般来说,宏语言并不知道 SAS 数据集或数据集中的变量。 DATA 步语言处理数据集和数据集变量。宏语言看起来和DATA步语言很像,但是它们的用途完全不同,是两种不同的语言。
考虑:
87 options mprint;
88
89 %macro ShowValue(var);
90 data _null_;
91 set sashelp.class (obs=3);
92
93 %put The macro variable VAR has the value: &VAR;
94 put "The dataset variable &VAR has the value: " &VAR;
95
96 run;
97
98 %mend;
99
100 %ShowValue(var=height)
MPRINT(SHOWVALUE): data _null_;
MPRINT(SHOWVALUE): set sashelp.class (obs=3);
The macro variable VAR has the value: height
MPRINT(SHOWVALUE): put "The dataset variable height has the value: " height;
MPRINT(SHOWVALUE): run;
The dataset variable height has the value: 69
The dataset variable height has the value: 56.5
The dataset variable height has the value: 65.3
NOTE: There were 3 observations read from the data set SASHELP.CLASS.
宏语言有一个 %PUT 语句,数据步语言有一个 PUT 语句。上面,%PUT 语句的目的是显示名为 VAR
的宏变量(参数)的值。用户已将值 height
传递给它。上面 PUT 语句的目的是显示用户命名的数据步骤变量的值。因为用户已经传递了值 height
,所以列出了该数据集变量的值(针对处理的所有三个记录)。名为 VAR 的宏变量始终具有值 height
。名为 height
的数据集变量对于不同的记录具有不同的值。请注意,%PUT 语句仅执行一次,即使它在数据步循环内也是如此。这是因为在执行(甚至编译)任何数据步骤代码之前执行宏语句。 PUT 语句是数据步骤代码,因此它为数据步骤处理的每条记录编译和执行一次。 PUT 语句需要引号,以便它可以区分文本文字和数据步骤变量的名称。 %PUT 语句(实际上整个宏语言)不需要引号,因为宏变量由 &.
引用
如果要根据宏变量的值进行条件求值来决定宏应该生成哪个数据步,可以使用宏%IF语句,例如:
102 %macro ShowValue(var);
103 data _null_;
104 set sashelp.class (obs=3);
105
106 %if &var=height %then %do;
107 &var=&var * 2.54; *convert height from inches to cm;
108 %end;
109
110 %put The macro variable VAR has the value: &VAR;
111 put "The dataset variable &VAR has the value: " &VAR;
112
113 run;
114
115 %mend;
116
117 %ShowValue(var=height)
MPRINT(SHOWVALUE): data _null_;
MPRINT(SHOWVALUE): set sashelp.class (obs=3);
MPRINT(SHOWVALUE): height=height * 2.54;
MPRINT(SHOWVALUE): *convert height from inches to cm;
The macro variable VAR has the value: height
MPRINT(SHOWVALUE): put "The dataset variable height has the value: " height;
MPRINT(SHOWVALUE): run;
The dataset variable height has the value: 175.26
The dataset variable height has the value: 143.51
The dataset variable height has the value: 165.862
NOTE: There were 3 observations read from the data set SASHELP.CLASS.
118 %ShowValue(var=weight)
MPRINT(SHOWVALUE): data _null_;
MPRINT(SHOWVALUE): set sashelp.class (obs=3);
The macro variable VAR has the value: weight
MPRINT(SHOWVALUE): put "The dataset variable weight has the value: " weight;
MPRINT(SHOWVALUE): run;
The dataset variable weight has the value: 112.5
The dataset variable weight has the value: 84
The dataset variable weight has the value: 98
NOTE: There were 3 observations read from the data set SASHELP.CLASS.
宏 %IF 语句解析宏变量并将该宏变量的解析值与文本字符串进行比较 height
。请注意,%IF 语句中不需要(或不需要)引号。因为宏语言是处理文本的,所以不使用引号来表示文本值。
简而言之,使用宏%IF语句根据宏变量中存储的文本值进行决策,以控制宏语言生成哪些数据步代码,并使用数据步IF语句进行决策数据步变量的值来控制执行哪个数据步代码。 (IF语句中引用的数据步变量的名称可能是使用宏变量生成的,甚至是宏。)
了解 %PUT 与 PUT、%IF 与 IF、%DO 与 DO 等之间的区别是学习使用宏语言的关键步骤。
我有理由使用重复过程处理给定数据集的不同变量。为了解决这个问题,我编写了一个宏,其输入是感兴趣的特定变量。然后宏将只处理该变量。然而,事实证明其中一个变量需要稍微不同地处理。我的快速修复是应用条件;如果变量是异常,则执行与其他变量不同的操作。问题解决了吧?没有。
我发现宏变量的值会根据它是否在数据步骤中使用而变化。
请考虑,
data example;
length dataset_var1 $ 6 dataset_var2 $ 6;
input dataset_var1 $ dataset_var2;
datalines;
value1 value2
value3 value4
;
run;
宏及其调用:
%macro NoQuotes(macro_var);
%put ¯o_var. ;
data _null;
set example;
put ¯o_var. ;
if ¯o_var. = 'dataset_var1' then do;
put "The IF evaluated";
end;
else do;
put "The ELSE evaluated";
end;
run;
%put ¯o_var. ;
%mend;
%NoQuotes(dataset_var1);
这会产生以下日志条目:
dataset_var1
value1
The ELSE evaluated
value3
The ELSE evaluated
NOTE: There were 2 observations read from the data set WORK.EXAMPLE.
NOTE: The data set WORK._NULL has 2 observations and 2 variables.
NOTE: DATA statement used (Total process time):
real time 0.01 seconds
cpu time 0.00 seconds
dataset_var1
注意 macro_var
的值如何根据它是否在 DATA 步内而变化。在 DATA 步中,macro_var
取 dataset_var1
的值,即 value1
和 value3
,而不是像您期望的那样保留名称 dataset_var1
。一旦在 DATA 步之外,macro_var
的值神奇地 returns 到它的正确值。
在同事的建议下,我将宏变量的名称放在条件语句中的引号中。这使得条件行为符合预期。
%macro WithQuotes(macro_var);
%put ¯o_var. ;
data _null_;
set example;
put ¯o_var. ;
if "¯o_var." = 'dataset_var1' then do;
put "The IF evaluated";
end;
else do;
put "The ELSE evaluated";
end;
run;
%put ¯o_var. ;
%mend;
%WithQuotes(dataset_var1);
这会产生以下日志条目:
dataset_var1
value1
The IF evaluated
value3
The IF evaluated
NOTE: There were 2 observations read from the data set WORK.EXAMPLE.
NOTE: DATA statement used (Total process time):
real time 0.00 seconds
cpu time 0.00 seconds
dataset_var1
虽然条件现在按预期执行,但我们再次看到宏变量采用 value1
和 value3
的值。
宏变量的行为似乎 运行 与我从 BASIC、C++、Java、C#、VBA、Python、Lisp 和 R.
有人可以向我解释一下这是怎么回事吗?我已经阅读了大部分 Macro Language Reference,但不确定在哪里可以找到对这种行为的解释。
宏语言生成代码。简单来说,它会进行文本替换。
因此您的第一个宏调用会创建以下代码
%put dataset_var1 ;
data _null;
set example;
put dataset_var1 ;
if dataset_var1 = 'dataset_var1' then do;
put "The IF evaluated";
end;
else do;
put "The ELSE evaluated";
end;
run;
%put dataset_var1 ;
因为变量存在于数据集上,所以它引用了变量值,而您看到的输出正是我所期望的。
添加引号时,您不再引用变量,而是创建一个与 IF 条件中的值匹配的文本字符串。
这是字符串与字符串的比较:
if "dataset_var1" = 'dataset_var1' then do;
这引用了一个变量 dataset_var1,它包含两个值,value1/value3。
if dataset_var1 = 'dataset_var1' then do;
如果您使用 OPTIONS MPRINT SYMBOLGEN;
,您将看到 SAS 在每个步骤中将变量解析为什么以及逻辑是如何计算的。
the macro variable takes on the values of value1 and value3
否:宏变量是一串字符dataset_var1
。宏变量不变。宏变量与数据步骤中同名的变量不同。每次 SAS 宏处理器看到 &dataset_var1
时,它都会用字符串 dataset_var1
替换它。 SAS 宏处理器通过字符串替换生成代码,而不是计算代码。
提示:如果您打开 options mprint;
,您可以看到宏处理器生成的代码。
%put ¯o_var;
将 ¯o_var
替换为字符串 dataset_var1
并将字符串 dataset_var1
放入日志中,就像您写了 %put dataset_var1;
在数据步骤中,宏处理器重写了以下语句:
put ¯o_var
;变成 put dataset_var1;
put
(不是%put
),将数据集变量的值写入日志,所以这把变量的值dataset_var1
到日志。此变量是 'value1' 或 'value3'.
if "¯o_var." = 'dataset_var1'
变成 if dataset_var1 = 'dataset_var1'
它不是比较 ¯o_var
和 'dataset_var1`,而是比较 'value1' = 'dataset_var1'第一行,'value3' = 'dataset_var1'。这对两行都是错误的。
最后的 %put ¯o_var;
与第一个相同,因为 ¯o_var
没有改变。
The behavior of macro variables seems to run contrary to everything I've ever known about the concept of variables from BASIC, C++, Java, C#, VBA, Python, Lisp, and R.
那是因为SAS宏变量不是SAS变量! SAS 宏更像是 C pre-processor statements,而不是 C 代码。 SAS 宏变量就像编译器 #define
指令。根据我的经验,它们是 SAS 程序中最常见的混乱和错误原因之一,应该尽可能消除!
简答: 尝试使用宏 %IF 语句而不是数据步语言 IF 语句。
长答案: 宏语言是一种预处理器,它处理文本以生成代码。一般来说,宏语言并不知道 SAS 数据集或数据集中的变量。 DATA 步语言处理数据集和数据集变量。宏语言看起来和DATA步语言很像,但是它们的用途完全不同,是两种不同的语言。
考虑:
87 options mprint;
88
89 %macro ShowValue(var);
90 data _null_;
91 set sashelp.class (obs=3);
92
93 %put The macro variable VAR has the value: &VAR;
94 put "The dataset variable &VAR has the value: " &VAR;
95
96 run;
97
98 %mend;
99
100 %ShowValue(var=height)
MPRINT(SHOWVALUE): data _null_;
MPRINT(SHOWVALUE): set sashelp.class (obs=3);
The macro variable VAR has the value: height
MPRINT(SHOWVALUE): put "The dataset variable height has the value: " height;
MPRINT(SHOWVALUE): run;
The dataset variable height has the value: 69
The dataset variable height has the value: 56.5
The dataset variable height has the value: 65.3
NOTE: There were 3 observations read from the data set SASHELP.CLASS.
宏语言有一个 %PUT 语句,数据步语言有一个 PUT 语句。上面,%PUT 语句的目的是显示名为 VAR
的宏变量(参数)的值。用户已将值 height
传递给它。上面 PUT 语句的目的是显示用户命名的数据步骤变量的值。因为用户已经传递了值 height
,所以列出了该数据集变量的值(针对处理的所有三个记录)。名为 VAR 的宏变量始终具有值 height
。名为 height
的数据集变量对于不同的记录具有不同的值。请注意,%PUT 语句仅执行一次,即使它在数据步循环内也是如此。这是因为在执行(甚至编译)任何数据步骤代码之前执行宏语句。 PUT 语句是数据步骤代码,因此它为数据步骤处理的每条记录编译和执行一次。 PUT 语句需要引号,以便它可以区分文本文字和数据步骤变量的名称。 %PUT 语句(实际上整个宏语言)不需要引号,因为宏变量由 &.
如果要根据宏变量的值进行条件求值来决定宏应该生成哪个数据步,可以使用宏%IF语句,例如:
102 %macro ShowValue(var);
103 data _null_;
104 set sashelp.class (obs=3);
105
106 %if &var=height %then %do;
107 &var=&var * 2.54; *convert height from inches to cm;
108 %end;
109
110 %put The macro variable VAR has the value: &VAR;
111 put "The dataset variable &VAR has the value: " &VAR;
112
113 run;
114
115 %mend;
116
117 %ShowValue(var=height)
MPRINT(SHOWVALUE): data _null_;
MPRINT(SHOWVALUE): set sashelp.class (obs=3);
MPRINT(SHOWVALUE): height=height * 2.54;
MPRINT(SHOWVALUE): *convert height from inches to cm;
The macro variable VAR has the value: height
MPRINT(SHOWVALUE): put "The dataset variable height has the value: " height;
MPRINT(SHOWVALUE): run;
The dataset variable height has the value: 175.26
The dataset variable height has the value: 143.51
The dataset variable height has the value: 165.862
NOTE: There were 3 observations read from the data set SASHELP.CLASS.
118 %ShowValue(var=weight)
MPRINT(SHOWVALUE): data _null_;
MPRINT(SHOWVALUE): set sashelp.class (obs=3);
The macro variable VAR has the value: weight
MPRINT(SHOWVALUE): put "The dataset variable weight has the value: " weight;
MPRINT(SHOWVALUE): run;
The dataset variable weight has the value: 112.5
The dataset variable weight has the value: 84
The dataset variable weight has the value: 98
NOTE: There were 3 observations read from the data set SASHELP.CLASS.
宏 %IF 语句解析宏变量并将该宏变量的解析值与文本字符串进行比较 height
。请注意,%IF 语句中不需要(或不需要)引号。因为宏语言是处理文本的,所以不使用引号来表示文本值。
简而言之,使用宏%IF语句根据宏变量中存储的文本值进行决策,以控制宏语言生成哪些数据步代码,并使用数据步IF语句进行决策数据步变量的值来控制执行哪个数据步代码。 (IF语句中引用的数据步变量的名称可能是使用宏变量生成的,甚至是宏。)
了解 %PUT 与 PUT、%IF 与 IF、%DO 与 DO 等之间的区别是学习使用宏语言的关键步骤。