【问题标题】:Reset a temporary array in SAS重置 SAS 中的临时阵列
【发布时间】:2017-11-27 11:42:51
【问题描述】:

声明一个数组后,我想为其余代码重置它的值。

array cutoffs[4] _temporary_ (1 2 3 4); /*works well*/
... use of the array
array cutoffs[3] _temporary_ (3.5 5 7.5); /*Error*/
... use of the updated array

错误如下:

错误 124-185:变量 cutoffs 已定义。

这个错误很清楚,但我想知道如何在不更改名称的情况下重新分配数组(这将是最乏味的)。

我尝试了一些语法但我自己找不到,我在 google 和 stackoverflow 上都没有看到资源。

我该怎么做?

编辑:主要目的是我创建了一个函数(使用proc fcmp),它将数组作为参数并剪切值(如R的cut函数)。该函数将用于很多列但具有不同的截止值,我不想为每一列都创建一个乏味的数组。

【问题讨论】:

  • 来自 R 和其他语言,这令人惊讶地难以发现。
  • “为每一列繁琐地创建一个数组”,因为您每次在数据步骤中都有一个array cutoffs[x] _temporary_ (values) 行硬编码。使用不同的名称并不会让它变得更乏味。
  • 是的。我可以复制粘贴数组声明和函数,只更改数组和列内的值以应用函数,但现在我每次都必须更改数组的名称。我并不是说我使用最好的方法,因为我是一个不到一个月前开始 SAS 的菜鸟,但我可以向你保证,这非常乏味。
  • 我编辑了我的答案以提出另一种方法。
  • 不确定 FCMP 是否是解决此问题的正确方法。您没有在此处发布它,但您之前询问过您的截止功能。为这个应用程序创建一个宏可能会更容易。或者提前将截止标准设置为格式,并在代码中使用格式而不是值列表。

标签: arrays sas


【解决方案1】:

这是您的 FCMP 函数的宏版本:

%macro cut2string(var,cutoffs,values);
%if &var. lt %scan(&cutoffs.,1,%str( )) %then "%scan(&values.,1,%str( ))";
%else %if &var. ge %scan(&cutoffs.,-1,%str( )) %then "%scan(&values.,-1,%str( ))";
%else %do i=1 %to %sysfunc(countw(&cutoffs.,%str( )));
    %if &var. ge %scan(&cutoffs.,&i.,%str( )) and &var. lt %scan(&cutoffs.,%eval(&i.+1),%str( )) %then "%scan(&values.,%eval(&i.+1),%str( ))";
%end;
%mend;

下面是您如何称呼它,使用与您在链接页面中使用的示例相同的示例:

data Work.nonsales2;
    /*set Work.nonsales;*/
    salary_string  = %cut2string(30000, 20000 100000 500000, <20k 20k-100k 100k-500k >500k);
run;

您可以使用关键字参数而不是位置来使您的调用更清晰:

%macro cut2string(var=,cutoffs=,values=);
...
salary_string  = %cut2string(var=30000,cutoffs=20000 100000 500000,values=<20k 20k-100k 100k-500k >500k);

但是现在我看到了代码,这应该是 SAS 中的一种格式:

proc format;
  values cutoffs
    low-<20000='<20k'
    20000-<100000='20k-100k'
    100000-<500000='100k-500k'
    500000-high='>500k'
    ;
run;
data work.nonsales2
  salarystrings=put(30000,cutoffs.);
run;

【讨论】:

  • 非常感谢,通过阅读我学到了很多东西。太糟糕了,这不是我的问题,因为你应该得到一个公认的答案!
  • 不确定如何使用该宏来剪切数据集变量的值。我用var=salary 调用它来传递变量的名称,它会将字符串salary 与截止值进行比较,而不是比较薪水的值。
  • 非常正确。这个宏的工作原理与它所取自的帖子一样。
【解决方案2】:

您可以一一更改cutoffs数组的值。

