0

我有一个数据集,其中包含有关一系列食品消费的信息,基本上是消费频率、消费数量以及它是否是您按时消费的食品。此外,一些食品也有一种类型(例如,您食用普通苏打水或无糖苏打水)。每个食品项目都被命名为“DIEA#”加上一个字母(F 表示频率,Q 表示数量,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(food=1))

我实际上对 SAS 语言相当陌生,这是我使用它的第二个月,所以我想我可能在我的代码中做了一些多余或不是最优的事情。任何提示将非常感谢。提前致谢。

4

1 回答 1

1

这是部分答案,我会在有更多时间时尝试更新它...

我认为最好的解决方案是使用哈希表作为查找表,然后通过数据集在一次解析中处理所有数据。这可能会将时间减少到 < 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;

这需要对数据进行 1 次解析。代码中有几个地方可以从中受益。

最后 - 如果您可以发布运行 1 种食品的日志,这可能有助于我们确定最能从优化中受益的部分。

于 2012-08-16T16:48:22.250 回答