Горизонтальное объединение датасетов.

Merge Statement (Слияние датасетов)

Обязательна к прочтению Chapter 10. Subsetting and Combining SAS Data Sets.

В предыдущем уроке мы добавляли строки из одного датасета к другому. Существует, в некотором смысле, противоположная задача – добавление переменных из одного датасета в другой.

5.2.1

Допустим, информация о возрасте пациентов содержится в датасете Demo_age, а остальная информация (например, пол, страна) - в датасете Demog. Необходимо объединить эту информацию в один датасет Demo_all5.

5.2.2
proc sort data = Demog;
    by SUBJID;
run;

proc sort data = Demo_age;
    by SUBJID;
run;

data demo_all5;
    merge Demog
        Demo_age;
    by SUBJID;
run;
5.2.3

В результате выполнения этого дата степа к датасету Demog справа будут присоединены столбцы BRTHDTC и AGE. При этом, by-переменная SUBJID является своеобразным связующим звеном – ее значения определяют какие значение AGE и BRTHDTC записать в одну строку со значениями SEX, COUNTRY, STUDYID. Например, значение SUBJID = ‘05-1209’ присутствует в обоих датасетах, в датасете Demo_age для этого пациента значение AGE = 19, а в датасете Demog – значение SEX = ‘F’, это значит, что в результирующем датасете мы получим строку, где SUBJID = ‘05-1209’, AGE = 19, SEX = ‘F’.

5.2.4

Значение SUBJID = ‘05-1212’ встретилось только в датасете Demog_age, следовательно, значения STUDYID, SEX, COUNTRY в этой строке будут пустыми. И наоборот, в случае, когда значения SUBJID “19-2373” не нашлось в датасете Demog_age, пустыми будут значения AGE и BRTHDTC.

5.2.5

Опция IN

Часто бывает нужным оставить в результирующем датасете только те наблюдения, в которых значения by-переменных встретились во всех входящих датасетах. Или же, наоборот, узнать какие значения by-переменной встретились только в одном из датасетов.

Это возможно с помощью опции IN.

Опцию IN можно также использовать для SET Statement, для того, чтобы определить с какого из входящих датасетов пришли те или иные строки при вертикальном объединении датасетов. Рассмотрим пример для наглядности:

5.2.8.1

Объеденим датасеты Females и Males следующим образом:

data All_sex;
    set Females(in=in1)
        Males(in=in2);
            
    Females=in1;
    Males=in2;
run;
5.2.8.2

В результате получили dataset All_sex, в котором переменные Females и Males показывают с какого входящего датасета пришли строки.

Но, это было отступление, давайте все же вернемся к Merge Statement и подробнее разберем эту опцию. Для этого рассмотрим следующий код:

*Demo_age and Demog datasets are sorted by SUBJID;
data demo_all6;
    merge Demog(in=in1)
        Demo_age(in=in2);
    by SUBJID;
    
    Demog = in1;
    Demo_age = in2;
run;

Шаг 1. Опция in для датасета Demog создает временную переменную in1, которая равна 1(истина), если строка присутствует в Demog, и 0(ложь) – если нет. Аналогично, для датасета Demo_age, создана временная переменная in2: in2 = 1, если строка присутствует в Demo_age, in2 = 0 – если нет. Переменные Demog и Demo_age созданы для демонстрации значений временных переменных in1 и in2.

5.2.6

Шаг 2. Воспользуемся временными переменными in1 и in2 для отбора нужных записей.

data demo_all7;
    merge Demog(in=in1)
        Demo_age(in=in2);
    by SUBJID;
    
    if in1 and in2;
run;
5.2.7

В результате применения оператора IF в датасете demo_all7 окажутся только те записи, где in1 = 1 и in2 = 1, то есть только те записи, которые присутствовали в обоих входящих датасетах.

Таким образом, контролировать какие строки останутся в результирующем датасете можно, задав подходящее условие в IF оператор. Например, если в предыдущем примере заменить IF оператор на такой: if in1;, то останутся только те строки, которые были в датасете Demog.

Попробуем узнать теперь, у кого из пациентов есть информация только о возрасте, но нет о поле.

*Demo_age and Demog datasets are sorted by SUBJID;
data demo_age_only;
    merge Demog(in=in1)
        Demo_age(in=in2);
    by SUBJID;
    
    if not in1 and in2;
