Как читать из двух наборов данных по определенному порядку?
Предположим, у меня есть 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, если у вас большой набор данных и вы неоднократно комбинируете его с небольшим набором данных. Если два набора данных имеют одинаковый размер или многократно установленный набор данных больше, точка, вероятно, так же хороша, как вы получите (учитывая более высокую сложность поддержания хэша).