迭代一维(列向量)SAS table 并将条目存储为宏变量以执行一些计算

Iterating over a 1-dimensional (column vector) SAS table and storing the entry as a macro-variable to perform some computation

我目前正在学习如何使用 SAS,我有一个问题,是否可以将 SAS table 中的条目存储为宏变量,然后在进入下一个之前进行一些计算迭代。也就是说,使用 DUMMY_TABLE[i,j] 之类的方法从 table 中获取值,例如在 R 或 Python.

中可能实现的值

Suppose we were to have a SAS table with name, "dummy_tab", with one variable named, "NAME" and entries [a,b,c,d,e,f]. Is it then possible using a DO Loop, or some other method, to be able to iterate over the values in "dummy_tab" and store this as a macrovariable to perform some operation on it before iterating to the next entry?

关于为什么我什至考虑这种方法的一些想法,这是我的问题:

我正在尝试创建一个自动重命名具有带下划线的特殊字符的变量的宏。我目前的方法是提取变量名并将其存储在一维 SAS table(列向量)中。

然后,我将迭代此列向量的条目并将每个条目存储到宏变量 (mc1) 中。在此基础上,我将使用 PXRCHANGE 将所有特殊字符替换为下划线,然后将其存储为新的宏变量 (mc2.)

最后,我将使用我定义的两个宏变量重命名原始数据库中的变量,即:RENAME &mc1。 = &mc2。之后我将进行下一次迭代,并开始重命名第二个变量的过程,等等。

/* Generate dummy data */
option validvarname = any;
data dummy_data;
    input "dummy?"n "te!st"n;
    datalines;
    1 1
    2 2
    3 3
    ;
run;

/* Obtain variable names as a column vector */
proc contents
    data = dummy_data
    noprint
    out = col_name
        (keep = name);
run;

/* Stores number of variables in dummy_data in macrovariable n_obs*/
proc sql;
    select count(NAME) into : n_obs
    from col_name;
quit;

/* Iterates over column names and stores i_th column name as a macrovariable */
data output_tab;
set col_name;
    
    do i = 1 to &n_obs.;
        
/* Here is where the problem lies: */
        %put i_col_name = col_name[i];
                
        /* PRXCHANGE step and rename of variable to go here */
run;

您描述的所有内容都是可能的,但“可能”和“SAS 惯用”之间存在一些差异。

在 SAS 中要理解的主要内容是,“数据步骤”基本上是所有编程所在的地方,并且不容易从其他一些语言 (R/Python) 转移,并且在 SAS 中,“列”是您操作的主要变量。宏变量确实存在,但它们的目的是存储 SAS 代码(或者更确切地说,存储将成为 SAS 代码的东西)而不是数据。

在你的情况下,它更复杂,因为你正在编写 SAS 代码,所以将它放在宏变量中确实有意义 - 但不是在你正在编写的阶段谈论。将所有内容保存在数据集中,直到您准备好真正执行重命名!

最直接的方法是...

data vars_to_rename;    *this is identical, basically, to the proc contents output - use whichever you prefer;
  set sashelp.vcolumn;
  where libname='WORK' and memname='DUMMY_DATA';
  new_name = translate(name,'__','!?');  *replace this with your PRXCHANGE;
run;

%macro renamer(from=,to=);   *simple one line macro to do the renaming;
  rename "&from."n = &to.;
%mend renamer;

proc sql;  *create one renamer macro call for each variable to be renamed;
  select cats('%renamer(from=',name,',to=',new_name,')')
    into :renamelist separated by ' '
    from vars_to_rename
  ;   *could easily add a WHERE name ne new_name; to only rename vars that actually need it;
quit; 

data new_dataset;  *could do this in PROC DATASETS also to save disk time;
  set dummy_data;
  &renamelist;
run;

唯一以宏语言结尾的是您要编写的实际代码。可以仅使用宏语言完成所有这些操作,但这既不符合习惯也不高效。