run;
5.2.8

By-переменная при merge должна обязательно называться одинаково во всех входящих датасетах, и быть одного типа. Естественно в реальном мире, это не всегда оказывается так, и приходится проделывать дополнительные шаги. Как сделать merge датасетов по пациентам, если в одном из них номер пациента – это переменная SUBJID, а во втором – PT? Ответ вам должен быть уже известен – переименовать одну из переменных. Как сделать merge датасетов по пациентам, если в одном из них переменная с номером пациента – числовая, а во втором – строковая? Ответ очевиден – создать в одном из датасетов новую переменную, которая будет соответствовать типу аналогичной переменной во втором датасете.

Рассмотрим такую задачу на примере. Датасет subj содержит информацию о пациентах (дата рождения, пол, возраст, страна), в нем переменная SUBJID (номер пациента) - строковая. Датасет TRT содержит информацию о лечении пациентов: он содержит числовую переменную SUBJID (номер пациента) и строковую переменную TRT (равна “PLACEBO” или “ TEST ”, в зависимости от того, принимал ли пациент плацебо, или тестируемое лекарство). Задача: создать датасет, в котором для каждого пациента была бы вся информация из обоих датасетов.

5.2.9
proc sort data = subj;
    by SUBJID;
run;

data TRT;
    set TRT(rename=(SUBJID=PT));
    length SUBJID $ 10;
    SUBJID = put(PT, best.-l);
    if lengthn(SUBJID) = 5 then SUBJID = "0" || SUBJID;
    drop PT;
run;

proc sort data = TRT;
    by SUBJID;
run;

data demo_all8;
    merge subj TRT;
    by SUBJID;
run;
5.2.10 5.2.11

Обратите внимание на лог программы. SAS предупреждает, что длина by-переменной во входящих датасетах разная, что может привести к непредвиденным последствиям. Это последствие мы можем наблюдать на пациенте с необычным номером, более длинным, чем у всех остальных, в датасете TRT он был “10000001”, а в результирующем demo_all8 стал “100000”, поскольку, длина переменной SUBJID в датасете subj была равна 6. Длина by-переменной будет такой, как в первом из перечисленных датасетов после ключевого слова merge. Выходов из данной ситуации может быть несколько.

Третий вариант сработает именно потому, что теперь первым указывается TRT датасет, в котором длинна by-переменной больше, чем в subj (а, как мы помним, в результирующем датасете длинна by-переменной устанавливается такой, как в первом из перечисленных датасетов).

One-to-Many Merge (Слияние один ко многим)

Рассмотрим понятие ключа в датасете. Ключ – это такой набор by-переменных, любая by-группа которого является уникальной строкой в датасете.

Во всех предыдущих примерах, в обоих входящих датасетах, by-переменная SUBJID являлась ключом, поскольку все значения переменной SUBJID были уникальными (другими словами, любое значение переменной SUBJID встречалось в одной, и только в одной строке датасета).

Такое слияние датасетов называется один к одному.

Можно так же рассматривать понятие слияния один ко многим. В этом случае, в одном из входящем датасете, но не более, чем в одном, by-переменные не являются ключом.

Рассмотрим пример. Датасет Adverse содержит информацию о том, какие нежелательные явления (adverse events) происходили с пациентом на протяжении исследования. Это могли быть болезни, травмы, реакции, и т.п. Понятно, что у каждого пациента могло возникнуть несколько таких явлений, и поэтому в датасете Adverse может быть несколько записей для одного и того же пациента. С датасетом TRT вы уже знакомы, там находятся те же пациенты, что и в Adverse, но в датасете TRT каждое значение SUBJID задает уникальную строку. Наша задача, добавить в датасет Adverse информацию о лечении пациента из датасета TRT.

5.2.12
proc sort data = Adverse;
    by SUBJID;
run;

*dataset TRT has already been sorted;
data demo_all9;
    merge TRT Adverse(in=inae);
    by SUBJID;
    if inae;
run;

В результате получим нужный нам датасет. Снова обратите внимание на использование опции IN, с ее помощью мы избавились от пациентов, у которых не случилось ни одного нежелательного явления (то есть те, которых не было в датасете Adverse, но были в датасете TRT).

Ccылка на merge statement sas-support: