Тема довольно простая и задача, я думаю, распространенная, но поскольку мне в моей работе с ней пришлось столкнуться совсем недавно, то не обошлось без кривого кода и дальнейшего радостного нахождения необходимой функции. Поэтому для наглядности расскажу все по порядку.
Вначале мной был опробован метод основанных на тех знаниях которые у меня были, а именно использовались магические 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
Этот комментарий был удален автором.
К сожалению, проблема в обоих способах заключается в том, что требуется использовать сортировку. Обычно это означает нагрузку на БД, которая не желательна.
Я бы порекомендовал использовать 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;
Стоит сравнить методы, у меня был довольно большой массив больше 50 мл записей и отрабатывал он за секунды, тогда завтра поэкспериментирую и сравним результаты, это будет интересно)
а как ищутся дубликаты без сортировки?
Насколько я знаю, в DATA STEP никак.
Не совсем верно описана работа proc sort.
Опция NODUPLICATES позволяет выкинуть все дублирующиеся строчки. А команда BY _ALL_; говори лишь о том, что нужно произвести сортировку по всем переменным и никак не влияет на удаление дублей.
На мой взгляд этот код просто создаст в таблице t2 копию t1. Можно написать having count(ID) = 0; но тогда будут выброшены вообще все строчки у которых есть дубли, а в задаче требуется один экземпляр оставить.
Проще всего сделать так:
proc sql;
create table t2 as select distinct * from t1;
quit;
имелось в виду, конечно having count(ID) = 1;
Еще стоит обратить внимание, что count(ID) будет считать только непустые ID. В общем случае, когда нужно просто подсчитывать количесво строчек в группах, лучше писать count(*) .
Это не совсем верно, нашла заметку:
http://www.datasavantconsulting.com/roland/nodup.html
Потестировала NODUPLICATES действительно результат несколько не устойчивый, зато nodupkey выполняет свою функцию вполне стабильно. Думаю в мой заметки стоит заменить одну функцию на другую и будет корректнее)