Посмотрите и замените значения из отдельной таблицы в SAS - PullRequest
0 голосов
/ 27 мая 2018

Набор данных 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(), но шаг DATA в SAS не подходит для нескольких наборов данных.,Как я могу сделать эти замены, используя CORRECTIONS в качестве справочной таблицы?

Ответы [ 4 ]

0 голосов
/ 28 мая 2018

Данные даны

*** 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
  ;

Другой, единственный способ SAS для выполнения встроенных левых объединений - использовать пользовательский формат.

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;
0 голосов
/ 27 мая 2018

Вы можете использовать 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 ;
0 голосов
/ 27 мая 2018

Существует много способов поиска в SAS.

Прежде всего, я бы предложил исключить дублирование вашей таблицы поиска (например, с помощью PROC SORT и Data Step / Set)./ By) - решение о том, какой дубликат оставить (если он существует).

Что касается самой задачи поиска, для простоты и изучения я бы предложил следующее:

Способ "СТАРАЯ ШКОЛА"- хорошо подходит для аудита входных и выходных данных (легче проверить результаты объединения, когда входные таблицы расположены в требуемом порядке):

*** 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 - который позволяет избежать сортировки таблицы имён:

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 (т. Е. Когда нет коррекции) именами из таблицы имён

EDIT: чтобы отразить комментарий Квентина (ПРАВИЛЬНЫЙ), который яя пропустил обновление как имен, так и полей друзей.

Основываясь на исправлении 2 полей, снова много подходов, но суть в том, чтобы обновлять значение только в том случае, если оно существует в поиске (исправлениях).возможность.Хеш-объект довольно хорош в этом, как только вы поняли, что это объявление.

Примечание: любые ключевые поля в объекте Hash необходимо указывать в операторе Length BEFOREHAND.

EDIT: согласно альтернативе ChrisJ объявлению оператора Length и моему ответу (см. Ниже)- было бы лучше указать, что ключевые переменные должны быть определены ДО того, как вы объявите хеш-таблицу.

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

В основном,вам нужно ограничить каждую инструкцию UPDATE ТОЛЬКО теми строками, которые имеют значения имен или значения друзей в таблице исправлений - это делается добавлением еще одной инструкции where ПОСЛЕ того, как вы указали set var = (предложение).См. Ниже.

Примечание.AFAIK, решение SQL вашего требования потребует БОЛЕЕ 1 прохода как базовой таблицы, так и таблицы поиска.

Однако для просмотра / хэш-таблицы требуется один проход базовой таблицы, загрузка таблицы поиска и затем сами действия поиска.Вы можете увидеть разницу в производительности в журнале ...

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;
0 голосов
/ 27 мая 2018

Попробуйте это:

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

Дайте мне знать в случае каких-либо разъяснений.

...