array cutoffs{4} _temporary_ (1 2 3 4); /*works well*/
... use of the array
cutoffs[1]=3.5;
cutoffs{2}=5;
cutoffs{3}=7.5;
cutoffs{4}=.;

或者您可以第二次为数组使用另一个名称。

话虽如此,你使用它的方式似乎有点奇怪。

编辑:您可以考虑重写您的 proc fcmp 函数以期望值列表作为字符串(例如 '3.5,5,7.5')而不是数组并完全取消数组。

你的 proc fcmp 会从类似的东西改变

do i=1 to dim(array);
  val=array{i};
  ...
end;

类似的东西;

do i=1 to countw(array,',');
  val=input(scan(array,i,','),best32.);
  ...
end;

【讨论】:

  • 但是dim(cutoff)也会改变吗?顺便说一句,在我知道的所有其他编程语言中,这并不奇怪,我会编辑我的帖子来解释一下。
  • 不,尺寸不会改变。如果 "this" 你的意思是 重用变量名 那么不,这并不奇怪。但是在数据步骤中定义一个 SAS 数组,然后在同一次迭代中,用其他值和另一个维度再次定义它奇怪的。
  • 不要使用 FCMP。而是创建一个宏。
  • @Tom 你是告诉我还是 OP?我也会使用宏来执行此操作,但由于 OP 说他是 SAS 新手并且他已经关闭了 FCMP,所以我在回答中坚持使用 FCMP。
  • 我尝试使用宏但没有成功,因为它没有返回值。当您来自 R 时,SAS 非常令人沮丧,所有这些都非常容易且有据可查。
【解决方案3】:

为什么不使用宏而不是函数?

%macro cut(invar,outvar,cutoffs,categories,dlm=%str( ));
/*
"CUT" continuous variable into categories
by generating SELECT code that can be used in
a data step.

The list of CATEGORIES must have one more entry that the list of CUTOFFS
*/
%local i ;
select ;
%do i=1 %to %sysfunc(countw(&cutoffs,&dlm));
  when (&invar <= %scan(&cutoffs,&i,&dlm)) &outvar=%scan(&categories,&i,&dlm) ;
%end;
  otherwise &outvar= %scan(&categories,-1,&dlm);
end;
%mend ;

这是一个创建数字和字符输出变量的示例。对于字符变量,要么在使用宏之前定义变量,要么确保第一个类别的值对于所有值都足够长。

让我们测试一下。

data test ;
  input x @@;
  %cut(invar=x,outvar=y,cutoffs=3.5 5 7,categories=1 2 3 4)
  %cut(invar=x,outvar=z,cutoffs=3.5|5|7,categories="One  "|"Two"|"Three"|"Four",dlm=|)
cards;
2 3.5 4 5 6 7.4 8
;

如果您打开 MPRINT 选项,您可以在 SAS 日志中看到生成的代码。

2275    %cut(invar=x,outvar=y,cutoffs=3.5 5 7,categories=1 2 3 4)
MPRINT(CUT):   select ;
MPRINT(CUT):   when (x <= 3.5) y=1 ;
MPRINT(CUT):   when (x <= 5) y=2 ;
MPRINT(CUT):   when (x <= 7) y=3 ;
MPRINT(CUT):   otherwise y= 4;
MPRINT(CUT):   end;
2276    %cut(invar=x,outvar=z,cutoffs=3.5|5|7,categories="One  "|"Two  "|"Three"|"Four ",dlm=|)
MPRINT(CUT):   select ;
MPRINT(CUT):   when (x <= 3.5) z="One  " ;
MPRINT(CUT):   when (x <= 5) z="Two" ;
MPRINT(CUT):   when (x <= 7) z="Three" ;
MPRINT(CUT):   otherwise z= "Four";
MPRINT(CUT):   end;

结果

Obs     x     y      z

 1     2.0    1    One
 2     3.5    1    One
 3     4.0    2    Two
 4     5.0    2    Two
 5     6.0    3    Three
 6     7.4    4    Four
 7     8.0    4    Four

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-29
    • 2013-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多