Хранить дубликаты ID только в случае отсутствия новой информации - PullRequest
0 голосов
/ 02 октября 2018

Я хочу сохранить все дубликаты идентификаторов, кроме случаев, когда нет новой информации из-за пропущенных значений.Например,

data test;
input id var1 var2 var3
    datalines;
    1 2 3 4
    1 4 . 4
    1 6 5 4
    1 . 3 .
    1 2 4 4
    1 6 . 4
    1 . 8 4
    ;
run;

Я хочу, чтобы результат был

1 2 3 4
1 4 . 4
1 6 5 4
1 2 4 4
1 . 8 4    

Строка 4 удалена, поскольку строка 1 имеет одинаковые id, var2 и var3.Строка 6 удалена, потому что строка 3 имеет тот же идентификатор, var1, var3.Я также хочу надежное решение, так как я хочу, чтобы решение работало для любого числа переменных в наборе данных (id всегда будет уникальным ключом).

Есть мысли?Я думал сортировать nodupkey, но он не работает, если в строке более одного пропущенного значения.

Ответы [ 2 ]

0 голосов
/ 02 октября 2018

Вот схема одного шага данных + двойной подход DOW:

  • Для каждого идентификатора:
    • Создание хеш-объекта + итератор со всеми переменными в качестве ключей
    • Попытка загрузить все строки для этого идентификатора в хеш.Это эквивалентно первоначальному проходу с процедурой сортировки nodupkey.
    • Сделайте второй проход через все строки с одинаковым идентификатором (double DOW), пропуская все, которые содержат только не пропущенные значения.
      • Подсчитать каждую строку как дубликат по умолчанию
      • Для каждой пары переменных с хотя бы одним не пропущенным значением в текущей строке:
        • Проверить, является ли эта пара значенийприсутствует в любом предыдущем элементе в хэше.
      • Пометьте строку как неповторяющуюся, если мы найдем хотя бы одну пару значений, которые не совпадают нигде в хэше.Обработайте пропущенные значения в текущей строке как совпадающие.Как только строка помечается как не дублированная, мы можем перейти к следующей строке.

Я думаю, что это наихудший случай O(n ^ 4), но если есть большая доля дубликатов, то это должно быть лучше.

Обновление:

Вот пример реализации - это действительно было довольно грязно:

proc sql noprint;
  select 
    quote(trim(name)), 
    name,
    count(name) 
  into 
    :varlist separated by ',', 
    :arraylist separated by ' ',
    :varcount
  from dictionary.columns 
  where 
    libname = 'WORK' 
    and memname = 'TEST' 
    and type = 'num'
    and name ne 'id'
  ; 
quit;

data want;
  /*Set up arrays*/
  if 0 then set test;
  array vars[*] &arraylist;
  array temp[&varcount] _temporary_;
  length sub_id 8;
  keep id &arraylist;

  /*Set up hash + iterator*/
  if _n_ = 1 then do;
    declare hash h(ordered:'a');
    rc = h.definekey('sub_id', &varlist);
    rc = h.definedata('sub_id', &varlist);
    rc = h.definedone();
    declare hiter hi('h');
  end;

  /*DOW #1 - load hash and output definite non-duplicates*/
  do _n_ = 1 by 1 until(last.id);
    set test;
    by id;
    /*We need a way to keep track of rows within each id so that we don't count rows as duplicates when they match themselves in DOW #2*/
    sub_id = _n_;
    rc = h.add();
    if rc = 0 and nmiss(of vars[*]) = 0 then output;
  end;

  /*DOW #2 - check for any previously unseen pairs of values*/
  do _n_ = 1 to _n_;
    set test;
    /*Make a copy of the current row to retrieve after looping through the hash iterator*/
    do i = 1 to dim(vars);
      temp[i] = vars[i];
    end;
    if nmiss(of vars[*]) > 0 then do;
      dup_flag = 1;
      /*Work through successive pairs of values*/
      do i = 1 to dim(vars) while(dup_flag = 1);
        do j = 1 to i - 1 while(dup_flag = 1);
          __v_i = temp[i];
          __v_j = temp[j];
          match_flag = 0;
          /*For each pair, loop through the iterator until we find a 'match'*/
          rc = hi.first();
          do while(rc = 0 and match_flag = 0 and sub_id < _n_);            
            if    (missing(__v_i) or __v_i = vars[i])
              and (missing(__v_j) or __v_j = vars[j])
              then match_flag = 1;
            rc = hi.next();
          end;
          /*If we didn't find a match, we have a new combination and the row is not a duplicate*/
          if match_flag = 0 then dup_flag = 0;
        end;
      end;
      if dup_flag = 0 then do;
        do i = 1 to dim(vars);
          vars[i] = temp[i];
        end;
        output;
      end;
    end;
  end;
  rc = h.clear();
run;
0 голосов
/ 02 октября 2018

Вы можете извлекать строки, у которых пропущена еще одна или более.На втором шаге вы должны сгенерировать RegExp, который поможет вам идентифицировать похожие строки.

Код может быть улучшен вашим предложением.

data test;
input id var1 var2 var3;
    datalines;
    1 2 3 4
    1 4 . 4
    1 6 5 4
    1 . 3 .
    1 2 4 4
    1 6 . 4
    1 . 8 4
    ;
run;

data test2 missing;
/*incrase this strings if you have big values*/
length res $ 200 addedEl $ 10;
    set test;
    array num _NUMERIC_;

    /*add flag  to determine is there missin in row*/
    flag=0;
    do i=1 to dim(num);
        addedEl=compress(put(num(i),8.));
        if num(i)=. then
            do;
                flag=1;
                /*template for number. If you have comma separated vars then replace on \d+\.\d*        */
                addedEl="\d+";
            end;
        /*add delimeter to row parse, if you have more than one digits in vars =)*/
        res=catx("_",res,addedEl);
    end;


    if flag=0 then  output test2;
    else    do;
        res=catt("/",res,"/");
        output missing;
    end;


    drop i flag addedEl;
run;

/*determine rows that dublicates*/
proc sql noprint;
create table matched as
  select  B.* 
          ,prxparse(B.res) as prxm 
          ,A.*
  from  test2 as A
        ,missing as B
  where prxmatch(calculated prxm,A.res)
  order by B.res;
quit;
run;

/*pre-merge sort*/
proc sort data=missing;
    by res;
run;

/*delete rows that are in second dataset*/
data miss_correctred;
    merge missing(in=mss)
        matched(in=mtch)
    ;
    by res;

    if mss=1 and mtch=0;
run;

data test_res(drop=prxm res);
    set test2 miss_correctred;
run;

результат:

+----+------+------+------+
| id | var1 | var2 | var3 |
+----+------+------+------+
|  1 |    2 |    3 |    4 |
|  1 |    6 |    5 |    4 |
|  1 |    2 |    4 |    4 |
|  1 |    4 |    . |    4 |
|  1 |    . |    8 |    4 |
+----+------+------+------+
...