Сортировка датасетов.

Рассмотрим несложное, но очень важное понятие сортировки датасетов. Перед вами датасет Demo. Обратите внимание на столбец Subject Identifier for the Study (переменная SUBJID).

5.1.1

Наблюдения в датасете располагаются в порядке возрастания значений переменной SUBJID. Мы говорим, датасет Demo отсортирован по переменной SUBJID.

Отсортируем датасет по переменной AGE:

proc sort data = Demo out = dm_by_AGE;
    by AGE;
run;
5.1.2

Обратите внимание на результат работы этой процедуры: создался новый датасет dm_by_AGE, который отсортирован по переменной AGE. Переменная seq показывает, как изменился порядок строк в датасете dm_by_AGE по сравнению с Demo.

Датасеты можно сортировать, как по числовым, так и по строковым переменным. Если это числовая переменная – соблюдается порядок возрастания/убывания, если это строковая переменная – соблюдается алфавитный порядок/обратный алфавитный порядок.

Чтобы отсортировать датасет в обратном порядке, нужно использовать опцию descending.

proc sort data = dm_by_AGE;
    by descending AGE;
run;
5.1.3

Как видите, можно не указывать опцию out, датасет dm_by_AGE теперь отсортирован по убыванию переменной AGE.

By-группа (подгруппа)

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

5.1.4

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

Пусть мы хотим, чтоб каждая by-группа AGE была отсортирована по дате рождения (BRTHDTC).

proc sort data = Demo out = dm_by_AGE_BRTHDTC;
    by AGE BRTHDTC;
run;
5.1.5

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

Одна уникальная комбинация – одна by-группа.

Количество by-переменных может быть более чем две, это число ограничено только количеством переменных в вашем датасете.

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

Итак, сортировка датасета, by-переменные, by-группы, ключ – это понятия, с которыми мы неразлучны. Когда мы создаём стандартный датасет – он обязательно будет отсортирован в определенном порядке. Когда мы создаем таблицы и листинги – данные будут представлены в удобном порядке для просмотра (чаще всего хронологический или алфавитный, в зависимости от данных). Когда мы проводим какой-либо анализ – нам всегда может потребоваться провести его по подгруппам. Почти все наиболее часто используемые нами процедуры имеют оператор by, значит, все такие процедуры могут быть рассмотрены в контексте by-групп, и, перед использованием такой процедуры, ваш датасет должен быть предварительно отсортирован соответствующим образом.

Поверьте, многое в вашей работе будет зависеть от того, насколько правильно вы отсортируете ваш датасет.

    На SAS support есть раздел «BY-Group Processing in the DATA Step», ознакомиться можно по ссылке:

  • BY-Group Processing in the DATA Step
  • Наиболее подробный синтаксис процедуры SORT смотрите по ссылке

  • SORT Procedure

Работа с by-группами на этапе дата степа

Итак, мы изучили понятие by-групп. Рассмотрим некоторые полезные операторы, связанные с обработкой by-групп.

Переменные FIRST. и LAST.

Имеется датасет Demo, отсортированный по переменной AGE и BRTHDTC:

proc sort data=Demo;
    by AGE BRTHDTC;
run;
5.4.1

Постановка задачи: в каждой возрастной подгруппе определить самого молодого пациента. Для этого создать переменную, которая бы принимала значение ‘истина’, если пациент имеет самую позднюю дату рождения среди пациентов того же возраста, и ‘ложь’ – в противном случае. Например, в by-группе AGE = 18, таким пациентом будет 09-1397 с датой рождения 1995-09-17.

Решение: Сортировка по переменной AGE и BRTHDTC располагает строки в датасете так, что пациент с самой поздней датой рождения (т.е. самый молодой пациент) будет последним в данной возрастной подгруппе. Последнюю строку в by-группе мы всегда можем определить с помощью переменной last.<by-переменная>.

data Demo1;
    set Demo;
    by AGE;
    
    youngest = last.AGE;
run;

В результате получим датасет Demo1 c переменной youngest, которая равна 1 на последней строке в каждой by-группе AGE.

5.4.2

Аналогично можно определить самых старших пациентов в каждой возрастной подгруппе, воспользовавшись переменной first.<by-переменная>. На этот раз не будем создавать новую переменную, а просто выведем всех старших пациентов в датасет Demo_old, а всех младших – в датасет Demo_young.

*dataset Demo are sorted by AGE BRTHDTC;
data Demo_old Demo_young;
    set Demo;
    by AGE;
    
    if first.AGE then output Demo_old;
    if last.AGE then output Demo_young;
run;

Таким образом, мы научились определять первую и последнюю строку в by-группе.

Важно:

data example;
    set Demo;
    by AGE BRTHDTC;
    if first.BRTHDTC then output example;
run;

Оператор RETAIN

Как мы знаем, дата степ выполняется пошагово. За один шаг итерации обрабатывается только одна строка датасета, поэтому, в общем-то, у нас нет прямого доступа к предыдущим или последующим наблюдениям в дата степе. Тем не менее, существуют определенные инструменты, позволяющие «запоминать» значения переменных из предыдущих наблюдений. Среди таких инструментов вам уже должны быть знакомы функции lag и diff (Lesson 2.3). Здесь же мы рассмотрим оператор retain, который дает более широкие возможности.

Постановка задачи: В датасете LB хранятся результаты лабораторных тестов пациента, например это может быть анализ крови на содержание кровяных телец, уровень гемоглобина и т.д. Образцы крови для анализа берутся во время посещений (визитов) места, в котором проводят данное исследование (например, исследования могут проводиться на базе больниц). Такие визиты могут происходить несколько раз за исследование.

Рассмотрим пример датасета LB.

LB

В данном исследовании мы имеем один визит до приема исследуемого лекарства (VISIT=”SCREENING”) и несколько после. Вы можете заметить, что не у всех пациентов набор визитов после SCREENING одинаков. Это естественно, ведь не все пациенты проходят до конца исследования, некоторые заканчивают свое участие раньше или просто пропускают какие-то визиты в середине исследования. Итак, в датасете LB содержится одна запись на каждую уникальную комбинацию значений пациент/тест/визит. Нашей задачей будет создать переменную BASE, которая ‘запомнит’ результаты теста пациента с первого визита для всех последующих. Вот таким образом будет выглядеть итоговый датасет:

LB_base

Возьмите любую by-группу пациент/тест, и вы увидите, что переменная BASE на всех визитах равна LBSTRESN с визита SCREENING. Например, уровень гемоглобина у пациента 001-131 на визите SCREENING (т.е. до приема лекарства) был 148 г/л, это значение сохранено на каждом последующем визите для того, чтобы иметь возможность сравнивать результаты до и после приема лекарства.

Решение: для решения данной задачи воспользуемся оператором retain.

proc sort data=LB;
    by SUBJID LBTESTCD VISIT;
run;

dataLB_base;
    set LB;
    by SUBJID LBTESTCD VISIT;
    
    retain BASE;
    if first.LBTESTCD then call missing(BASE);
    if VISIT = "SCREENING" then BASE = LBSTRESN;
run;

Рассмотрим код пошагово.

I. Мы отсортировали датасет, зная ключ – пациент/тест/визит. Это очень важный шаг. Здесь мы располагаем строки датасета таким образом, чтобы

II. С помощью оператора retain мы создали переменную BASE, которая на каждой итерации дата степа будет повторять свое значение с предыдущего шага, если не задано иное. В данном случае у нас это ‘иное’ задано дважды: