重新编码并为 sas 变量添加前缀
recode and add prefix to sas variables
假设我有一堆以相同方式命名的变量,我想对它们重新编码并为每个变量添加一个前缀(变量都是数字)。
在 Stata 中我会做类似的事情(假设变量以 eq
开头)
foreach var of varlist eq* {
recode var (1/4=1) (else=0), pre(r_)
}
我如何在 SAS 中执行此操作?我想使用 %DO 宏,但我不熟悉它们(我想避免 SQL)。如果您能附上解释每个步骤的评论,我将不胜感激!
您可以使用 rename
和一个破折号来指示您要重命名的变量。请注意以下仅重命名 col
变量,而不重命名 other
变量:
data have;
col1=1;
col2=2;
col3=3;
col5=5;
other=99;
col12=12;
run;
%macro recoder(dsn = , varname = , prefix = );
/*select all variables that include the string "varname"*/
/*(you can change this if you want to be more specific on the conditions that need to be met to be renamed)*/
proc sql noprint;
select distinct name into: varnames
separated by " "
from dictionary.columns where memname = upcase("&dsn.") and index(name, "&varname.") > 0;
quit;
data want;
set have;
/*loop through that list of variables to recode*/
%do i = 1 %to %sysfunc(countw(&varnames.));
%let this_varname = %scan(&varnames., &i.);
/*create a new variable with desired prefix based on value of old variable*/
if &this_varname. in (1 2 3) then &prefix.&this_varname. = 0;
else if &this_varname. in (4 5) then &prefix.&this_varname. = 1;
%end;
run;
%mend recoder;
%recoder(dsn = have, varname = col, prefix = r_);
如果您的变量使用数字后缀命名,则 SAS 语法会更容易。也就是说,如果您有十个名称为 eq1、eq2、....、eq10 的变量,那么您可以只使用变量列表来定义两组变量。
有多种方法可以翻译您的重新编码逻辑。如果我们假设您有干净的变量,那么我们可以只使用布尔表达式来生成 0/1 结果。因此,如果 4 和 5 映射到 1,其余映射到 0,您可以使用 x in (4,5)
或 x > 3
作为布尔表达式。
data want;
set have;
array old eq1-eq10 ;
array new r_eq1-r_eq10 ;
do i=1 to dim(old);
new(i) = old(i) in (4,5);
end;
run;
如果您有缺失值或其他复杂情况,您可能需要使用 IF/THEN 逻辑或 SELECT 语句,或者您可以定义可用于转换值的格式。
如果您的名称列表更加随机,那么您可能需要使用一些代码生成(例如宏代码)来生成新的变量名称。
这是一种在 SAS 中使用 eq:
变量列表语法的方法,它类似于您之前选择变量的语法。在源数据集的空 (obs=0) 版本上使用 PROC TRANSPOSE 来获取变量名称与您的名称模式匹配的数据集。
proc transpose data=have(obs=0) out=names;
var eq: ;
run;
然后用新旧名称列表生成两个宏变量。
proc sql noprint ;
select _name_
, cats('r_',_name_)
into :old_list separated by ' '
, :new_list separated by ' '
from names
;
quit;
然后您可以在 ARRAY 语句中使用这两个宏变量。
array old &old_list ;
array new &new_list ;
PROC TRANSPOSE 将在变量命名方式方面为您提供良好的灵活性。
proc transpose data=have(obs=0) out=vars;
var col1-numeric-col12;
copy col1;
run;
proc transpose data=vars out=revars(drop=_:) prefix=RE_;
id _name_;
run;
data recode;
set have;
if 0 then set revars;
array c[*] col1-numeric-col12;
array r[*] re_:;
call missing(of r[*]);
do _n_ = 1 to dim(c);
if c[_n_] in(1 2 3) then r[_n_] = 0;
else if c[_n_] in(4 5) then r[_n_] = 1;
else r[_n_] = c[_n_];
end;
run;
proc print;
run;
编写一个宏来解析几乎完全相同的语法几乎是微不足道的。
我不一定会使用这个 - 我更喜欢转置和数组方法,两者都更 'SASsy' (想想 'pythonic' 但对于 SAS) - 但这个或多或少正是你在上面做的。
首先设置一个数据集:
data class;
set sashelp.class;
age_ly = age-1;
age_ny = age+1;
run;
然后宏:
%macro do_count(data=, out=, prefix=, condition=, recode=, else=, var_start=);
%local dsid varcount varname rc; *declare local for safety;
%let dsid = %sysfunc(open(&data.,i)); *open the dataset;
%let varcount = %sysfunc(attrn(&dsid,nvars)); *get the count of variables to access;
data &out.; *now start the main data step;
set &data.; *set the original data set;
%do i = 1 %to &varcount; *iterate over the variables;
%let varname= %sysfunc(varname(&dsid.,&i.)); *determine the variable name;
%if %upcase(%substr(&varname.,1,%length(&var_start.))) = %upcase(&var_start.) %then %do; *if it matches your pattern then recode it;
&prefix.&varname. = ifn(&varname. &condition., &recode., &else.); *this uses IFN - only recodes numerics. More complicated code would work if this could be character.;
%end;
%end;
%let rc = %sysfunc(close(&dsid)); *clean up after yourself;
run;
%mend do_count;
%do_count(data=class, out=class_r, var_start=age, condition= > 14, recode=1, else=0, prefix=p_);
The expression (1/4=1) means values {1,2,3,4} should be recoded into
1.
也许您根本不需要创建新变量?如果有值为 1、2、3、4、5 的变量,并且您想将它们视为只有两个组,您可以使用一种格式。
首先使用格式定义您的分组。
proc format ;
value newgrp 1-4='Group 1' 5='Group 2' ;
run;
然后您可以在分析步骤中使用 FORMAT 语句让 SAS 处理您的五级变量,就像它只有两个级别一样。
proc freq ;
tables eq: ;
format eq: NEWGRP. ;
run;
假设我有一堆以相同方式命名的变量,我想对它们重新编码并为每个变量添加一个前缀(变量都是数字)。
在 Stata 中我会做类似的事情(假设变量以 eq
开头)
foreach var of varlist eq* {
recode var (1/4=1) (else=0), pre(r_)
}
我如何在 SAS 中执行此操作?我想使用 %DO 宏,但我不熟悉它们(我想避免 SQL)。如果您能附上解释每个步骤的评论,我将不胜感激!
您可以使用 rename
和一个破折号来指示您要重命名的变量。请注意以下仅重命名 col
变量,而不重命名 other
变量:
data have;
col1=1;
col2=2;
col3=3;
col5=5;
other=99;
col12=12;
run;
%macro recoder(dsn = , varname = , prefix = );
/*select all variables that include the string "varname"*/
/*(you can change this if you want to be more specific on the conditions that need to be met to be renamed)*/
proc sql noprint;
select distinct name into: varnames
separated by " "
from dictionary.columns where memname = upcase("&dsn.") and index(name, "&varname.") > 0;
quit;
data want;
set have;
/*loop through that list of variables to recode*/
%do i = 1 %to %sysfunc(countw(&varnames.));
%let this_varname = %scan(&varnames., &i.);
/*create a new variable with desired prefix based on value of old variable*/
if &this_varname. in (1 2 3) then &prefix.&this_varname. = 0;
else if &this_varname. in (4 5) then &prefix.&this_varname. = 1;
%end;
run;
%mend recoder;
%recoder(dsn = have, varname = col, prefix = r_);
如果您的变量使用数字后缀命名,则 SAS 语法会更容易。也就是说,如果您有十个名称为 eq1、eq2、....、eq10 的变量,那么您可以只使用变量列表来定义两组变量。
有多种方法可以翻译您的重新编码逻辑。如果我们假设您有干净的变量,那么我们可以只使用布尔表达式来生成 0/1 结果。因此,如果 4 和 5 映射到 1,其余映射到 0,您可以使用 x in (4,5)
或 x > 3
作为布尔表达式。
data want;
set have;
array old eq1-eq10 ;
array new r_eq1-r_eq10 ;
do i=1 to dim(old);
new(i) = old(i) in (4,5);
end;
run;
如果您有缺失值或其他复杂情况,您可能需要使用 IF/THEN 逻辑或 SELECT 语句,或者您可以定义可用于转换值的格式。
如果您的名称列表更加随机,那么您可能需要使用一些代码生成(例如宏代码)来生成新的变量名称。
这是一种在 SAS 中使用 eq:
变量列表语法的方法,它类似于您之前选择变量的语法。在源数据集的空 (obs=0) 版本上使用 PROC TRANSPOSE 来获取变量名称与您的名称模式匹配的数据集。
proc transpose data=have(obs=0) out=names;
var eq: ;
run;
然后用新旧名称列表生成两个宏变量。
proc sql noprint ;
select _name_
, cats('r_',_name_)
into :old_list separated by ' '
, :new_list separated by ' '
from names
;
quit;
然后您可以在 ARRAY 语句中使用这两个宏变量。
array old &old_list ;
array new &new_list ;
PROC TRANSPOSE 将在变量命名方式方面为您提供良好的灵活性。
proc transpose data=have(obs=0) out=vars;
var col1-numeric-col12;
copy col1;
run;
proc transpose data=vars out=revars(drop=_:) prefix=RE_;
id _name_;
run;
data recode;
set have;
if 0 then set revars;
array c[*] col1-numeric-col12;
array r[*] re_:;
call missing(of r[*]);
do _n_ = 1 to dim(c);
if c[_n_] in(1 2 3) then r[_n_] = 0;
else if c[_n_] in(4 5) then r[_n_] = 1;
else r[_n_] = c[_n_];
end;
run;
proc print;
run;
编写一个宏来解析几乎完全相同的语法几乎是微不足道的。
我不一定会使用这个 - 我更喜欢转置和数组方法,两者都更 'SASsy' (想想 'pythonic' 但对于 SAS) - 但这个或多或少正是你在上面做的。
首先设置一个数据集:
data class;
set sashelp.class;
age_ly = age-1;
age_ny = age+1;
run;
然后宏:
%macro do_count(data=, out=, prefix=, condition=, recode=, else=, var_start=);
%local dsid varcount varname rc; *declare local for safety;
%let dsid = %sysfunc(open(&data.,i)); *open the dataset;
%let varcount = %sysfunc(attrn(&dsid,nvars)); *get the count of variables to access;
data &out.; *now start the main data step;
set &data.; *set the original data set;
%do i = 1 %to &varcount; *iterate over the variables;
%let varname= %sysfunc(varname(&dsid.,&i.)); *determine the variable name;
%if %upcase(%substr(&varname.,1,%length(&var_start.))) = %upcase(&var_start.) %then %do; *if it matches your pattern then recode it;
&prefix.&varname. = ifn(&varname. &condition., &recode., &else.); *this uses IFN - only recodes numerics. More complicated code would work if this could be character.;
%end;
%end;
%let rc = %sysfunc(close(&dsid)); *clean up after yourself;
run;
%mend do_count;
%do_count(data=class, out=class_r, var_start=age, condition= > 14, recode=1, else=0, prefix=p_);
The expression (1/4=1) means values {1,2,3,4} should be recoded into 1.
也许您根本不需要创建新变量?如果有值为 1、2、3、4、5 的变量,并且您想将它们视为只有两个组,您可以使用一种格式。
首先使用格式定义您的分组。
proc format ;
value newgrp 1-4='Group 1' 5='Group 2' ;
run;
然后您可以在分析步骤中使用 FORMAT 语句让 SAS 处理您的五级变量,就像它只有两个级别一样。
proc freq ;
tables eq: ;
format eq: NEWGRP. ;
run;