优化SAS中的代码,重新计算基于其他变量的变量

时间:2012-08-16 13:48:58

标签: sas

我有一个数据集,其中包含一系列食品消费的信息,基本上是消费的频率,消费的数量,以及它是否是您消费的食品。此外,一些食品也有一种类型(例如,您可以使用普通苏打水或减肥苏打水)。每个食品被命名为“DIEA#”加上一个字母(频率为F,数量为Q,sazonality为S,类型为T(如果需要))。数据集看起来像这样。

ID    DIEA1F    DIEA1Q    DIEA1S    DIEA1T    DIEA2F    DIEA2Q    DIEA2S    ...
1     3         20        0         1         1         10        0         ...
2     1         50        0         2         3         30        0         ...
3     5         10        1         2         1         15        0         ...
4     8         5         0         1         2         10        1         ...
...   ...       ...       ...       ...       ...       ...       ...       ...

在另一个数据集中,我使用频率变量作为指标

获得有关每种食物的营养信息
VARF      TYPE    CALORIES  FAT       VIT.A     VIT.B     ...
DIEA1F    1       150       20        8         0         ...
DIEA1F    2       120       5         7         0         ...
DIEA2F    .       50        0         3         25        ...
DIEA3F    .       67        5         1         10        ...
...     ...       ...       ...       ...       ...    

因此,我为每种食物项目提供大约15k响应者,114种食物项目和160种营养变量(如果该项目具有不同类型,则更多)。 我需要的是根据食物消费数据计算每个人每种营养素的总消费量。 我实际上解决了这个问题,但是,我认为我的解决方案太慢了。计算大约需要5个小时。

这是我的代码:

LIBNAME DIET "C:\Nutri\diet";

DATA diet.Test;
SET diet.qf2_die_100412;
    /*here is a list of nutrition variables, and i set each one to zero (exemple CAL=0;)*/
RUN;

PROC IMPORT OUT= diet.dadosdie DATAFILE= "C:\Nutri\diet\nutrifacts.xls" 
        DBMS=xls REPLACE;
 SHEET="plan1"; 
 GETNAMES=YES;
RUN;

DATA dadosdie;
SET diet.Dadosdie;
RUN;

