【问题标题】:SAS name error when using a variable from a looping macro inside a hash dataset statement在哈希数据集语句中使用循环宏中的变量时出现 SAS 名称错误
【发布时间】:2014-06-05 08:38:05
【问题描述】:

关于如何修改下面的问题代码行以正确命名我的数据集的任何建议?

我有一个数据集,我想将处理公司 (4400) 与 48 个行业和 14 年的大约 100,000 家控制公司进行匹配,然后是最接近的规模而不进行替换。我下面的方法可能有点笨拙,但我正在学习。我将处理和控制数据集分成 48x14 组,(之后我将尝试通过某种类型的循环运行最接近的匹配而无需替换代码)。

我已经使用以下哈希码的变体来制作 48 个数据集 out1...out48。现在,我正在尝试通过使用以下代码将这 48 个数据集中的每一个进一步细分为 14 年。我收到一条错误数据集创建行,我正在尝试创建名为 out12004、out22004、out32004 的 48x14 数据集。 . .out482013

问题代码行(大约是下面代码的 2/3):hh.output (dataset: 'out'||&i||put(year, best.-L)) ;

SAS 错误如下所示:

错误:值 OUT 12004 不是有效的 SAS 名称。

错误:“DATASTEP.HASH”的实例方法 OM_OUTPUT(505) 期间发生错误。

