查找每行的最近三个数据年

时间:2015-08-09 22:34:08

标签: sas sas-macro

我有一个数据集,每个国家/地区有一行,100列(10个变量,每个数据年数为10年)。

对于每个变量,我试图为每个国家(可能不是连续的)的变量创建一个新的数据集,其中包含最近三年的数据年份。

这是我到目前为止所做的,但我知道它的错误是因为嵌套循环,并且recent1 recent2 recent3具有相同的值但是我还没弄明白如何创建recent1 recent2 recent3没有两个循环。

%macro test();  
   data Maternal_care_recent; 
   set wb;
   keep country MATERNAL_CARE_2004 -- MATERNAL_CARE_2013 recent_1 recent_2 recent_3;
 %let rc = 1;
    %do i = 2013 %to 2004 %by -1;
     %do rc = 1 %to 3 %by 1;
        %if MATERNAL_CARE_&i. ne . %then %do;
            recent_&rc. = MATERNAL_CARE_&i.;
        %end;
    %end;
%end; run; %mend; %test();

4 个答案:

答案 0 :(得分:3)

您不需要使用宏来执行此操作 - 只需要一些数组:

data Maternal_care_recent; 
   set wb;
   keep country MATERNAL_CARE_2004-MATERNAL_CARE_2013 recent_1 recent_2 recent_3;
   array mc {*} MATERNAL_CARE_2004-MATERNAL_CARE_2013;
   array recent {*} recent1-recent3;
    do i = 2013 to 2004 by -1;
     do rc = 1 to 3 by 1;
        if mc[i] ne . then do;
            recent[rc] = mc[i];
        end;
    end;
run;

答案 1 :(得分:0)

也许我没有收到您的请求,但根据您的说明: "对于每个变量,我试图为每个国家(可能不是连续的)变量的三个最近数据年创建一个新的数据集。我用dt1和dt2以及2个位置创建了这个样本数据集。

输出将是2个数据集(通常是以DT开头的变量的数量),名为DS1和DS2,每个国家有3个观测值,第一个用于第一个变量,第二个用于第二个变量。

这是样本数据集:

data sample_ds;
length city $10 dt1 dt2 8.;
infile datalines dlm=',';
input city $ dt1 dt2;
datalines;
MS,5,0
MS,3,9
MS,3,9
MS,2,0
MS,1,8
MS,1,7
CA,6,1
CA,6,.
CA,6,.
CA,2,8
CA,1,5
CA,0,4
;

这是示例宏:

%macro help(ds=);

data vars(keep=dt:); set &ds; if _n_ not >0; run;
%let op = %sysfunc(open(vars));
%let nvrs = %sysfunc(attrn(&op,nvars));
%let cl = %sysfunc(close(&op));

%do idx=1 %to &nvrs.;
proc sort data=&ds(keep=city dt&idx.) out=ds&idx.(where=(dt&idx. ne .)) nodupkey; by city DESCENDING dt&idx.; run;
data ds&idx.; set ds&idx.;
retain cnt;
by city DESCENDING dt&idx.;
if first.city then cnt=0; else cnt=cnt+1;
run;
data ds&idx.(drop=cnt); set ds&idx.(where=(cnt<3)); rename dt&idx.=act&idx.; run;
%end;

%mend;

您将使用以下命令运行此宏:

%help(ds=sample_ds);

在宏的第一个语句中,我选择了我想要迭代的变量:

data vars(keep=dt:); set &ds; if _n_ not >0; run;

如果你想让你的代码工作,或者只是将你的变量重命名为DT1 DT2 ......

让我知道你是否正确。

答案 2 :(得分:0)

编写宏代码时,请始终牢记在何时需要执行的操作。 SAS逐步处理代码。

  • 在编译sas代码之前,解析宏变量并执行宏代码
  • 然后编译生成的SAS Base代码
  • 最后执行代码。

当您编写%if MATERNAL_CARE_&i. ne . %then %do时,这是在编译之前解释的宏代码。 那时MATERNAL_CARE_&i.不是变量,而是包含宏变量的文本字符串。 第一次运行%do i = 2013 %to 2004 by -1时,它会以MATERNAL_CARE_2013填充,第二次填充为MATERNAL_CARE_2012.,等等。 然后解释宏%if语句,并且由于文本字符串MATERNAL_CARE_1不等于点,因此将其评估为FALSE 并且recent_&rc. = MATERNAL_CARE_&i.未包含在要传递给编译器的代码中。 如果您使用 option mprint;

运行代码,则可以看到

决议;

options mprint;
%macro test();  
    data Maternal_care_recent; 
        set wb;
        keep country MATERNAL_CARE_: recent_:;
        ** The : acts as a wild card here **;

        %do i = 2013 %to 2004 %by -1;
            if MATERNAL_CARE_&i. ne . then do;
                %do rc = 1 %to 3 %by 1;
                    recent_&rc. = MATERNAL_CARE_&i.;
                %end;
            end;
        %end; 
    run; 
%mend; 
%test();

现在,在编译if MATERNAL_CARE_&i. ne . then do之前,只有&i.被评估,if MATERNAL_CARE_2013 ne . then do被传递给编译器。 如果SAS变量MATERNAL_CARE_1缺少值,编译器会将此视为测试,这正是您想要的;

<强>注: 我把if语句移到了``之上并不重要。它的效率更高,因为这种情况的评估频率较低。 但是,您必须使用%if以及%do%end if关闭doend s ;

<强>注: 您不需要%let rc = 1,因为%do rc = 1 to 3已初始化&amp; rc .;

为了完整性,SAS逐步编译: 下一个 PROC 数据步骤及其宏代码仅在执行preveous时才考虑。 这就是为什么你可以从数据步骤或sql select into中编写宏变量来影响你在下一步中编译的代码, 有些你不能用例如C ++预编译;

答案 3 :(得分:0)

谢谢大家。找到了几个解决方案的混合解决方案。

data sample_ds;
infile datalines dlm=',';
input country $ maternal_2004 maternal_2005
maternal_2006 maternal_2007 maternal_2008 maternal_2009 maternal_2010        maternal_2011 maternal_2012 maternal_2013;
datalines;
MS,5,0,5,0,5,.,5,.,5,.
MW,3,9,5,0,5,0,5,.,5,0
WE,3,9,5,0,5,.,.,.,.,0
HU,2,0,5,.,5,.,5,0,5,0
MI,1,8,5,0,5,0,5,.,5,0
HJ,1,7,5,0,5,0,.,0,.,0
CJ,6,1,5,0,5,0,5,0,5,0
CN,6,1,.,5,0,5,0,5,0,5
CE,6,5,0,5,0,.,0,5,.,8
CT,2,5,0,5,0,5,0,5,0,9
CW,1,5,0,5,0,5,.,.,0,7
CH,0,5,0,5,0,.,0,.,0,5
;


%macro test(var);
data &var._recent; 
set sample_ds;
keep country  &var._1 &var._2 &var._3;
array mc {*} &var._2004-&var._2013;
array recent {*} &var._1-&var._25;
count=1;
do i = 10 to 1 by -1;

    if mc[i] ne . then do;
        recent[count] = mc[i];
        count=count+1;
    end;
end; 
run;
%mend;