Как читать из двух наборов данных по определенному порядку?

Предположим, у меня есть 2 набора данных A и B:

Data A;
input data $;
datalines;
A1
A2
A3
;
run;

Data B;
input data $;
datalines;
B1
B2
B3
;
run;

Я хочу создать набор данных в определенном порядке, как показано ниже:

A1
B1
B2
B3
A2 
B1
B2
B3
A3
B1
B2
B3

Как сделать это из наборов данных A и B с шагом данных без POINT=?

Я попробовал этот метод:

DATA WRONG_ANSWER;
SET A;
OUTPUT;
DO i = 1 to 3;
SET B;
OUTPUT;
END;
RUN;

Вот результат:

A1
B1
B2
B3
A2

Похоже, что индикатор конца файла в B завершает этот шаг данных.

Я также попробовал другой подход с POINT=, и я получил правильный результат. Однако этот подход очень медленный из-за большого времени ввода-вывода при доступе к конкретным объектам из B:

DATA WRONG_ANSWER;
SET A;
OUTPUT;
DO i = 1 to 3;
SET B POINT=i;  //this is the only different from above 
OUTPUT;
END;
RUN;

1 ответ

Предполагая несколько вещей, самый быстрый метод для этого, вероятно, является решением итератора хеша. Предположения:

  • Набор данных B достаточно мал, чтобы поместиться (один раз) в память.
  • Либо вам нет дела до порядка строк набора данных B в результирующем наборе данных, либо у вас есть возрастающий или нисходящий порядок по ключевым переменным, или вы можете создать переменную порядка ключей.
  • Набор данных B может иметь ключ, определенный таким образом, чтобы он содержал уникальные строки, или вы можете использовать 'multidata:yes' (иметь достаточно новую версию SAS для поддержки этого).

Учитывая эти предположения, это работает:

data want;
  if 0 then set b;
  if _n_=1 then do;
    declare hash b_hash(dataset:'b', ordered:'a');
    b_hash.defineKey('data');
    b_hash.defineData('data');
    b_hash.defineDone();
    declare hiter b_iter;
    b_iter = _new_ hiter('b_hash');
  end;
  set a;
  output;
  rc = b_iter.first();
  do while (rc=0);
    output;
    rc = b_iter.next();
  end;
run;

В зависимости от вашего варианта использования, вы можете построить defineData вызов через систему макросов и / или запрос dictionary.columns, чтобы избежать жесткого кодирования имен столбцов.

Это намного быстрее, чем точка; по сравнению с исходным уровнем:

data want_point;
  set a;
  output;
  do _n_ = 1 to nobs_b;
    set b point=_n_ nobs=nobs_b;
    output; 
  end;
run;
  • С большими A, 1e7 строками и маленькими B, 3 строками, это занимает ~10 секунд в реальном времени /8 секунд процессорного времени (не намного больше, чем общее время записи), в то время как базовая точка занимает 100 секунд в реальном времени /12 секунд CPU время.
  • При меньшем значении A точка становится несколько более эффективной, но все еще превосходит хэш (хотя и незначительно, что, вероятно, не стоит разницы в сложности кодирования). Оба приближаются к 10 секундам записи, чтобы записать комбинацию 1e4 A/ 1e3 B (что дает файл схожего размера с первым).
  • С маленькими A и большими B ( 3 строки A, 1e7 строк B) хеш-код занимает немного больше времени, поскольку он требует больших затрат на первоначальную настройку; 67 секунд для хеш-решения (28 секунд ЦП) против 65 секунд для точки (17 секунд ЦП).

Таким образом, рекомендуется использовать Hash, если у вас большой набор данных и вы неоднократно комбинируете его с небольшим набором данных. Если два набора данных имеют одинаковый размер или многократно установленный набор данных больше, точка, вероятно, так же хороша, как вы получите (учитывая более высокую сложность поддержания хэша).

Другие вопросы по тегам