用SAS宏语言

时间:2016-12-11 18:42:32

标签: lua sas

我对SAS很陌生,我很难理解如何用SAS宏语言编写数据驱动的程序。但是, proc lua 对我有意义。尽管如此,我想知道两者。

下面的代码 - 可能是愚蠢的 - 说明了我正在努力的概念。它随机列出了猫的名字,然后找出哪些猫是顽皮的,哪些是好的。然后它打印出一个圣诞节清单,让我知道哪些猫应该买礼物以及我可以花多少钱。

我在翻译/实现SAS宏语言时遇到困难的部分代码是:

1)确定第j个cat后缀的部分,然后将其打印到日志中。如何动态更改宏变量的值,然后在宏循环中使用它们来写入日志?有没有办法在宏中使用'call symput'或'symget'?

2)如何在宏循环中写入多个数据集,类似于我在下面所做的。

3)如何在sas宏中调用用 proc fcmp 编译的自定义函数来控制宏的流程。

* This macro create a list of cat names;
%macro getsomecats(num);
    %local mycat;
    %let mycat = cat1;
    %do j = 2 %to #
        %let mycat = &mycat.%str( cat)&j.;
    %end;
    &mycat
%mend;

* SAS macro that duplicates the Excel RANDBETWEEN function. Taken from 
  http://blogs.sas.com/content/iml/2015/10/05/random-integers-sas.html;
%macro RandBetween(min, max);
   (&min + floor((1+&max-&min)*rand("uniform"))) 
%mend;

* Get the number of cats that will be in our list;
data _null_;
    seed = %randbetween(1,50);
    call symputx('myseed',seed);
run;
* Make a random list of cat names;
%let morecats = %getsomecats(&myseed.);

* Reference some custom functions compiled with proc fcmp;
options cmplib=(MY_FUNCS.PURE_MATH);
libname NUMBERS '/folders/myfolders';

* Make two data sets: one for all of my cats, and another for
  the cats that I should by Christmas presents;
proc lua;
submit;
    -- Import Lua libraries
    require 'string'
    require 'math'

    -- If the tables I want to create exist, then delete them.
    if sas.exists('my_cats') then
        sas.submit([[
            proc delete data=my_cats;
        ]])
        print('my_cats deleted')
    end

    if sas.exists('xmas_list') then
        sas.submit([[
            proc delete data=xmas_list;
        ]])
        print('xmas_list deleted')
    end

    -- Set up some data sets
    sas.new_table('my_cats', {
        {name='Name', type='C', length=8},
        {name='Status', type='C', length=8},
        {name='Gender', type='C', length=6}
    })
    sas.new_table('xmas_list', {
        {name='Name', type='C', length=8},
        {name='Status', type='C', length=8},
        {name='Gender', type='C', length=6},
        {name='Budget', type='N', length=8}
    })

    -- Create data set handels for our new data set 
    local dsh1 = sas.open('my_cats', 'u')
    local dsh2 = sas.open('xmas_list', 'u')

    -- Declare some useful variables
    local suffix, status, gender, name
    local ub = 1 -- upper bound for 'for' loop
    local mystr = sas.symget("morecats")

    -- Find out upper bound on number of cats
    for j = 1, string.len(mystr) do
        if mystr:sub(j,j) == ' ' then ub = ub + 1 end
    end
    mystr = nil -- we do not need mystr anymore

    print('Making my christmas list:') -- Write header in log
    for j = 1, ub do
        -- Create a suffix for jth cat; I am very confused about
        -- how to do this in the SAS macro language.
        if j % 10 == 1 and j % 100 ~= 11 then suffix = 'st'
        elseif j % 10 == 2 and j % 100 ~= 12 then suffix = 'nd'
        elseif j % 10 == 3 and j % 100 ~= 13 then suffix = 'rd'
        else suffix = 'th' end


    -- Find out if the jth cat has been naughty or nice.
    -- 'isprime' is a custom function compiled with proc fcmp,
    -- it returns 1 if a number is prime and 0 if it is composite.
    if sas.isprime(j) == 1 then
        status = 'naughty'
    else
        status = 'nice'
    end

    -- Assign the cat a gender randomly. I would like to 
    -- know how to this in the SAS macro language, including
    -- how to use a list so that I can reference the two different
    -- charchteristics of gender.
    if sas.ranuni(0) < .5 then 
        gender = {'male', 'he'}
    else
        gender = {'female', 'she'}
    end

    -- Get the cats name; scan the macro variable
    -- 'morecats' for the jth entry.
    name =sas.scan(sas.symget("morecats"),j)

    -- Write information in our log about this cat,
    -- again, I cannot figure out how to deal with the
    -- suffix part here.
    print('My '..j..suffix.." cat's name is "..name..
        ', and '..gender[2]..' is usually '..status)

    -- Add the jth cat to my data set of cats
    sas.append(dsh1)
    sas.put_value(dsh1,"Name", name)
    sas.put_value(dsh1,"Status", status)
    sas.put_value(dsh1,"Gender", gender[1])
    sas.update(dsh1)

    -- If the jth cat is usually nice then, add him or her
    -- to the data set of cats that need to by Christmas
    -- presents for.
    if status == 'nice' then 
        local budget = 10 * sas.phi(math.random(30))
        sas.append(dsh2)
        sas.put_value(dsh2,"Name", name)
        sas.put_value(dsh2,"Status", status)
        sas.put_value(dsh2,"Gender", gender[1])
        sas.put_value(dsh2,"Budget", budget)
        sas.update(dsh2)
    end             