假设 VALIDVARNAME 出于某种原因对您来说不是一个好的解决方案,这里有一个不同的方法。

  • 使用VCOLUMNS/dictionary.columns 数据集来获取列名
  • 创建一个新变量,new_name 使用基于 name 变量
  • 的新变量名称
  • 使用 CALL EXECUTE 生成代码,而不是宏

下面的完整示例采用 SASHELP.CLASS 数据集并将变量重命名为 VAR001-VAR###。 您可以将需要的任何内容添加到 new_name 计算中以简化过程。

不过,如果您想创建一个新的数据集,可以执行类似的操作。

如果您想要更类似于 R/Python 的功能,您可以根据需要查看 IML。

*fake data to play with;
data class;
set sashelp.class;
run;

*data set name to fix;
*making code slightly easier to follow;
%let dsn_in = CLASS;

data _null_;
set sashelp.vcolumn end=eof;
where libname = 'WORK' and memname = "&dsn_in";

 if _n_=1 then
    call execute ("proc datasets lib=WORK nodetails nolist; modify &dsn_in; rename ");
 
 *create new variable name here;
 *in this example just renaming it to VAR001 - VAR###;
 *variable name is 'name';
 new_name=catt('VAR', put(_n_, z3.));
 
 *pass new and old name to proc datasets;
 call execute (name);
 call execute ('=');
 call execute (new_name);
 
 *if last record then quit;
 If eof then
    call execute (';run;quit;');
    
    
run;

部分日志:

NOTE: CALL EXECUTE generated line.
 1         + proc datasets lib=WORK nodetails nolist; modify CLASS;
 1         +                                                        rename
 2         + Name
 3         + =
 4         + VAR001
 5         + Sex
 6         + =
 7         + VAR002
 8         + Age
 9         + =
 10        + VAR003
 11        + Height
 12        + =
 13        + VAR004
 14        + Weight
 15        + =
 16        + VAR005
 17        + ;
 NOTE: Renaming variable Name to VAR001.
 NOTE: Renaming variable Sex to VAR002.
 NOTE: Renaming variable Age to VAR003.
 NOTE: Renaming variable Height to VAR004.
 NOTE: Renaming variable Weight to VAR005.
 17        +  run;
 
 NOTE: MODIFY was successful for WORK.CLASS.DATA.
 17        +      quit;
 
 NOTE: PROCEDURE DATASETS used (Total process time):
       real time           0.00 seconds
       user cpu time       0.00 seconds
       system cpu time     0.01 seconds
       memory              596.56k
       OS Memory           26020.00k
       Timestamp           01/18/2022 05:14:09 PM
       Step Count                        26  Switch Count  0
       Page Faults                       0
       Page Reclaims                     245
       Page Swaps                        0
       Voluntary Context Switches        0
       Involuntary Context Switches      0
       Block Input Operations            0
       Block Output Operations           272

编辑:要了解逻辑,请查看此数据集,然后查看日志:

data temp;
*data _null_;
set sashelp.vcolumn (keep = libname memname name type) end=eof;
where libname = 'WORK' and memname = "&dsn_in";

 if _n_=1 then
    call execute ("proc datasets lib=WORK nodetails nolist; modify &dsn_in; rename ");
 
 *create new variable name here;
 *in this example just renaming it to VAR001 - VAR###;
 *variable name is 'name';
 new_name=catt('VAR', put(_n_, z3.));
 
 *pass new and old name to proc datasets;
 call execute (name);
 call execute ('=');
 call execute (new_name);
 
 *if last record then quit;
 If eof then
    call execute (';run;quit;');
    
    
run;

宏处理器在将结果代码传递到 SAS 以 运行 之前完成对源代码的修改。因此,放置在数据步骤中间的 %LET 语句实际上会 运行 在数据步骤 运行s.

之前

要生成一系列宏变量,您可以使用 PROC SQL

proc sql noprint;
  select name into :name1- 
    from col_name
  ;
%let n_obs = &sqlobs;
quit;

或者在数据步骤中使用 CALL SYMPUTX()。

data _null_;
  if eof then call symputx('n_obs',_n_-1);
  set col_name end=eof;
  call symputx(cats('name',_n_),name);
run;