根据特定条件使用 sas table 创建新变量

Creating new variables using sas table according specific condition

我有一个 SAS table,它有一个数字变量 age。我需要根据 age 的值构造新变量。新变量应该有这样的逻辑:

所以我用宏写了这段代码以避免重复:

%macro intervalle_age(var,var1,var2);
if (&var=>&var1) and (&var<=&var2);
then return 1;
else return 0;
%mend;

然后我调用宏来获取每个新变量的值:

age0=%intervalle_age(age,0,25);
age25=%intervalle_age(age,26,40);
age25=%intervalle_age(age,41,65);
age25=%intervalle_age(age,65,771);

但这行不通!

请问如何解决? 提前致谢!

return 在 SAS 宏中没有任何特殊含义。宏被称为 "generate" 代码,即宏调用被文本替换,这是在处理宏处理器 "understands" 的东西之后留下的(基本上,涉及以 [=15 开头的标记(单词) =] 或 %).

在你的例子中,宏处理器只是扩展宏变量(其余只是文本,宏处理器保持不变),导致:

age0=if (age=>0) and (age<=25);
then return 1;
else return 0;
age25=/*and so on*/

理解how the macro processor and regular execution interact很重要(基本上,所有宏扩展必须在给定的 DATA 或 PROC 步骤开始执行之前完成)。


要完成这项工作,您需要生成完整的 if 语句,包括对输出变量的赋值:

%macro calc_age_interval(outvar, inputvar, lbound, ubound);
  if (&inputvar=>&lbound) and (&inputvar<=&ubound) then do;
    &outvar = 1;
  end; else do;
    &outvar = 0;
  end;
%mend calc_age_interval;
%calc_age_interval(outvar=age0, inputvar=age, lbound=0, ubound=25);

或者让它生成一个表达式,该表达式在执行时将计算为 0 或 1(通过将结果直接分配给变量(布尔表达式的结果无论如何都是 1 或 0),或使用 IFN() 更明确):

%macro calc_age_interval(inputvar, lbound, ubound);
  ifn((&inputvar=>&lbound) and (&inputvar<=&ubound), 1, 0)
%mend;
age0 = %calc_age_interval(age, 0, 25); /* expands to age0=ifn(..., 1, 0); */

退一步说,在这种情况下我根本不会为宏操心。您可以使用 in (M:N) range notation 或将所有输出变量重置为 0,然后执行 if-elseif:

if age < 0 then age_missing_or_negative = 1;
else if age <= 25 then age0 = 1;
else if age <= 40 then age25 = 1;
...

我同意 Nikolay 的观点,您应该退后一步,完全避免宏观操作。您发布的示例代码似乎不正确,您有四个针对不同年龄范围的条件仅分配给两个变量。

在 SAS 中,逻辑评估解析为 1 表示真,0 表示假。此外,数值变量可用于具有非零、非缺失值的逻辑表达式,否则表示真假。

因此,分配年龄范围标志变量的代码序列为:

age0  =  0 < age <= 25 ;
age25 = 25 < age <= 40 ;
age40 = 40 < age <= 65 ;
age65 = 65 < age <= 71 ;
age71 = 71 < age ;

将简单易读的 SAS 语句隐藏在宏代码墙之后可能会导致维护问题并降低未来的理解。但是,如果您的用例是构建多组此类代码块,则基于断点的宏可能会带来更好的易读性和理解性。

data have; age = 22; bmi = 20; run;
options mprint;

* easier to understand and not prone to copy paste issues or typos;
data want;
  set have;
  %make_flag_variables (var=age, breakpoints=0 25 40 65 71)
  %make_flag_variables (var=bmi, breakpoints=0 18.5 25 30)
run;

取决于这个宏

%macro make_flag_variables (var=, breakpoints=);
  %local I BREAKPOINT SUFFIX_LOW RANGE_LOW SUFFIX_HIGH RANGE_HIGH;
  %let I = 1;
  %do %while (%length(%scan(&breakpoints,&I,%str( ))));
    %let BREAKPOINT = %scan(&breakpoints,&I,%str( ));

    %let SUFFIX_LOW = &SUFFIX_HIGH;
    %let SUFFIX_HIGH = %sysfunc(TRANSLATE(&BREAKPOINT,_,.));

    %let RANGE_LOW = &RANGE_HIGH;
    %let RANGE_HIGH = &BREAKPOINT;

    %if &I > 1 %then %do;
        &VAR.&SUFFIX_LOW = &RANGE_LOW < &VAR <= &RANGE_HIGH; /* data step source code emitted here */
    %end; 

    %let I = %eval ( &I + 1 );
  %end;
%mend;

日志片段显示宏执行的代码生成

92   data want;
93     set have;
94
95     %make_flag_variables (var=age, breakpoints=0 25 40 65 71)
MPRINT(MAKE_FLAG_VARIABLES):   age0 = 0 < age <= 25;
MPRINT(MAKE_FLAG_VARIABLES):   age25 = 25 < age <= 40;
MPRINT(MAKE_FLAG_VARIABLES):   age40 = 40 < age <= 65;
MPRINT(MAKE_FLAG_VARIABLES):   age65 = 65 < age <= 71;
96     %make_flag_variables (var=bmi, breakpoints=0 18.5 25 30)
MPRINT(MAKE_FLAG_VARIABLES):   bmi0 = 0 < bmi <= 18.5;
MPRINT(MAKE_FLAG_VARIABLES):   bmi18_5 = 18.5 < bmi <= 25;
MPRINT(MAKE_FLAG_VARIABLES):   bmi25 = 25 < bmi <= 30;
97   run;