%MACRO cnt_list(list=);
%LET i = 1;
%DO %WHILE (%CMPRES(%SCAN(&list., &i.)) ne );
%LET item&i. = %CMPRES(%SCAN(&list., &i.));
%LET i = %EVAL((&i. + 1);
%END;
%*** STORE THE COUNT OF THE NUMBER OF ITEMS IN A MACRO VARIABLE: &CNTITEM;
%LET cntitem = %EVAL((&i. - 1);
&cntitem.
%MEND cnt_list;

%MACRO NoType(food=);

%LET RootItem=DIEA&food.;

DATA diet.test;
        set diet.test;
        if &RootItem.F = 1 then CONS_&RootItem.FPF = 3;
        if &RootItem.F = 2 then CONS_&RootItem.FPF = 2.5;
        if &RootItem.F = 3 then CONS_&RootItem.FPF = 1;
        if &RootItem.F = 4 then CONS_&RootItem.FPF = 0.8;
        if &RootItem.F = 5 then CONS_&RootItem.FPF = 0.4;
        if &RootItem.F = 6 then CONS_&RootItem.FPF = 0.1;
        if &RootItem.F = 7 then CONS_&RootItem.FPF = 0.07;
        if &RootItem.F = 8 then CONS_&RootItem.FPF = 0;
    RUN;

PROC SQL NOPRINT;

    SELECT Gramature INTO :gramature FROM dadosdie 
    WHERE VARF = "&RootItem.F" ;

    SELECT Grams_Ref INTO :gramsref FROM dadosdie 
    WHERE VARF = "&RootItem.F" ;

QUIT;

%LET listvarcomp= \* Here goes the same list of nutrition variables *\
%DO i=1 %TO %cnt_list(list=&listvarcomp.) ;
    %LET measure=%scan(&listvarcomp.,&i.);
    PROC SQL NOPRINT;
        SELECT &measure. INTO :parameter FROM dadosdie 
        WHERE VARF = "&RootItem.F" ;
    QUIT;
    DATA diet.test;
        set diet.test;
        if &RootItem.Q ne . and &RootItem.Q ne .P and &RootItem.S ne 1
        then &measure.= &measure. + (CONS_&RootItem.FPF*&gramature.*&parameter.*&RootItem.Q/&gramsref.);
    RUN;
%END;

%MEND NoType;   

%MACRO WithType(food=);

%LET RootItem=DIEA&food.;

DATA diet.test;
        set diet.test;
        if &RootItem.F = 1 then CONS_&RootItem.FPF = 3;
        if &RootItem.F = 2 then CONS_&RootItem.FPF = 2.5;
        if &RootItem.F = 3 then CONS_&RootItem.FPF = 1;
        if &RootItem.F = 4 then CONS_&RootItem.FPF = 0.8;
        if &RootItem.F = 5 then CONS_&RootItem.FPF = 0.4;
        if &RootItem.F = 6 then CONS_&RootItem.FPF = 0.1;
        if &RootItem.F = 7 then CONS_&RootItem.FPF = 0.07;
        if &RootItem.F = 8 then CONS_&RootItem.FPF = 0;
    RUN;

PROC SQL NOPRINT;

    SELECT gramature INTO :gramature FROM dadosdie 
    WHERE VARF = "&RootItem.F" ;

    SELECT Grams_Ref INTO :gramsref FROM dadosdie 
    WHERE VARF = "&RootItem.F" ;

    SELECT COUNT(*) INTO :NOBStype FROM dadosdie            
    WHERE VARF = "&RootItem.F" ;

QUIT;

%LET listvarcomp= \* Here goes the same list of nutrition variables *\

%DO j=1 %TO &NOBStype. ;

    %DO i=1 %TO %cnt_list(list=&listvarcomp.) ;
        %LET measure=%scan(&listvarcomp.,&i.);
        PROC SQL NOPRINT;
            SELECT &measure. INTO :parameter FROM dadosdie 
            WHERE VARF = "&RootItem.F" AND TYPE = &j.;
        QUIT;
        DATA diet.test;
            set diet.test;
            if &RootItem.Q ne . and &RootItem.Q ne .P and &RootItem.S ne 1 and &RootItem.T = &j.
            then &measure.= &measure. + (CONS_&RootItem.FPF*&gramature.*&parameter.*&RootItem.Q/&gramsref.);
        RUN;
    %END;
%END;

%MEND WithType; 

然后我将适当的宏应用于每个食物项目(例如%WithType(食物= 1))

我实际上是SAS语言的新手,这是我第二个月使用它,所以我想我可能在我的代码中做了多余或不优化的事情。任何提示将非常感谢。提前谢谢。

1 个答案:

答案 0 :(得分:1)

当我有更多时间时,我会尝试更新它...

我认为最好的解决方案是使用hash tables作为查找表,然后通过数据集在一个解析中处理所有数据。这可能会减少到< 1分钟。这意味着你也可以避免使用宏变量。

可替换地....

1)考虑向表中添加索引。

2)在我看来,这可能是使用SASFILE声明的候选人。它允许您将经常查询的数据集加载到内存中,以便更快地访问它们。

3)以下代码块

PROC SQL NOPRINT;

    SELECT Gramature INTO :gramature FROM dadosdie 
    WHERE VARF = "&RootItem.F" ;

    SELECT Grams_Ref INTO :gramsref FROM dadosdie 
    WHERE VARF = "&RootItem.F" ;

QUIT;

可以更改为:

PROC SQL NOPRINT;

    SELECT Gramature, Grams_Ref INTO :gramature, :gramsref FROM dadosdie 
    WHERE VARF = "&RootItem.F" ;

QUIT;

这需要少一点的数据解析。代码中有一些地方可以从中受益。

最后 - 如果您可以发布日志来运行它,可以帮助我们确定可以从优化中获益最多的部分。