SAS 9.4基于当前值替换当前行之后的所有值

SAS 9.4基于当前值替换当前行之后的所有值,sas,next,Sas,Next,我正在根据ID号匹配文件。我需要用要匹配的ID格式化一个数据集,以便在a列中不重复相同的ID号(因为b列的ID是匹配完成后的剩余ID)。我的ID列表有超过100万个观察值,同一个ID可能在任一列/两列中重复多次 以下是我所拥有/需要的示例: 样本数据 ID1 ID2 1 2 3 4 2 5 6 1 1 7 5 8 幸存的ID将是: 2 4 5 ID1 ID2 1 2 3 4 2 5 6 5 5 7 5 8 错误-1不再存

我正在根据ID号匹配文件。我需要用要匹配的ID格式化一个数据集,以便在a列中不重复相同的ID号(因为b列的ID是匹配完成后的剩余ID)。我的ID列表有超过100万个观察值,同一个ID可能在任一列/两列中重复多次

以下是我所拥有/需要的示例:

样本数据

ID1 ID2

1 2    
3 4    
2 5
6 1 
1 7 
5 8    
幸存的ID将是:

2    
4    
5
ID1 ID2

1 2    
3 4    
2 5
6 5 
5 7 
5 8 
错误-1不再存在 错误-1不再存在 八,

我需要什么

ID1 ID2

1 2    
3 4    
2 5    
6 5
5 7
7 8
很明显,我是一名SAS新手,但以下是我尝试过的,我一遍又一遍地运行,因为我有一些ID重复了50次以上

Proc sort data=Have;    
    by ID1;    
run;
这种排序使重复的ID1值连续,因此我可以使用LAG将被破坏的ID1替换为上面一行中幸存的ID2

Data Want;
    set Have;
        by ID1;
    lagID1=LAG(ID1);  
    lagID2=LAG(ID2); 
    If NOT first. ID1 THEN DO;  
        If ID1=lagID1 THEN ID1=lagID2; 
        KEEP ID1 ID2;
        IF ID1=ID2 then delete;
   end;
run;
这样做是可行的,但我仍然会遇到一些重复的问题,无论我运行多少次都无法解决(我会循环它,但我不知道如何解决),因为它们只是在具有其他重复的ID之间来回切换(我可以得到大约2000个)

我已经明白了,我需要用ID2替换当前行之后的所有值,而不是使用LAG来替换每个ID1值,但我不知道如何做到这一点

我想阅读观察值1,在ID1或ID2列中查找ID1值的所有后续实例,并用当前观察值的ID2值替换该值。然后我想用第2行重复这个过程,以此类推

例如,我想查找值1的第1行之后的任何实例,并将其替换为2,因为这是该对的剩余ID-1可能会在任一列中出现多次,并且我需要将它们全部替换。第二行将查找后面的值3,并将其替换为4,因此为1。最终结果应该是ID号在ID1列中只出现一次(尽管它可能在ID2列中出现多次)

读取第一行后,数据集如下所示: ID1 ID2

1 2    
3 4    
2 5
6 2 
2 7 
5 8 
阅读观察2不会有任何变化,因为3不会再次出现;观察3后,设定为:

2    
4    
5
ID1 ID2

1 2    
3 4    
2 5
6 5 
5 7 
5 8 
同样,观察结果4不会有任何变化。但观察结果5将导致最终的变化:

ID1 ID2

1 2    
3 4    
2 5
6 5 
5 7 
7 8 
我尝试过使用下面的语句,但我甚至不能判断我是否完全走错了方向,或者我只是不能理解语法

Data want;
Set have;
      Do i=_n_;
          ID=ID2;
          Replace next var{EUID} where (EUID1=EUID1 AND EUID2=EUID1);
      End;
Run;

谢谢你的帮助

这里是您建议的算法的一个实现,使用
modify
语句一次加载和重写每一行。它适用于您的简单示例,但对于messier数据,您可能会在
ID1
中获得重复的值

data have;
input ID1 ID2 ;
datalines;
1 2    
3 4    
2 5
6 1 
1 7 
5 8 
;
run;

title "Before making replacements";
proc print data = have;
run;

/*Optional - should improve performance at cost of increased memory usage*/
sasfile have load;

data have;
    do i = 1 to nobs;
        do j = i to nobs;
            modify have point = j nobs = nobs;
            /* Make copies of target and replacement value for this pass */
            if j = i then do;
                id1_ = id1;
                id2_ = id2;
            end;
            else do;
                flag = 0; /* Keep track of whether we made a change */
                if id1 = id1_ then do;
                    id1 = id2_;
                    flag = 1;
                end;
                if id2 = id1_ then do;
                    id2 = id2_;
                    flag = 1;
                end;
                if flag then replace; /* Only rewrite the row if we made a change */                
            end;
        end;
    end;
    stop;
