如何在SAS数据集中查找两个变量并更新值

如何在SAS数据集中查找两个变量并更新值,sas,lookup,Sas,Lookup,我有一个数据集,其中有两个变量,即ID和Telephone no,我想查找Telephone no,并在缺少ID的地方更新相同的变量,例如: 在所附的示例中,ID A和B在电话列中缺少值,我想在可用的地方选择相同的值,并在不可用的地方更新 日期访客ID电话号码 2016年3月1日100003634_4152228768 2016年3月1日1000094865_1269576832 2016年3月1日1000103735_1035466360 2016年3月1日1000103735_1035466

我有一个数据集,其中有两个变量,即ID和Telephone no,我想查找Telephone no,并在缺少ID的地方更新相同的变量,例如:

在所附的示例中,ID A和B在电话列中缺少值,我想在可用的地方选择相同的值,并在不可用的地方更新

日期访客ID电话号码
2016年3月1日100003634_4152228768
2016年3月1日1000094865_1269576832
2016年3月1日1000103735_1035466360 2016年3月1日1000103735_1035466360 fda6a5563867eeebf19fb3 2016年3月1日1000108145_3760680616
2016年3月1日1000123010_2631619556
2016年3月1日1000123010_2631619556 fda6a75c3765e0e8f797b4 2016年3月1日1000126547974397207 2016年3月1日1000126592_2744218771
2016年3月1日1000137177_3054387520
2016年3月1日1000137208\U 498258799
2016年3月1日1000137208\U 498258799 fda6a5563660e0ebf295b3 2016年3月1日1000137460_2624495603 2016年3月1日1000137460_2624495603 FDA6583763EAF29EBA 2016年3月1日1000151867_3243977925
2016年3月1日1000151867_3243977925 fda6a15a3f63eaedfb94b3 2016年3月1日1000166048_3215927260
2016年3月1日1000174960_357067493
2016年3月1日1000178443_623552771
2016年3月1日1000183569_27289541999
2016年3月1日1000220805_3781532691
2016年3月1日1000220805_3781532691 fda6aa5c3a64e0ebfb96b0


这里有一个解决方案-它涉及哈希表和键联接-您可能以前没有见过这些。哈希表只是一个保存在内存中的表,您可以轻松访问它。键联接非常适合您在此处尝试执行的操作,您可以使用它们查找索引并更新现有数据集中的字段

data telephone_nos;
 length id $1. telephone $2;
 id = "a"; telephone = ""; output;
 id = "b"; telephone = ""; output;
 id = "c"; telephone = ""; output;
 id = "b"; telephone = "13"; output;
 id = "a"; telephone = "12"; output;
 id = "e"; telephone = ""; output;
 id = "d"; telephone = ""; output;
 id = "c"; telephone = ""; output;
 id = "a"; telephone = ""; output;
run;


/* Create a telephone number lookup table that is deduped and indexed by id*/
data lookup_telephone_nos (drop = rc index = (id));
 /*create a hash table with a lookup id*/
 declare hash dedupe();
 dedupe.definekey('id');
 dedupe.definedone();
  do while (not e);
   /*Only read in data with telephone numbers*/
   set telephone_nos (keep = id telephone
                      where = (telephone ne "")) end = e;
   /*Check to see if you have already seen this telephone number*/
   rc=dedupe.check();
   /*If you haven't add it to the hash table and output it*/
   if rc ne 0 then do;
    rc=dedupe.add(); 
    output;              
   end;
  end;
  /*Remove the hash table*/
  dedupe.delete();
  stop;
 run;

/*If you don't have enough memory to use hash tables to dedupe - then create
  the above table without deduping (see below). This may take up more    
  physical disc space, but the key join will still work as it will pick up   
  the first instance that matches*/

/*
data lookup_telephone_nos (drop = rc index = (id));
 set telephone_nos (keep = id telephone
                    where = (telephone ne ""));
run;
*/

 /*Use a key join to fill in the missing telephone numbers*/
 data telephone_nos;
  set telephone_nos;
  /*Use a key join to fill in the missing telephone numbers*/
  set lookup_telephone_nos key = id / unique;

  /* _iorc_ will be 0 if a match is found, if no match is found and error will be written to the log, therefore
     If no matches are found (e.g. the b and c examples) then make sure that these do not cause errors*/
  if _iorc_ ne 0 then _ERROR_ = 0;
 run;

