2014-09-25 4 views
0

набора данных выглядит следующим образом:Эффективный способ выбрать уникальные записи в SAS

Code Type  Rating 
0001 NULL  1 
0002 NULL  1 
0003 NULL  1 
0003 PA 1  3 
0004 NULL  1 
0004 PB 1  2 
0005 AC 1  3 
0005 NULL  6 
0006 AC 1  2 

Я хочу, чтобы выходной набор данные выглядит

Code Type  Rating 
0001 NULL  1 
0002 NULL  1 
0003 PA 1  4   
0004 PB 1  3   
0005 AC 1  9   
0006 AC 1  2 

Для каждого Code, Type имеет не более двух значений. Я хочу выбрать уникальный Code, суммируя Rating. Но проблема в том, что для Type, если он имеет только одно значение, его значение передается на выходный набор данных. Если есть два значения (один должен быть NULL), то он передает значение, равное NULL, для вывода набора данных.

Общее количество наблюдений N>100,000,000. Так есть ли какой-нибудь сложный способ достичь этого?

+0

Сколько различных значений 'type' у вас есть? Являются ли данные отсортированными, как указано выше, или не отсортированы (по крайней мере, по «коду»)? Что «не сложно», мы сравниваем это с (то есть, что у вас есть сейчас и как долго это требуется)? – Joe

+0

Несколько сотен типов. Данные сортируются по 'code', затем' type'. Я объединяю 'type' для каждого уникального' code'. Затем найдите нулевую подстроку. – Lovnlust

+0

Если он уже отсортирован (т. Е. Вы не делаете вид только для этого), то решение для шага данных Keith предлагает, вероятно, самое быстрое. Если это не так, то, скорее всего, решение массива будет самым быстрым, если это всего лишь несколько сотен типов. – Joe

ответ

0

Это очень легко сделать на одном шаге SQL. Просто используйте CASE ... WHEN ... END, чтобы удалить NULL и MAX, чтобы получить ненулевое значение.

data have; 
input @1 Code 4. 
     @9 Type $4. 
     @19 Rating 1.; 
datalines; 
0001 NULL  1 
0002 NULL  1 
0003 NULL  1 
0003 PA 1  3 
0004 NULL  1 
0004 PB 1  2 
0005 AC 1  3 
0005 NULL  6 
0006 AC 1  2 
;;;; 
run; 

proc sql; 
create table want as 
    select code, 
     max(case type when 'NULL' then '' else type end) as type, 
     sum(Rating) as rating 
     from have 
     group by code; 
quit; 

Если вы хотите NULLs обратно, то вам нужно обернуть выбрать в select code, case type when ' ' then 'NULL' else type end as type, rating from (...);, хотя я бы предложил оставить их пустыми.

+0

Не могли бы вы объяснить, почему здесь работает max? – Lovnlust

+0

Вам необходимо использовать итоговую функцию (чтобы работать GROUP BY). MAX работает на персонаже так же, как числовое (Z> A, а пробел = отсутствует меньше). Я превращаю NULL в пространство, чтобы он был наименее сравним с вашим другим значением. – Joe

+0

Я не следую. – Joe

1

Если данные отсортированы в соответствии с вашим примером, вы можете достичь этого на одном шаге данных. Я предположил, что значения NULL действительно отсутствуют, но если нет, то измените [если отсутствует (тип)] на [if type = 'NULL']. Все это означает суммирование значений рейтинга для каждого кода, а затем вывод последней записи, сохраняя непустой тип. Если ваши данные не отсортированы или не индексированы в коде, вам сначала нужно выполнить сортировку, что, очевидно, добавит немного времени на время выполнения.

/* create input file */ 
data have; 
input Code Type $ Rating; 
infile datalines dsd; 
datalines; 
0001,,1 
0002,,1 
0003,,1 
0003,PA 1,3 
0004,,1 
0004,PB 1,2 
0005,AC 1,3 
0005,,6 
0006,AC 1,2 
; 
run; 

/* create summarised dataset */ 
data want; 
set have; 
by code; 
retain _type; /* temporary variable */ 
if first.code then do; 
    _type = type; 
    _rating_sum = 0; /* reset sum */ 
end; 
_rating_sum + rating; /* sum rating per Code */ 
if last.code then do; 
    if missing(type) then type = _type; /* pick non-null value */ 
    rating = _rating_sum; /* insert sum */ 
    output; 
end; 
run; 
+0

Это сильно зависит от того, что у вас есть только два наблюдения максимум для одного кода, не могли бы вы просто сказать, как он будет расширен, если будет больше? –

+0

Хорошо, но было ясно указано, что появилось не более двух наблюдений. Если их было больше, это простое изменение логики. если first.code, то вызов отсутствует (_type, _rating_sum); если не хватает (тип), то _type = type; _rating_sum + рейтинг; и т. Д. – Longfish

+0

Это была не атака, а вопрос о том, как перейти к другому делу, ближе к моему. Извините, если это было оскорбительно. –

0

С учетом комментариев, другая возможность представляет хэш-решение. Это ограничено памятью, поэтому оно может или не может работать с фактическими данными (хеш-таблица не очень большая, но 100-миллиметровые строки могут означать 60 или 70 М строк в хэш-таблице, раз 40 или 50 байтов все еще довольно большой).

Это почти наверняка уступает методу простого шага данных, если набор данных отсортирован по коду, поэтому его следует использовать только на несортированных данных.

Понятия:

  • Создать хэш-таблицу с ключом по коду
  • Если входящая запись нового, добавить в хэш-таблице
  • Если входящая запись не новый код, возьмите извлеченное значение и сумму рейтинг. Проверьте, нужно ли заменять тип.
  • Выходные данные для набора данных.

Код:

data _null_; 
    if _n_=1 then do; 
     if 0 then set have; 
     declare hash h(ordered:'a'); 
     h.defineKey('code'); 
     h.defineData('code','type','rating'); 
     h.defineDone(); 
    end; 
    set have(rename=(type=type_in rating=rating_in)) end=eof; 
    rc_1 = h.find(); 
    if rc_1 eq 0 then do; 
    if type ne type_in and type='NULL' then type=type_in; 
    rating=sum(rating,rating_in); 
    h.replace(); 
    end; 
    else do; 
    type=type_in; 
    rating=rating_in; 
    h.add(); 
    end; 
    if eof then do; 
    h.output(dataset:'want'); 
    end; 
run; 
Смежные вопросы