我有一个数据集,其中包含一系列食品消费的信息,基本上是消费的频率,消费的数量,以及它是否是您消费的食品。此外,一些食品也有一种类型(例如,您可以使用普通苏打水或减肥苏打水)。每个食品被命名为“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.*¶meter.*&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.*¶meter.*&RootItem.Q/&gramsref.);
RUN;
%END;
%END;
%MEND WithType;
然后我将适当的宏应用于每个食物项目(例如%WithType(食物= 1))
我实际上是SAS语言的新手,这是我第二个月使用它,所以我想我可能在我的代码中做了多余或不优化的事情。任何提示将非常感谢。提前谢谢。
答案 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;
这需要少一点的数据解析。代码中有一些地方可以从中受益。
最后 - 如果您可以发布日志来运行它,可以帮助我们确定可以从优化中获益最多的部分。