run;

sasfile have close;

title "After making replacements";
proc print data = have;
run;

请记住,由于这会修改数据集,因此在运行数据步骤时中断数据步骤可能会导致数据丢失。确保您首先有一个备份,以防您需要回滚更改。

这似乎应该可以做到,而且非常简单。让我知道您是否正在寻找:

data have;
input id1 id2;
datalines;
1 2    
3 4    
2 5
6 1 
1 7 
5 8 
;
run;

%macro test();
  proc sql noprint;
     select count(*) into: cnt
     from have;
  quit;

  %do i = 1 %to &cnt;
     proc sql noprint;
        select id1,id2 into: id1, :id2
        from have
        where monotonic() = &i;quit;

     data have;
     set have;
     if (_n_ > input("&i",8.))then do;
        if (id1 = input("&id1",8.))then id1 = input("&id2",8.);
        if (id2 = input("&id1",8.))then id2 = input("&id2",8.);
     end;
     run;        
  %end;
%mend test;
%test();

这可能会快一点:

data have2;
input id1 id2;
datalines;
1 2    
3 4    
2 5
6 1 
1 7 
5 8 
;
run;

%macro test2();
   proc sql noprint;
      select count(*) into: cnt
      from have2;
   quit;

   %do i = 1 %to &cnt;
      proc sql noprint;
         select id1,id2 into: id1, :id2
         from have2
         where monotonic() = &i;

         update have2 set id1 = &id2         
         where monotonic() > &i
         and id1 = &id1;
      quit;
      proc sql noprint;
         update have2 set id2 = &id2         
         where monotonic() > &i
         and id2 = &id1;
      quit;
%end;
%mend test2;
%test2();

不需要通过数据文件来回工作。您只需保留替换信息,就可以一次性处理文件

一种方法是使用ID变量的值作为索引创建一个临时数组。对于具有小ID值的简单示例,这很容易做到

例如,如果所有的ID值都是1到1000之间的整数,那么这一步将完成这项工作

data want ;
  set have ;
  array xx (1000) _temporary_;
  do while (not missing(xx(id1))); id1=xx(id1); end;
  do while (not missing(xx(id2))); id2=xx(id2); end;
  output;
  xx(id1)=id2;
run;
您可能需要添加一个测试来防止循环(1->2->1)

对于更通用的解决方案,您应该用哈希对象替换数组。比如说:

data want ;
  if _n_=1 then do;
    declare hash h();
    h.definekey('old');
    h.definedata('new');
    h.definedone();
    call missing(new,old);
  end;
  set have ;
  do while (not h.find(key:id1)); id1=new; end;
  do while (not h.find(key:id2)); id2=new; end;
  output;
  h.add(key: id1,data: id2);
  drop old new;
run;

你能更好地解释一下从数据中得到你想要的东西的规则是什么吗?我希望ID1的第一个实例作为ID1之后的所有值都替换为相应的第一个实例的ID2。所以在这个例子中,我在第四次观察中加入了1的值,将被替换为2(基于第一次观察),然后是5(基于第三次观察)。仍然没有意义。为什么6,1会以6,5结束?排序顺序重要吗?因为在程序中,一旦1与2匹配,这些数据就会被输入,1就会被销毁。如果以后引用了1,则会创建一个错误,因为ID 1已不存在,所以在匹配记录时它变成了2。我仍然不理解。听起来你在谈论一个类似于改变人名的系统。在第一个表中,我们看到1变成2,然后2变成5,然后5变成8。所以结果应该是1,2和5都变成了8。这就是您想要的吗?
proc sql
效率很低-如果您想要选择特定的行,请使用
obs
firstobs
数据集获取仅获取该行,而不是扫描整个表并查找匹配的行号。这到底是如何工作的?我需要用have(obs=&I firstobs=&I)从have where monotonic()=&I递增地从have(obs=&I firstobs=&I)获得每一行1到n。两者都给出相同的结果,但前者读取整个表,而后者读取一行。啊,但它不应该是obs=1吗?不-obs选项实际上意味着lastobs,但它的命名相当混乱。ID实际上是10位整数。100亿的数组可能太大,无法存储。因此,请尝试使用哈希对象实现相同的逻辑。添加使用哈希对象的示例。