среда, 8 октября 2014 г.

Удаление строк дубликатов в SAS


Тема довольно простая и задача, я думаю, распространенная, но поскольку мне в моей работе с ней пришлось столкнуться совсем недавно, то не обошлось без кривого кода и дальнейшего радостного нахождения необходимой функции. Поэтому для наглядности расскажу все по порядку.

Вначале мной был опробован метод основанных на тех знаниях которые у меня были, а именно использовались магические First.переменная и Last.переменная . Что это?
Если вы отсортируете вашу базу по какой-либо переменной , например, Name, то получите, скажем, такой  результат:


Name
1
Андрей  
2
Андрей  
3
Вася   
4
Коля   
5
Яна    
6
Яна    
7
Яна    
8
Яна    

Так вот, поскольку SAS обрабатывает данные построчно, то находясь на первой строке он будет знать что Андрей под номером 1 это начало группы Андреев и для первого Андрея будет выполнятся условие  Fist.Name=1, для последнего Андрея (2 строка), замыкающего группу будет верно Last.Name=1.


Name
Для проверки переменной Name
1
Андрей  
Fist=1
2
Андрей  
Last=1
3
Вася   
Fist=1    Last=1
4
Коля   
Fist=1    Last=1
5
Яна    
Fist=1
6
Яна    

7
Яна    

8
Яна    
Last=1

Т.к. Вася у нас в списке один то он является как началом, так и концом группы т.е. обрабатывая строчку с Васей  SAS будет считать что для него верны оба выраженияFist.Name=1 и Last.Name=1.
Ян у нас много, для Ян в 6 и 7 строке оба условия

if  Fist.Name=1 и 
if  Last.Name=1 

ложные т.к. они не начинают и не замыкают отсортированные группы. 

Если попытаться подытожить, то после сортировки вы с помощью условийFist.переменная=1 и Last. переменная =1 можете понять - где начинается и где кончается отсортированная группа. К слову если вы будете сортировать по нескольким переменным картина будет примерна такая:


Name
Age
Для проверки переменной Name
Для проверки переменной Age
1
Андрей  
12
Fist=1
Fist=1    Last=1
2
Андрей  
13
Last=1
Fist=1    Last=1
3
Вася   
11
Fist=1    Last=1
Fist=1    Last=1
4
Коля   
15
Fist=1    Last=1
Fist=1    Last=1
5
Яна    
14
Fist=1
Fist=1
6
Яна    
14


7
Яна    
14

Last=1
8
Яна    
67
Last=1
Fist=1    Last=1

Ну вот на идеи заключающейся в том, что нам надо оставить только те записи, которые являются началом очередной группы ( при сортировке по двум переменным началом группы по второй переменной) и был основан «гениальный» метод  удаления дубликатов. Этот метод еще допустим, если надо решить проблему дубликатов по одной или двум переменным, но как только встает вопрос о всех переменных, тут уж простите «хана», надо сперва отсортировать по всем переменным а потом прописать кучу условий и становится понятно что кто-то чего то не знает. Т.к. не могли создатели языка не подумать о столь простой и нужной вещи. Она и нашлась спустя пары минут поиска:

PROC SORT  in= ФАЙЛ out=ФАЙЛ NODUNKEY;
BY _ALL_; *в этом случае дубликаты ищутся по всем переменным, можно прописать по каким именно вам надо;
Run;

Отрабатывае этот код, естественно, в разы быстрее чем все придуманные мной до этого извращения.
Заметка будет полезна скорее начинающим чем людям долго работающим с SAS



9 комментариев:

  1. Этот комментарий был удален автором.
    Ответить
  2. К сожалению, проблема в обоих способах заключается в том, что требуется использовать сортировку. Обычно это означает нагрузку на БД, которая не желательна.

    Я бы порекомендовал использовать PROC SQL в этих целях.

    Допустим у нас есть табличка TABLE (ID CHAR) на стороне БД с которой нам предстоит работать.

    Тогда код бы примерно выглядел так:

    libname t1 ;
    libname t2 ;

    proc sql;
    create table t2.TABLE2 as t1.TABLE;
    insert into t2.TABLE2
    select * from t1.table
    group by ID
    having count(ID) > 0;
    quit;
    Ответить
    Ответы
    1. Стоит сравнить методы, у меня был довольно большой массив больше 50 мл записей и отрабатывал он за секунды, тогда завтра поэкспериментирую и сравним результаты, это будет интересно)
    2. а как ищутся дубликаты без сортировки?
    3. Насколько я знаю, в DATA STEP никак.
    4. На мой взгляд этот код просто создаст в таблице t2 копию t1. Можно написать having count(ID) = 0; но тогда будут выброшены вообще все строчки у которых есть дубли, а в задаче требуется один экземпляр оставить.
      Проще всего сделать так:
      proc sql;
      create table t2 as select distinct * from t1;
      quit;
    5. имелось в виду, конечно having count(ID) = 1;
      Еще стоит обратить внимание, что count(ID) будет считать только непустые ID. В общем случае, когда нужно просто подсчитывать количесво строчек в группах, лучше писать count(*) .
  3. Не совсем верно описана работа proc sort.
    Опция NODUPLICATES позволяет выкинуть все дублирующиеся строчки. А команда BY _ALL_; говори лишь о том, что нужно произвести сортировку по всем переменным и никак не влияет на удаление дублей.
    Ответить
    Ответы
    1. Это не совсем верно, нашла заметку:
      http://www.datasavantconsulting.com/roland/nodup.html
      Потестировала NODUPLICATES действительно результат несколько не устойчивый, зато nodupkey выполняет свою функцию вполне стабильно. Думаю в мой заметки стоит заменить одну функцию на другую и будет корректнее)

Комментариев нет:

Отправить комментарий