end
sas.close(dsh1)
sas.close(dsh2)
endsubmit;
run;

proc print data=xmas_list;
     var _all_;
     sum Budget;
run;

示例输出:

enter image description here

示例日志:

 Making my christmas list:
 My 1st cat's name is cat1, and she is usually nice
 My 2nd cat's name is cat2, and he is usually naughty
 My 3rd cat's name is cat3, and she is usually naughty
 My 4th cat's name is cat4, and she is usually nice
 My 5th cat's name is cat5, and she is usually naughty
 My 6th cat's name is cat6, and he is usually nice
 My 7th cat's name is cat7, and she is usually naughty
 My 8th cat's name is cat8, and she is usually nice

3 个答案:

答案 0 :(得分:1)

  

1)确定第j个猫后缀的部分,然后   将其打印到日志中。如何更改宏变量的值   在运行中然后在宏循环中使用它们来写东西   日志?有没有办法使用&#39;来电咨询&#39;或者&#39; symget&#39;在一个宏?

有一些宏语句如%PUT用于向日志显示值。如果要在宏代码中执行循环,请使用%DO语句。

%do i=1 %to 5 ;
  %put I=&i ;
%end;

您可以使用%LET分配宏变量值。

%let cat1=Fluffy;
%let cat2=Tiger ;

您可以从宏值构建宏变量引用。当您使用&&时,它将被&替换,并触发宏处理器再次传递以解析引用。

%let i=2 ;
%put Cat &i is &&cat&i ;
  

2)如何在宏循环中写入多个数据集,   类似于我在下面所做的。

您不能使用宏代码写入数据集。您可以使用宏代码生成可以写入数据集的SAS语句。并且您可以在不使用任何代码生成的情况下写入多个数据集。

data good bad;
  set cats;
  if status='nice' then output good;
  else output bad;
run;
  

3)如何在sas中调用使用proc fcmp编译的自定义函数   宏控制宏的流程。

不确定这个,但为什么不只是有一个数据步骤来调用该函数?

data _null_;
  call symputx('mvar',myfunc());
run;
%if (&mvar = 1) %then %do ;
  ...
%end;

答案 1 :(得分:1)

我将 proc lua 程序重新编写为数据步骤,正如Tom建议的那样,并且运行得更快。我能够通过创建一个包含我想要放入日志的消息的字符串来实现所需的日志,然后将其传递给put函数;之前,当我尝试过这样的事情时,我试图在put语句中构造字符串,这会返回错误。

echo $navbarCode;

答案 2 :(得分:0)

您可以通过将luaproc lua中取出来实现。只要文件扩展名为%inc,就可以直接在宏中使用.lua lua代码。 mv_webout框架的sasjs宏采用了这种方法,以读取从javascript发送的数据。

这是一个简短的例子。

%macro my_lua();
  filename mylua "%sysfunc(pathname(work))/some.lua";
  data _null_;
    file mylua;
    put 'sas.submit([[';
    put '    proc print data=sashelp.class;run;';
    put ']])';
    put 'print("some lua got in my macro code")';    
  run;
  %inc mylua;
%mend;
%my_lua()