数据集HAVE
包含两个带有拼写错误名称的变量:names
和friends
。
Name Age Friend
Jon 11 Ann
Jon 11 Tom
Jimb 12 Egg
Joe 11 Egg
Joe 11 Anne
Joe 11 Tom
Jed 10 Ann
我有一个小数据集CORRECTIONS
,其中包含wrong_names
和resolved_names
。
current_names resolved_names
Jon John
Ann Anne
Jimb Jim
我需要names
中的friends
或HAVE
中与wrong_names
CORRECTIONS
列中的名称匹配的任何名称,以便重新编码到相应的字符串在resolved_name
。生成的数据集WANT
应如下所示:
Name Age Friend
John 11 Anne
John 11 Tom
Jim 12 Egg
Joe 11 Egg
Joe 11 Anne
Joe 11 Tom
Jed 10 Anne
在R中,我可以使用if_else()
简单地调用每个数据帧和向量,但SAS中的DATA步骤并不能很好地与多个数据集一起使用。如何使用CORRECTIONS
作为查找表来进行这些替换?
答案 0 :(得分:3)
有很多方法可以在SAS中进行查找。
但是,首先,我建议删除您的查找表(例如,使用PROC SORT和Data Step / Set / By) - 决定保留哪些副本(如果存在)。
至于查找任务本身,为了简单和学习,我建议如下:
“OLD SCHOOL”方式 - 适用于审计输入和输出(当输入表符合所需顺序时,更容易验证连接的结果):
*** data to validate;
data have;
length name $10. age 4. friend $10.;
input name age friend;
datalines;
Jon 11 Ann
Jon 11 Tom
Jimb 12 Egg
Joe 11 Egg
Joe 11 Anne
Joe 11 Tom
Jed 10 Ann
run;
*** lookup table;
data corrections;
length current_names $10. resolved_names $10.;
input current_names resolved_names;
datalines;
Jon John
Ann Anne
Jimb Jim
run;
*** de-duplicate lookup table;
proc sort data=corrections nodupkey; by current_names; run;
proc sort data=have; by name; run;
data have_corrected;
merge have(in=a)
corrections(in=b rename=(current_names=name))
;
by name;
if a;
if b then do;
name=resolved_names;
end;
run;
SQL方式 - 避免对has表进行排序:
proc sql;
create table have_corrected_sql as
select
coalesce(b.resolved_names, a.name) as name,
a.age,
a.friend
from work.have as a left join work.corrections as b
on a.name eq b.current_names
order by name;
quit;
注意,Coalesce()用于替换缺少的resolved_names值(即没有更正时),其中包含来自has表的名称
编辑:反映昆汀(CORRECT)的评论,我错过了对名称和朋友字段的更新。
基于对2个字段的校正,再次许多方法,但实质是仅在它存在于查找(校正)表中时更新值。一旦你理解了它的声明,哈希对象就相当不错了。
注意:需要在BEFOREHAND的长度语句中指定Hash对象中的任何关键字段。
编辑:根据ChrisJ的替代长度语句声明和我的回复(见下文) - 最好说明在声明哈希表之前需要定义关键变量。
data have_corrected;
keep name age friend;
length current_names $10.;
*** load valid names into hash lookup table;
if _n_=1 then do;
declare hash h(dataset: 'work.corrections');
rc = h.defineKey('current_names');
rc = h.defineData('resolved_names');
rc = h.defineDone();
end;
do until(eof);
set have(in=a) end=eof;
*** validate both name fields;
if h.find(key:name) eq 0 then
name = resolved_names;
if h.find(key:friend) eq 0 then
friend = resolved_names;
output;
end;
run;
编辑:回答ChrisJ的SQL /更新备选
的评论基本上,您需要将每个UPDATE语句限制为仅在更正表中具有名称值或朋友值的行 - 这是通过在您指定set var =(子句)之后添加另一个where子句来完成的。见下文。
NB。 AFAIK,一个满足您需求的SQL解决方案,需要多次传递基表和查找表。
然而,查找/哈希表需要基表的单次传递,查找表的加载以及查找操作本身。您可以在日志中看到性能差异...
proc sql;
*** create copy of have table;
create table work.have_sql as select * from work.have;
*** correct name field;
update work.have_sql as u
set name = (select resolved_names
from work.corrections as n
where u.name=n.current_names)
where u.name in (select current_names from work.corrections)
;
*** correct friend field;
update work.have_sql as u
set friend = (select resolved_names
from work.corrections as n
where u.friend=n.current_names)
where u.friend in (select current_names from work.corrections)
;
quit;
答案 1 :(得分:2)
您可以在UPDATE
中使用proc sql
:
proc sql ; update have a set name = (select resolved_names b from corrections where a.name = b.current_names) where name in(select current_names from corrections) ; update have a set friend = (select resolved_names b from corrections where a.friend = b.current_names) where friend in(select current_names from corrections) ; quit ;
或者,您可以使用以下格式:
/* Create format */ data current_fmt ; retain fmtname 'NAMEFIX' type 'C' ; set resolved_names ; start = current_names ; label = resolved_names ; run ; proc format cntlin=current_fmt ; run ; /* Apply format */ data want ; set have ; name = put(name ,$NAMEFIX.) ; friend = put(friend,$NAMEFIX.) ; run ;
答案 2 :(得分:2)
给定数据
*** data to validate;
data have;
length name $10. age 4. friend $10.;
input name age friend;
datalines;
Jon 11 Ann
Jon 11 Tom
Jimb 12 Egg
Joe 11 Egg
Joe 11 Anne
Joe 11 Tom
Jed 10 Ann
run;
*** lookup table;
data corrections;
length from_name $10. to_name $10.;
input from_name to_name;
datalines;
Jon John
Ann Anne
Jimb Jim
run;
一种SQL替代方法是在要映射的每个字段上执行现有的映射选择查找。这对于每个要映射的字段加入校正表是违反的。
proc sql;
create table want1 as
select
case when exists (select * from corrections where from_name=name)
then (select to_name from corrections where from_name=name)
else name
end as name
, age
, case when exists (select * from corrections where from_name=friend)
then (select to_name from corrections where from_name=friend)
else friend
end as friend
from
have
;
执行内联左连接的另一种方法是使用自定义格式。
data cntlin;
set corrections;
retain fmtname '$cohen'; /* the fixer */
rename from_name=start to_name=label;
run;
proc format cntlin=cntlin;
run;
data want2;
set have;
name = put(name,$cohen.);
friend = put(friend,$cohen.);
run;
答案 3 :(得分:1)
试试这个:
proc sql;
create table want as
select p.name,p.age,
case
when q.current_names is null then p.friend
else q.resolved_names
end
as friend1
from
(
select
case
when b.current_names is null then a.name
else b.resolved_names
end
as name,
a.age,a.friend
from
have a
left join
corrections b
on upcase(a.name) = upcase(b.current_names)
) p
left join
corrections q
on upcase(p.friend) = upcase(q.current_names);
quit;
输出
name age friend
John 11 Anne
Jed 10 Anne
Joe 11 Anne
Jim 12 Egg
Joe 11 Egg
Joe 11 Tom
John 11 Tom
如有任何澄清,请通知我。