SAS-跨行复制多个观测值

时间:2018-11-07 10:26:24

标签: sas rows replicate

我有一个看起来像这样的数据结构:

DATA have ; 
INPUT famid indid implicate imp_inc; 
CARDS ; 
1 1 1 40000
1 1 2 25000
1 1 3 34000
1 1 4 23555
1 1 5 49850
1 2 1 1000
1 2 2 2000
1 2 3 3000
1 2 4 4000
1 2 5 5000
1 3 1 .
1 3 2 .
1 3 3 .
1 3 4 .
1 3 5 .
2 1 1 40000
2 1 2 45000
2 1 3 50000
2 1 4 34000
2 1 5 23500
2 2 1 .
2 2 2 .
2 2 3 .
2 2 4 .
2 2 5 .
2 3 1 41000
2 3 2 39000
2 3 3 24000
2 3 4 32000
2 3 5 53000
RUN ;

因此,我们为每个隐式都有家庭身份证,个人身份证,隐含号码和估算收入。

我需要的是为每个家庭中的其余个体复制每个家庭中第一个个体的结果(所有五个含义),替换我们以前在这些单元格上具有的任何值,像这样:

DATA want ; 
INPUT famid indid implicate imp_inc; 
CARDS ; 
1 1 1 40000
1 1 2 25000
1 1 3 34000
1 1 4 23555
1 1 5 49850
1 2 1 40000
1 2 2 25000
1 2 3 34000
1 2 4 23555
1 2 5 49850
1 3 1 40000
1 3 2 25000
1 3 3 34000
1 3 4 23555
1 3 5 49850
2 1 1 40000
2 1 2 45000
2 1 3 50000
2 1 4 34000
2 1 5 23500
2 2 1 40000
2 2 2 45000
2 2 3 50000
2 2 4 34000
2 2 5 23500
2 3 1 40000
2 3 2 45000
2 3 3 50000
2 3 4 34000
2 3 5 23500
RUN ;

在此示例中,我尝试仅复制一个变量,但是在我的项目中,我将不得不对许多变量进行此操作。

到目前为止,我想出了以下解决方案:

%let implist_1=imp_inc;

%macro copyv1(list);
    %let nwords=%sysfunc(countw(&list));
    %do i=1 %to &nwords;
    %let varl=%scan(&list, &i);
        proc means data=have max noprint;
            var &varl;
            by famid implicate;
            where indid=1;
            OUTPUT OUT=copy max=max_&varl;  
        run;
        data want;
            set have;
            drop &varl;
        run;
        data want (drop=_TYPE_ _FREQ_);
            merge want copy;
            by famid implicate;
            rename max_&varl=&varl;
        run;
    %end;
%mend;
%copyv1(&imp_list1);

这对于一个或两个变量非常有效。但是,一旦对大小为1.5 GB的数据集中的400个变量执行此操作,速度将非常慢。

我很确定可以使用某种形式的proc sql或first.var等来实现此目的的更快方法,但是我对SAS来说还比较陌生,到目前为止,我无法提出更好的解决方案

非常感谢您的支持。

最诚挚的问候

2 个答案:

答案 0 :(得分:0)

这很简单,只需一点SQL:

proc sql;
create table want as 
  select a.famid, a.indid, a.implicate, b.* from 
  have a 
  left join (
    select * from have 
    group by famid 
    having indid = min(indid)
  ) b 
  on
        a.famid = b.famid 
    and a.implicate = b.implicate
  order by a.famid, a.indid, a.implicate
  ;
quit;

该想法是将表连接到表的自身子集,该子集仅包含与每个家庭中的第一个个体相对应的行。

它设置为选择每个家庭中编号最小的个人,因此即使没有indid = 1的行也可以使用。如果您确定总会有这样的行,则可以使用更简单的查询:

proc sql;
create table want as 
  select a.famid, a.indid, a.implicate, b.* from 
  have(sortedby = famid) a 
  left join have(where = (indid = 1)) b 
  on
        a.famid = b.famid 
    and a.implicate = b.implicate
  order by a.famid, a.indid, a.implicate
  ;
quit;

指定sortedby = famid可以向查询优化器提供提示,表明它可以跳过联接所需的初始排序之一,这可能会提高性能。

答案 1 :(得分:0)

是的,这可以通过使用first.语句提供的by引用在DATA步骤中完成。

data want;
  set have (keep=famid indid implicate imp_inc /* other vars */);

  by famid indid implicate; /* by implicate is so step logs an error (at run-time) if data not sorted */

  if first.famid then if indid ne 1 then abort;

  array across imp_inc           /* other vars */;
  array hold [1,5] _temporary_;  /* or [<n>,5] where <n> means the number of variables in the across array */

  if indid = 1 then do;          /* hold data for 1st individuals implicate across data */
    do _n_ = 1 to dim(across);
      hold[_n_,implicate] = across[_n_];  /* store info of each implicate of first individual */
    end;
  end;
  else do;
    do _n_ = 1 to dim(across);
      across[_n_] = hold[_n_,implicate];  /* apply 1st persons info to subsequent persons */
    end;
  end;
run;

由于单次通过数据,因此DATA步骤可能会明显更快,但是在[]时计算所有那些讨厌的run;数组地址会产生内部处理成本,并且该成本可能变为对某些<n>

有影响

SQL是更简单的语法,更清晰的理解,并且在have数据集未排序或按组排序时具有特殊的排序功能。