这是完整的代码(修改自 Is it possible to loop over SAS datasets?SUGI30 paper 236-30

 %MACRO process_datasets(mdataset);
      data &mdataset.;
      set &mdataset.;
        data _null_ ;
        dcl hash hoh (ordered: 'a') ;
        dcl hiter hih ('hoh' ) ;
        hoh.definekey ('year' ) ;
        hoh.definedata ('year', 'hh' ) ;
        hoh.definedone () ;
        dcl hash hh () ;
        do _n_ = 1 by 1 until ( eof ) ;
        set out&i. end = eof ;
        if hoh.find () ne 0 then do ;
        hh = _new_ hash (ordered: 'a') ;
        hh.definekey ('industry','cik', 'year', '_n_') ;
        hh.definedata ('industry','cik','year','eventdat', 'at', 'roa') ;
        hh.definedone () ;
        hoh.replace () ;
        end ;
        hh.replace() ;
        end ;
        do rc = hih.next () by 0 while ( rc = 0 ) ;
        hh.output (dataset: '_'||&i||put(year, best.-L)) ;
        rc = hih.next() ;
        end ;
        stop ;
     run;

     data _null_;
       file 'tmp.csv' mod dsd dlm=',';  *saving everything to the same file;
       set &mdataset.;
       put (_all_) (+0);
     run;

%MEND process_datasets;

%MACRO loop_through_all;
    %DO i = 1 %to 48;
       %process_datasets(out&i.);
     %END;
%MEND loop_through_all;

【问题讨论】:

    标签: hash dataset sas nested-loops


    【解决方案1】:

    首先,关于另一个答案中的技术点的一些注释 - 即“问题不是直接来自哪里,尽管两者都是编码不佳的示例。”

    &i 确实可以在这里访问,尽管我建议以你的方式使用它是一种糟糕的风格。内部宏中依赖的宏变量应该定义为宏参数;这清楚地表明了他们来自哪里。但是,从技术上讲,这并没有错,请参见:

    %macro caller;
    %do i=1 %to 5;
      %called;
    %end;
    %mend;
    %macro called;
    %put &i;
    %mend called;
    
    %caller;
    

    但是,最好将 i 设为参数,例如 %macro called(i=);,以使您的宏更清晰,更可重用。

    其次,缺少引号实际上不是直接问题,尽管它确实指出了一个问题并且在某种程度上是一种解决方案。 SAS 确实将其中的数字转换为字符值——否则你会得到一个非常不同的错误消息;但是,这样做的方式没有帮助。你所做的最相似的实现是在它周围添加compress。那是因为问题在于 SAS 如何将数字转换为文本。 &i 是一个数字(在您的示例中为 1)。它需要转换为"1",而是使用best12. 转换为" 1"。这是个问题。

    hh.output (dataset: compress('_'||&i||put(year, best.-L))) ;
    

    这行得通。更好的实现是有意转换为字符值。宏参数很容易转换:只​​需在它们周围添加" "

    hh.output(dataset: cats('_',"&i.",year);
    

    cats 删除所有空格,并使-L 不必要。它与&i 一样有效,尽管添加引号肯定更好。

    我要补充一点,您可能会考虑为什么要对这些进行子集化。我认为这样做在概念上没有任何问题,但如果你按年做某事,你可以使用by year 并且不将它们子集化——将它们保存在每个治疗组的一个数据集中(甚至可能by group year?)。此外,您可以通过更少的步骤来完成此操作。最后你打算怎么办?假设每个组/年都有一个数据集。然后你会运行什么代码?可能您可以在一个或几个步骤中编写它而无需分解 48x14 数据集,这可能效率不高。如果您有兴趣找出答案,请先提出一个新问题,详细说明您希望仅使用一对数据集做什么。

    【讨论】:

    • 我用过猫('_',"&i.",year);这解决了它 - 非常感谢。我同意我的方法可能有点麻烦,但考虑到我接下来使用的宏,我仍然认为我需要单独的数据集。
    【解决方案2】:

    解决您关于输出行的具体问题:-

    hh.output (dataset: '_'||&i||put(year, best.-L)) ;
    

    一些注意事项:-

    • 必须引用数据集的参数,例如..(dataset: "Out123")。目前,您的不是。
    • 主宏无权访问宏变量&i。这是在调用宏中创建的,并且仅在该范围内可用。您可以向主宏添加另一个参数并以这种方式发送&i

    事实上,您创建的代码看起来很难维护;宏中的哈希是调试的噩梦。如果您正在生成创建大量输出数据集的代码,这些数据集应该会敲响警钟; SAS 中的by 语句允许将标准应用于数据集中的不同组,并且绝对优于许多数据集。

    让我们来看看这个问题:您要使用每个治疗产生多个控制组的变量来匹配控制与治疗?然后,您会根据一些距离标准为每个治疗选择一个对照吗?

    对于第一部分,Proc SQL 合并听起来很正确。你会得到一个很长的数据集,每个治疗公司重复它与对照匹配的次数。然后按treatment descending [distance criteria] 排序并选择by 组中的第一个。应该就是这样。当然,我确定我误会了什么……

    最后一点;您可以只在 SAS 中寻找匹配算法,特别是“最佳匹配”或“贪婪匹配”。根据我的经验,SAS 不适合匹配,尤其是当它涉及随机元素时(你的没有),但你应该能够找到比你现在正在使用的代码更有用的代码。

    【讨论】:

    • 你的两个要点都不正确。输出的问题与缺少引号无关,至少不是直接的。此外,&i。有明确的定义;因此是 2014 年之前的 1。我不知道之后的文字 - 可能是合理的建议 - 但技术点是错误的。
    【解决方案3】:

    我认为您可以通过在 set 语句中使用通配符而不是使用多个 set 语句来“循环它们”来大大简化您的上述代码。

    例如,下面的代码将遍历所有以前缀之一开头的数据集,这样您就可以在没有多个 set 语句的情况下处理它们。

    data all;
      set out1:
          out2:
          out3:
          out4:
          ;
    run;
    

    这甚至可以让您不再需要宏,从而简化代码。现有代码看起来很难维护/调试,所以我认为简化它是第一步。

    【讨论】:

    • 我想你误解了他在做什么。 (诚​​然,我自己只有一个模糊的想法。)他不是将多个数据集放在一起,而是从其他(更少)数据集制作多个数据集。
    • 在那种情况下,我确实误解了......我刚刚看到带有动态数据集名称的 set 语句被 DO 循环递增并做出了这个假设。
    • 我实际上不明白数据(东西)中的意义。我不明白为什么它不是data _null_。但谁知道...
    • 可以用于调试吗?我特意指的是代码第 12 行的 set 语句。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    • 2017-10-31
    相关资源
    最近更新 更多