有一个简单的解决方案需要两个简单的步骤

data temp;
   input Date $ Visitor_ID $ Telephone_number $30.;
   datalines;
1-Mar-16    1000003634_4152228768 .
1-Mar-16    1000094865_1269576832 .
1-Mar-16    1000103735_1035466360 .
1-Mar-16    1000103735_1035466360   fda6a5563867eeebf19fb3
1-Mar-16    1000108145_3760680616 .
1-Mar-16    1000123010_2631619556 .
1-Mar-16    1000123010_2631619556   fda6a75c3765e0e8f797b4
1-Mar-16    1000126547_974397207 .
1-Mar-16    1000126592_2744218771 .
1-Mar-16    1000137177_3054387520 .
1-Mar-16    1000137208_498258799 .
1-Mar-16    1000137208_498258799    fda6a5563660e0ebf295b3
1-Mar-16    1000137460_2624495603 .
1-Mar-16    1000137460_2624495603   fda6a6583763eaeaf29eba
1-Mar-16    1000151867_3243977925 .
1-Mar-16    1000151867_3243977925   fda6a15a3f63eaedfb94b3
1-Mar-16    1000166048_3215927260 .
1-Mar-16    1000174960_357067493 .
1-Mar-16    1000178443_623552771 .
1-Mar-16    1000183569_2728954199 .
1-Mar-16    1000220805_3781532691 .
1-Mar-16    1000220805_3781532691   fda6aa5c3a64e0ebfb96b0
    ;
run;
首先,为观察结果创建一个唯一的访客id/电话号码组合列表,这些组合不缺少电话号码:

proc sql;
    create table temp2 as select distinct
        visitor_id, telephone_number
        from temp (where = (not missing(telephone_number)));
quit;
如果缺少原始电话号码变量,则将其与原始表连接:

proc sql;
    create table temp3 as select
        a.date, a.visitor_id,
        case when missing(a.telephone_number) then b.telephone_number else a.telephone_number end as telephone_number
        from temp as a
        left join temp2 as b
        on a.visitor_id = b.visitor_id;
quit;
上面的文章有一个问题,因为一些访客id在数据集中没有CTN,因此数据集膨胀了很多次

这些措施似乎奏效了:

proc sql;
create table temp3 as 
select a.date, a.visitor_id,b.telephone_number
    from temp a inner join temp2 as b 
    on a.visitor_id = b.visitor_id;

退出

如果您提供了一个实际数据集的片段,这将非常有用。另外,您是否有理由保留ID和电话号码的重复观察结果?您好,我已经添加了相同的内容,是的,我希望保留重复的条目,因为这是一个事务数据,并将提供一段时间内电话号码发生任何活动的次数。您好,Paul,很好的开始,但我提供的示例只是一个示例,实际数据集有100万行,这对这种大小是否仍然有效?是的,尽管这取决于具体情况。根据您的系统内存大小,您可能会发现我的重复数据消除方法使用了太多内存,在这种情况下,实际上不需要使用哈希表。不过,我已经成功地在大型数据集上运行了这种类型的命令。关于键联接,您会发现这在大型数据集上非常有效。在可能的情况下,我会将其用于其他连接类型,只是因为它运行得太快了。我编辑了我的原始答案,让您可以选择不使用哈希表。我还假设您的基表可能比2列宽,如果是这种情况,您应该将查找表限制为匹配的字段,以及您想要更新的内容-因此,我在上面的示例中添加了一个keep语句来实现这一点,虽然在上面的示例中是多余的,因为基表只有这2列。是的,表宽于2列,我只想更新电话列,而上面的方法删除空白电话号码以创建查找表,如果您的ID具有多个不同的电话号码,由于左连接的性质,上述方法将导致比开始时更多的行。上述方法的效率也远低于键联接。是的,因此如果OP有这种情况,我们就必须以稍微不同的方式处理问题(如果他在处理这些问题时在他的问题中包含这样的问题,这会很有帮助)。但事实似乎并非如此。这种方法的好处是,它以一种直截了当的方式解决了所提出的问题,而且在一个只有1m obs的数据集上运行它不会遇到任何效率问题。