Удаление похожих строк в SAS

У меня есть две таблицы с похожей структурой:
- Первая таблица: id и col1,col2,col3 - все числа.
- Вторая таблица: id и col4,col5,col6 - все числа.

Я хочу удалить из первого все строки, которые похожи на любую из строк из второго тега. Я считаю строку похожей на другую строку, когда любой столбец из группы col1-col3 равен любому из столбцов из группы col4-col6. Теперь я делаю это за 9 последовательных шагов данных (сначала проверяется, col1=col4, второй col1=col5,..., девятый col3=col6), что, вероятно, не является оптимальным решением.

Есть идеи, как это улучшить?


person Tomek Tarczynski    schedule 20.03.2012    source источник


Ответы (2)


Это мое решение:

data vec1;
  set ds2;
  array cvar{*} col4 col5 col6;
  do ijk=1 to dim(cvar);
    compvar=cvar(ijk);
    output;
  end;
run;

proc sql noprint;
  select distinct compvar into :cvars separated by ' '
  from vec1;
quit;
%let numcvar=&sqlobs;

data ds1(drop=i);
  set ds1;
  array myvar(i) col:;
  do over myvar;
    if myvar in (&cvars.) then delete;
  end;
run;

Если у вас возникли проблемы с длиной макропеременной CVARS, вы можете использовать это вместо этого:

data vec1;
  set ds2;
  array cvar{*} col:;
  do ijk=1 to dim(cvar);
    compvar=cvar(ijk);
    output;
  end;
run;

proc sort data=vec1 out=vec2(keep=compvar) nodupkey;
  by compvar;
run;

proc transpose data=vec2 out=flat prefix=x;
run;

data ds1(keep=id col:);
  set ds1b;
  if _n_=1 then set flat;
  array myvar(i) col:;
  array xvar(j) x:;
  do over myvar;
    do over xvar;
      if myvar=xvar then delete;
    end;
  end;
run;

PROC SORT можно исключить, но это делает его более эффективным для больших наборов данных.

Или вы можете сгенерировать формат на лету:

data vec1;
  set ds2;
  array cvar{*} col4 col5 col6;
  do ijk=1 to dim(cvar);
    compvar=cvar(ijk);
    output;
  end;
run;

proc sort data=vec1 out=vec2 nodupkey;
  by compvar;
run;

data fmt1;
  set vec2;
  length start $20;
  fmtname="remobs";
  start=compress(put(compvar,best.));
  label="remove";
run;

proc format lib=work cntlin=fmt1;
run;

data ds1(drop=i);
  set ds1;
  array myvar(i) col:;
  do over myvar;
    if put(myvar,remobs.)="remove" then delete;
  end;
run;

Я подозреваю, что этот последний метод будет быстрее, чем два предыдущих решения.

ОБНОВИТЬ

Использование хеш-объектов

data vec1;
  set ds2;
  array cvar{*} col4 col5 col6;
  do ijk=1 to dim(cvar);
    compvar=cvar(ijk);
    output;
  end;
run;

proc sort data=vec1 out=vec2 nodupkey;
  by compvar;
run;

data ds1_new(keep=id col1 col2 col3);
  if _n_ = 0 then set work.vec2;
  declare hash myhash(DATASET:'work.vec2') ; 
  rc=myhash.defineKey('compvar'); 
  myhash.defineDone();
  set ds1;
  array rcarr{*} rc1-rc3;
  array lookup{*} col1 col2 col3;
  do i=1 to dim(lookup);
    rcarr(i)=myhash.find(key: lookup(i));
    if rcarr(i)=0 then delete;
  end;
run;
person DavB    schedule 20.03.2012
comment
Честно говоря, я был немного сбит с толку, когда впервые прочитал ваш последний метод. Я никогда не видел такого использования форматов, но SAS иногда настолько неинтуитивен, что я не удивлюсь, если это действительно самый быстрый метод. В любом случае +1, и если через несколько дней не будет лучшего решения, я приму ваш ответ. Спасибо! - person Tomek Tarczynski; 21.03.2012
comment
Разве хэш-таблицы не будут быстрее, чем ваше решение? - person Tomek Tarczynski; 23.03.2012
comment
@TomekTarczynski - я не уверен. Я на самом деле не использовал их раньше ... так что спасибо за пример использования! Я добавил решение хеш-объекта (которое, кажется, работает для меня). Вы можете сообщить о своих выводах относительно скорости различных методов. - person DavB; 23.03.2012

хорошо, 2-я попытка ответить на это. Я создал декартово соединение двух наборов данных, чтобы сопоставить каждую строку в таблице 1 с каждой строкой в ​​таблице 2. Затем вы можете использовать массивы, чтобы узнать, какие строки имеют повторяющиеся значения.

data ds1;
input id col1 col2 col3;
cards;
1   10  20  30
2   40  50  60
3   70  80  90
4   15  25  35
5   45  55  65
;
run;

data ds2;
input id col4 col5 col6;
cards;
10  100 200 300
12  60  50  600
13  700 800 70
16  15  20  300
;
run;

proc sql;
create view all_cols as select
ds1.id as id1, ds2.id as id2,* from ds1,ds2;
quit;

data match;
set all_cols (keep=id1 id2 col:);
array vars1{*} col1-col3;
array vars2{*} col4-col6;
do i=1 to dim(vars1);
do j=1 to dim(vars2);
    if vars1{i}=vars2{j} then do;
    output;
    return;
    end;
end;
end;
drop i j;
run;

proc sort data=match;
by id1;
run;

data ds1;
modify ds1 match (in=b keep=id1 rename=(id1=id));
by id;
if b then remove;
run;
person Longfish    schedule 20.03.2012
comment
Возможно, я был недостаточно точен, но может быть так, что строка с id=1 из первой таблицы похожа на запись с id=145 из второй таблицы. Столбец идентификатора в одной таблице может иметь совершенно другие значения, чем столбец идентификатора во второй таблице. Это просто некоторая дополнительная информация, позволяющая определить, какая запись похожа на какую. - person Tomek Tarczynski; 20.03.2012
comment
Извините, Томек, я не прочитал ваш вопрос должным образом. Я пересмотрел свой ответ, который, надеюсь, теперь делает то, что вы хотите. - person Longfish; 20.03.2012
comment
Это работает, но, если я не ошибаюсь, очень медленно. Если первая таблица состоит из 10000 строк и вторая, то представление all_cols будет состоять из 10^8 строк. - person Tomek Tarczynski; 20.03.2012