【问题标题】:put values to a file using functions without creating new variables使用函数将值放入文件而不创建新变量
【发布时间】:2023-03-23 20:13:01
【问题描述】:

我正在处理一个数据集,我事先不知道它的内容。我的目标 SAS 实例是 9.3,我不能使用 SQL,因为它具有某些不能用作列名的“保留”名称(例如“用户”)。

谜题如下所示:

data _null_;

  set some.dataset;  file somefile;

  /* no problem can even apply formats */
  put name age;

  /* how to do this without making new vars? */
  put somefunc(name) max(age);

run;

我不能输入var1=somefunc(name); put var1;,因为这可能会与名为var1 的源变量发生冲突。

我猜答案是创建一些宏函数来读取数据集标题并返回一个“安全”(非冲突)变量,或者一个格式的 fcmp 函数,但我想我会检查与社区一起看看 - 是否有一些“老派”的方式直接从函数中输出数据?

【问题讨论】:

  • SAS 和 PROC SQL 允许您使用任何有效的名称。在 SQL 中,您可能需要使用名称文字“user”n,或设置 DQUOTE=ANSI 选项并使用“user”。
  • 天哪 - 可怕的“名称文字”可能是答案,可以简单地在源上强制执行 validvarname=v7 和 - 'job done'n?
  • 友情提醒,还有 NLITERAL() 函数可以将名称格式化为正确的格式。
  • 请注意,NLITERAL() 不会将 USER 转换为 'USER'n,因为它不是 SAS 的无效名称。问题在于它是 SQL 中的保留字。

标签: sas


【解决方案1】:

临时数组?

34         data _null_;
35            set sashelp.class;
36            array _n[*] _numeric_;
37            array _f[3] _temporary_;
38            put _n_ @;
39            do _n_ = 1 to dim(_f);
40               _f[_n_] = log(_n[_n_]);
41               put _f[_n_]= @;
42               end;
43            put ;
44            run;

1 _f[1]=2.6390573296 _f[2]=4.2341065046 _f[3]=4.7229532216
2 _f[1]=2.5649493575 _f[2]=4.0342406382 _f[3]=4.4308167988
3 _f[1]=2.5649493575 _f[2]=4.1789920363 _f[3]=4.5849674787
4 _f[1]=2.6390573296 _f[2]=4.1399550735 _f[3]=4.6298627986
5 _f[1]=2.6390573296 _f[2]=4.1510399059 _f[3]=4.6298627986
6 _f[1]=2.4849066498 _f[2]=4.0483006237 _f[3]=4.4188406078
7 _f[1]=2.4849066498 _f[2]=4.091005661 _f[3]=4.4367515344
8 _f[1]=2.7080502011 _f[2]=4.1351665567 _f[3]=4.7229532216
9 _f[1]=2.5649493575 _f[2]=4.1351665567 _f[3]=4.4308167988

【讨论】:

  • 想要使用这种方法。但是碰撞风险是一样的,不是吗? data have;_n1='42';_n2=42;_f1='thistoo';run;
  • 没有新的变量名。
  • 唯一的冲突风险是临时数组名和一个变量名。选择一个不太可能的数组名称就足够了,并且冲突将被记录为错误(这是一件好事)
  • 是的,你是对的,我的问题措辞不当,我不想要新变量名的原因是为了避免与现有变量发生冲突的风险。这确实回答了标题中所述的问题
【解决方案2】:

PUT 语句不接受函数调用作为输出的有效项。

DATA 步骤不执行您在max(age) 中指示的列函数(因此在PUT 中使用此类函数的可能性更小;-)

避免名称冲突

我的建议是使用不太可能发生冲突的变量名。

_temp_001 = somefunc(<var>);
_temp_002 = somefunc2(<var2>);
put _temp_001 _temp_002;

drop _temp_:;

%let tempvar = _%sysfunc(rand(uniform, 1e15),z15.);
&tempvar = somefunc(<var>);
put &tempvar;

drop &tempvar;
%symdel tempvar;

重新利用

您可以重新使用对运行步骤不重要的任何自动变量。一些无所不在的候选人包括:

  • 数值变量:
    • _n_
    • _iorc_
    • _threadid_
    • _nthreads_
    • first.&lt;any-name&gt;(仅在与 BY 语句关联的 first. 逻辑之后进行调整)
    • last.&lt;any-name&gt;
  • 字符变量:
    • _infile_(需要一个空的datalines;
    • _hostname_
  • 避免
    • _file_
    • _error_

【讨论】:

  • 我喜欢这种方法,公平地说,那里不太可能发生碰撞。
【解决方案3】:

我认为选择一些不太可能发生冲突的名称会很安全。生成这些并仍然使代码具有一定可读性的一种简单方法是仅对字符串进行散列以创建有效的 SAS varname 并使用宏引用使代码可读。像这样的:

%macro get_low_collision_varname(iSeed=);
  %local try cnt result;  
  %let cnt = 0;
  %let result = ;
  %do %while ("&result" eq "");
    %let try = %sysfunc(md5(&iSeed&cnt),hex32.);
    %if %sysfunc(anyalpha(%substr(&try,1,1))) gt 0 %then %do;
      %let result = &try;
    %end;
    %let cnt = %eval(&cnt + 1);
  %end;  
  &result
%mend;

上面的代码接受一个种子字符串,并在它的末尾添加一个数字。它迭代该数字,直到它从 md5() 函数获得一个有效的 SAS varname 作为输出。您甚至可以测试目标数据集名称以确保该变量不存在。如果它确实将该逻辑构建到上述函数中。

测试一下:

%let my_var = %get_low_collision_varname(iSeed=this shouldnt collide);    
%put &my_var;

data _null_;
  set sashelp.class;
  &my_var = 1;
  put _all_;
run;

结果:

Name=Alfred Sex=M Age=14 Height=69 Weight=112.5 C34FD80ED9E856160E59FCEBF37F00D2=1 _ERROR_=0 _N_=1
Name=Alice Sex=F Age=13 Height=56.5 Weight=84 C34FD80ED9E856160E59FCEBF37F00D2=1 _ERROR_=0 _N_=2

这并没有具体回答如何在不创建新的变量名的情况下实现它的问题,但它确实提供了一个实用的解决方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-02
    • 2021-08-27
    • 2019-11-03
    相关资源
    最近更新 更多