Ввод данных и манипуляции с ними

Прежде, чем заниматься анализом данный их необходимо ввести в систему (создать временный или постоянный файл в двоичном формате SAS, с которым она будет работать при анализе результатов). В принципе возможны два основных способа хранения данных для их последующего анализа: если их мало, то мы можем ввести их сразу же вручную, а если их много, мы можем создать внешний (по отношению к SAS) файл и затем ввести в систему этот файл. В том случае, если данные хранятся в файле, то файл может быть в т.н. чисто текстовом формате (когда для представления данных используются только символы стандартной таблицы ASCII) или же в двоичном формате какой-нибудь другой программы (например MS ACCESS или MS EXCEL). В первом случае предполагается, что одно наблюдение занимает одну строчку, а переменные находятся в столбцах. При этом возможны два варианта расположения этих столбцов (или значений переменных) - между значениями наблюдений стоит знак пробела (как минимум, один) или же значения занимают фиксированные положения в строке. Например, у нас есть три переменных - фамилия, возраст и систолическое АД. В первом случае данные в файле будут выглядеть так:

Ivanov 25 120
Petrov 30 130
Sidorov 28 115

Между значениями стоит пробел, который и говорит SAS, когда закончилась одна переменная и началась другая.

При использовании форматат с фиксированной длинной никаких знаков между переменными нет, они определяются теми столбцами, в которых стоят. Например, описанные выше данные состоят из трех переменных, длина первой из которых (максимальная) составляет 7 знаков (фамилия Sidorov), второй два и третья три знака. Соответственно, первые семь позиций в строке будет занимать фамилия, позиции с восьмой по девятую - возраст и с десятой по двенадцатую - систолическое давление. Для удобства расположения напишем сверху номера позиций (их в окончательной версии файла быть не должно):

         1
123456789012
Ivanov 25120
Petrov 30130
Sidorov28115

Обратите внимание на то, что читабельность такого файла не очень высокая, ее можно повысить за счет пропуска позиций между переменными (например, переменная возраст может начинаться в 9 позиции, а не в восьмой, тогда между именем и возрастом всегда будет пробел). Преимуществом файлов с фиксированной длинной является то, что мы можем вносить в переменные значения с любыми знаками, любыми символами - например создав переменную имя и внося туда разделенные пробелом имя и фамилию обследованного.

Если мы теперь обратимся к тому, как система SAS вносит данные в файлы собственного формата, то обнаружим, что простейшим способом будет внесение данных, разделенных знаком пробела. Нам надо будет только указать имена переменных и их тип, если они не являютя числовыми (на практике мы обычно будем использовать числовые переменные и строковые - такие, как имя, тип лечения и т.п.). Строковые переменные помечаются знаком доллара $. Таким образом для ввода описанных выше трех переменных в систему надо будет написать:

DATA first; (1)
INPUT name $ age sbp; (2)
CARDS; (2)
Ivanov 25 120
Petrov 30 130
Sidorov 28 115
;
RUN; (3)

На первой строке (1) мы указываем название файла SAS куда будут помещаться переменные. Обратите внимание на то, что если файл с подобным именем в системе существует, то он будет уничтожен и на его место будет записан новый файл с данным именем. Сразу следует указать, что SAS может на шаге DATA создавать два типа файлов - временные, как описано в примере выше (они будут удаляться при выходе из программы - окончании данной сессии работы) или постоянные, которые можно будет использовать в другой сессии без необходимости заново вносить данные в систему. Постоянные файлы должны располагаться в определенной папке, называемой библиотекой (library) и подключаемой к системе в любой момент работы с ней при помощи команды

LIBNAME имя_библиотеки имя_папки;

где имя_папки должно быть путем, описанным в соответствии с правилами операционной системы, под управлением которой работает SAS. Например, если мы хотим сохранять наши данные в папке C:\SAS\MYDAT, то мы можем создать библиотеку myfiles, которая будет содержать в себе файлы папки C:\SAS\MYDAT таким образом:

LIBNAME myfiles ’C:\SAS\MYDAT’;

Обратите внимание на то, что указание пути к папке заключено в ординарные кавычки, как строковая переменная.

После того, как мы указали, где располагается библиотека, мы можем сохранять в ней файлы. Для этого надо просто добавить к имени файла через точку имя библиотеки, например так:

DATA myfiles.first;

Теперь файл будет создан в папке, которая было описана при вызове команды LIBNAME и сохранится после выхода из SAS.

Дополнение. Временные файлы в системе SAS хранятся в папке SASWORK в том каталоге, куда SAS была установлена. Этот католог очищяется при завершении работы с SAS. Отсюда следует два вывода - не надо писать вы эту папку файлы, которые вы не хотите потерять и, если произошло аварийной завершение работы, и не удалось сохранить файлы, с которыми Вы работали, поищите их в папке SASWORK, оттуда можно будет их скопировать в другую папку и подключить для дальнейшей работы ее как дополнительную библиотеку.

После указания имени файла, куда вводятся данные мы описываем имена и типы переменных (2).

Затем можно указать, какие надо проводить над данными манипуляции (этого пока в нашем примере нет) и указанием на собственно данные служит команда CARDS. Эта команда существует с времен больших ЭВМ, когда она означала, что компьютер должен быть переведен в режим ожидания перфокарт (cards) с данными. Поскольку многие из современных пользователей уже не знают, что такое перфокарты, команда CARDS в последних версиях заменена командой DATALINES (строки данных) и теперь вы можете использовать либо одну, либо другую команду.

При вводе данных строки не завершаются знаком точки с запятой, единственная точки с запятой, которая отделяет данные от команды RUN, должна стоять на отдельной строке.

В том случае, если данные организованы таким образом, что длина переменных фиксирована, нам надо указать столбцы, в которых находится та или иная переменная, следующим образом:

DATA first;
INPUT name $ 1-7 age 8-9 sbp 10-12;
CARDS;
Ivanov 25120
Petrov 30130
Sidorov28115
;
RUN;

Эти примеры показывают, как можно ввести небольшое количество данных прямо через шаг DATA. Однако выше мы обсуждали, что те же самые данные могут храниться и в текстовом файле на диске. Тогда указание на файл осуществляется при помощи команды INFILE:

DATA first;
INFILE 'C:\MYDAT\first.txt' TRUNCOVER;
INPUT name $ age sbp;
RUN;

Обратите внимание на то, что команда CARDS отсутствует, а указание на файл данных находится до команды INPUT, структура которой аналогична таковой в случае ввода данных напрямую. Ключевое слово TRUNCOVER означает, что иногда в строке последние наблюдения могут отсутствовать и тогда соответствующая строка будет короче, чем другие (“обрезана” - TRUNСated). Система не должна рассматривать это как ошибку и игнорировать “обрезание” строки, считая, что все переменные для которых данные не були указаны имеют “пропущенные” значения.

Одной из проблем при вводе данных для обработки в любую статистическую систему является то, что системы лучще работают с числовыми значениями, а человеку требуется письменное указание на то, какие значения скрываются за тем или иным числовым кодом. Например, мы закодировали в фале пол обследованных следующим образом - 0 - женщины, 1 - мужчины. Однако хотим, чтобы потом, в табличках, нам не надо было напряженно вспоминать, какой код что означает.

Для решения этой задачи SAS использует форматы. Форматы описываются при помощи специальной процедуры - FORMAT. При ее вызове мы должны дать название формату и установить соответствие между кодами и их обозначениями. Для пола создание формата может выглядеть следующим образом:

PROC FORMAT;
VALUE sex 0='female' 1='male';
RUN;

Команда VALUE (которых при одном вызове процедуры FORMAT может быть сколько угодно много) указывает на название формата (sex) и затем описывает какие коды имеют какие значения (код 0 имеет строковое значение female). Теперь необходимо ассоциировать формат с определенной переменной. Делается это при выполнении шага DATA следующим образом (мы вводим файл sbpsex, содержащий идентификационный номер пациента (idn), пол (sex), возраст и уровень систолического артериального давления (sbp)):

DATA firstsex;
INFILE 'C:\MYDAT\sbpsex' TRUNCOVER;
INPUT idn sex age sbp;
FORMAT sex sex.;
RUN;

Обратите внимание на то, что при связывании формата с переменной имя формата заканчивается точкой. Это показывает системе, где тут имена переменных, а где - имена форматов.

Кроме форматных значений для большей удобочитаемости результатов иногда надо расшифорвывать наименования переменных. Если мы вводим ответы на вопрос “Ездит ли обследованный летом на дачу”, а затем задаем еще ряд вопросов о даче, то просто имя переменной ezdacha может оказаться труднорасшифровываемым через полгода после ввода данных. Мы можем ассоциировать с именем переменной ее описание, называемое меткой (LABEL). Делается это также на шаге ввода данных при помощи команды LABEL. Ее структура напоминает структуру команды VALUE в процедуре FORMAT - указывается имя переменной и через знак равенства приводится, в ординарных кавычках, ее описание.

PROC FORMAT;
VALUE yesno 0='нет' 1='до';
VALUE samooc 0='плохо' 1='удовлетворительно' 2='хорошо';
DATA first;
INFILE 'C:\MYDAT\dacha.txt';
INPUT idn ezdacha q3 q4;
LABEL ezdacha='Ездит летом на дачу';
LABEL q3='Выращивает на даче овощи';
LABEL q4='Самочувствие';
FORMAT ezdacha yesno. q3 yesno. q4 samooc. ;
RUN;

В данной программе мы вначале определяем форматные значения для ответов на вопросы, когда возможные варианты “да” или “нет” (формат yesno), а затем для вопросов с тремя вариантами ответов (формат samooc). Обратите внимание на то, что русские названия могут использоваться не только в транслитерированном формате, на латинице. В более новых версиях SAS можно без особых проблем пользоваться кириллическими шрифтами (True Type - стандартные шрифты Windows). Однако в более ранних и самых распространенных версиях SAS (например 6.04) использовавшиеся по умолчанию шрифты не содержали кириллических символов и это вызывало большие проблемы. Как их обойти будет рассмотренно отдельно, при описании использования шрифтов в SAS.

После ввода данных в систему SAS мы указали метки для имен переменных и связали значения переменных с форматными. Теперь при распечатке данных будут указываться форматированные значения вместо кодов и около имени переменной будет находиться ее метка. Чтение распечатки резко облегчается.

Еще одной возможностью ввода данных в систему SAS является использование процедуры IMPORT. Эта процедура позволяет вводить данные из разных программ, включая MS EXCEL и MS ACCESS, формат баз данных dbf, а также различные варианты текстовых форматов с разделителями.

Общая структура процедуры для ввода файла будет таковой:

PROC IMPORT DATAFILE= 'C:\MYDAT\example.xls' OUT= first DBMS=EXCEL REPLACE;
SHEET=sheet1;
GETNAMES=yes;
RUN; 

Данная программа сообщает SASБ что данные содержатся в файле example.xls и должны быть перенесены в файл SAS по названием first (временный файл). Ключевое слово DBMS сообщает SAS в каком формате хранятся данные (это сокращение расшифровывается как Database Management System - система управления базами данных). Надо заметить, что иногда ключевое слово DBMS можно опустить и тогда SAS попытается угадать формат по расширению файла. В данном случае, например, система бы обратила внимание на расширение xls, которое характерно для файлов MS EXCEL и автоматически вызвала бы подпрограмму чтения файлов формата MS EXCEL. При этом, правда, система предполагает, что данные хранятся в самом последнем из известных ей форматов EXCEL. Поскольку фирма Microsoft меняла форматы файлов практически с каждой новой версией Excel, это может привести к сложностям, если, например, на машине установлена не новейшая версия этой програмы, но довольно новая версия SAS или наоборот, новая версия EXCEL и не очень новая версия SAS. Можно и напрямую указать версию EXCEL, если написать DBMS=EXCEL97. В этом случае система SAS загрузит модуль считывания файлов в формате EXCEL97.

Ключевое слово REPLACE приводит к тому, что если файл с именем, указанным после ключевого слова OUT существуетЮ то он будет замещен. Если этого ключевого слова не будет, то при существовании файла с подобным именем система выдаст сообщение об ошибке и импорт произведен не будет.

Относительно MS EXCEL известно, что в нем данные хранятся на различных рабочих листах (sheet) в различных рабочих книгах (workbook, аналогичны файлам). Фактически первая строка процедуры IMPORT указывала на рабочую книгу, из которой мы хотим импортировать данные. Затем нам надо сузить выбор до определенного рабочего листа. Для этого существует команда SHEET. В данном случае мы импортируем первый лист, который содержится в файле и создается по умолчанию - sheet1. Обратите внимание на то, что локализованные - руссифицированные версии MS EXCEL имеют русские наименования листов, что немного усложняет их ввод в систему. Вообще для устранения проблем с обменом данными с MS EXCEL можно порекомендовать сохранять свои данные в формате EXCEL 4, когда еще разбиение на рабочие листы отсутсвовало. Если в рабочей книге имеется только один лист, то команду SHEET можно опустить.

Очень часто пользователи в первой строке таблицы MS EXCEL указывают имена соответствующих переменных. Команда GETNAMES как раз и указывает на то, содержатся ли в первой строке таблицы имена переменных. Если да (GETNAMES=YES), то система автоматически использует их для присвоения имен переменным. Если же этого нет, то SAS сама сгенерирует имена для вводимых переменных.

Процедура IMPORT также может быть использована для импорта данных из такой популярной системы управления базами данных как MS ACCESS. Как известно, в MS ACCESS данные хранятся в файле базы данных в виде таблиц. При вводе в систему SAS необходимо указать имя таблицы и базы данных (т.е. файла):

PROC IMPORT TABLE=mysbp OUT=first DBMS=ACCESS97 REPLACE;
DATABASE='C:\MYDAT\sbp.mdb';
RUN;

Данная программа сообщает SAS, что необходимо найти файл базы данных spb.mdb (команда DATABASE=), из него взять таблицу mysbp (ключевое слово TABLE=) и записать ее во временный файл first (OUT=), причем данные франятся в формате ACCESS 97. Если файл first существует, его надо заменить (REPLACE).

Как и в случае MS EXCEL система SAS может сама догадаться о каком формате базы данных идет речь на основании расширения файла базы данных (mdb). При этом она будет предполагать, что файл записан в последнем из известных ей форматов. Поскольку, как и в случае с EXCEL, формат файлов ACCESS менялся с выпуском каждой новой версии программы, при импорте надо быть крайне аккуратным.

Возможно использование процелуры IMPORT и для импорта текстовых файлов с разделителями. Например, у нас есть файл, в котором переменные отделены друг от друга знаками “решетки” (#). Он хранится в папке C:\MYDAT и его имя sbpdlm.txt. Тогда мы можем написать следующее:

PROC IMPORT DATAFILE='C:\MYDAT\sbpdlm.txt' OUT=first DBMS=DLM REPLACE;
DELIMITER='#';
GETNAME=YES;
RUN;

Мы указываем системе SAS какой файл мы хотим импортировать при помощи ключевого слова DATAFILE. Затем мы сообщаем, что необходимо записать полученные данные во временный файл first и, если такой файл уже существует, заменить его (REPLACE). SAS должна вызвать подпрограмму импорта файлов с разделением (DBMS=DLM), где запомнить название подпрограммы достаточно легко, поскольку это первые три согласных английского слова “разделитель” (DeLiMiter).
Далее команда DELIMITER сообщает системе, какой же именно знак используется в качестве разделителя. Понятно, что можно использовать в этом качестве практически любой знак, что резко увеличивает гибкость при хранении данных по сравнению с разделителем- пробелом. Однако надо помнить, что никто не воспрещает использовать в процедуре IMPORT в качестве разделителя и знак пробела.

Команда GETNAMES, как и в случае импорта файлов MS EXCEL позволяет в файле в первой строке хранить наименования переменных. Если они отсутствуют там (GETNAMES=NO), наименования переменных будут автоматически сгенерированы SAS.

Другие форматы данных, которые поддерживает процедура IMPORT включают файлы, созданные электронными таблицами Lotus 1-2-3 (длительное время de-facto стандарт файлов электронных таблиц) и DBase (формат, который использовался системами управления базами данных DBase, FoxPro, Clipper, Ребус и до сих пор импорт-экспорт в этот формат поддерживают практически все системы управления базами данных для персональных компьютеров).

Если процедура IMPORT позволяет достаточно легко вводить в систему разные файлы, то зачем тогда нужен шаг DATA? Ответов на этот вопрос несколько. Во-первых процедура IMPORT стала официальной частью языка SAS только в версии 8 системы. Правда, до этого существовал Мастер Импорта (Import Wizard), который был доступен в Windows- версиях SAS из меню File. Однако и он отсутствовал в более ранних - MS-DOS версиях системы. Во-вторых, и это главное, шаг DATA позволяет не только вводить данные в систему, но и манипулировать ими. Достаточно часто в биомедицинских исследованиях используются не сами показатели, а различные производные индексы. Одним из примеров таковых является инекс массы телы, известный также как индекс Кетле. Он равен весу пациента, деленному на квадрат его роста. Исследователям настоятельно не рекомендуеться самостоятельно рассчитывать этот индекс и вносить его в качестве отдельной переменной в исходный файл данных из-за неизбежных ошибок округления. Вместо этого мы можем поручить SAS создание новой переменной одновременно с вводом данных. Предположим, что структура исходных данных такова: идентификационный номер пациента (idn), рост (height) и вес (weight). Тогда шаг DATA, вводящий эти сведения из файла myweight.txt будет выглядеть так:

DATA bmi;
INFLIE 'C:\MYDAT\myweight.txt';
INPUT idn height weight;
bmi=weight/height^2;
RUN;

Для модификации данных можно использовать все арифметические операторы (сложения +, вычитания -, умножения *, деления /), операторы возведения в степень (^) и извлечения квадратного корня (sqrt()), а также множество других функций, на которых мы пока останавливаться не будем. Кроме функций, в шаге DATA можно использовать логические операторы, которые позволят сделать подвыборку данных отобрав только те наблюдения, которые соответствуют некоему условию. Например, у нас есть файл sbp.txt, который содержит данные организованные аналогично нашему первому примеру - имя, возраст и уровень систолического артериального давления. Нам надо отобрать только наблюдения для лиц в возрасте от 30 до 40 лет:

DATA sbpage;
INFLIE name $ age sbp;
IF age>=30 AND age<=40;
RUN;

Обратите внимание на то, что в стандартном описании оператора IF приводится немного иная форма:

IF условие THEN действие; ELSE действие;

Если мы опускаем THEN, то такая форма служит как фильтр, оставляя в файле лишь записи, удовлетворяющие данному условию.

Отсюда следует, что мы можем использовать оператор IF для создания новых переменных, значения которых зависят от какого-нибудь условия. Например, мы возвращаемся к расчету индекса Кетле. Предположим, что не у всех пациентов были зарегистрированы значения роста. Это означает, что расчитать значения индекса у них будет невозможно. Опять-таки предположим, что в таких случаях вместо роста стояло нулевое значение. Тогда мы можем улучшить расчет индекса Кетле следующим образом:

DATA bmi;
INFLIE 'C:\MYDAT\myweight.txt';
INPUT idn height weight;
IF height>0 THEN bmi=weight/height^2;
ELSE bmi=.;
RUN;

Обратите внимание на то, что после команды ELSE идет присвоение переменной bmi значения точки. Этот формат записи говорит SAS, что у переменной должно быть “пропущенное” значение. На самом деле, поскольку переменная bmi только создается, эту запись можно было бы опустить, однако в целях безопасности (а вдруг такая переменная существовала) не следует оставлять SAS без указания на то, что надо делать если условие не выполняется.

В ряде случаев может возникнуть необходимость выполнить несколько действий при наступлении определенного условия. Например, в описанной выше ситуации, если рост оказался нулевым мы можем захотеть присвоить пропущенное значение как переменной bmi, так и самому росту. Для этого надо воспользоваться “скобочными” операторами DO; операторы; END;

DATA bmi;
INFLIE 'C:\MYDAT\myweight.txt';
INPUT idn height weight;
IF height>0 THEN bmi=weight/height^2;
ELSE DO;
 bmi=.;
 height=.;
END;
RUN;

Отступ мы ввели для того, чтобы улучшить внешний вид программы, SAS все отступы будет игнорировать. Между скобками DO...END может быть сколько угодно опреаторов, надо только не забывать ставить точку с запятой после DO и после END.

Приведенные выше примеры показывают, что уже при вводе данных их можно модифицировать практически любым способом.

Однако что же делать, если данные хранятся в формате электронных таблиц (например, MS EXCEL), но их надо модифицировать. Для этого данные надо вначале импортировать при помощи Мастера Импорта или процедуры IMPORT, а затем модифицировать при помощи шага данных. В этой ситуации на шаге данных уже нет операторов ввода INPUT, а есть команда указания на файл SET. Эта команда говорит системе, что вновь создаваемый файл должен быть заполнен данными из файла, идущего после этой команды. Например, если все, что нам надо, это создать файл second, являющийся копией файла first, мы введем следующее:

DATA second;
SET first;
RUN;

Естественно, что просто создание копии вряд ли разумно, но нам может понадобиться создать, например, случайную выборку из наших данных. Такой подход используется в рамках исследовательского анализа данных (exploratory data analysis) и состоит в том, что мы случайным образом делим выборку на обучающую (гипотезо-генерирующую) и контрольную (проверочную) группы. В одной мы ищем закономерности, а на другой проверяем, не являются ли найденные различия случайными, проверяем адекватность гипотез, выдвинутых на первом этапе. Вот как подобное деление на две части можно бы было выполнить в системе SAS, выделив из полной выборки подвыборку примерно половинного объема:

DATA second;
 SET first;
 x=RANUNI();
 if x>0.5;
RUN;

В данной программе используется функция RANUNI, которая генерирует последовательность случайных чисел в диапазоне от 0 до 1. Поскольку последовательность случайная, в случае очень большого количества наблюдений 50% будут иметь значения больше 0.5, а 50% - меньше 0.5. Аналогичным образом мы могли бы написать условие для выделения половины наблюдений x<=0.5. Если бы нам понадобилось только 30% наблюдений, то мы бы могли написать x<=0.3.

Однако описанный выше подход не очень хорош, поскольку мы, создав один новый файл, теряем возможность создать второй из второй половины оригинального файла (поскольку при следующем запуске программы с функцией RANUNI будет получена иная последовательность случайных чисел). Можно, конечно, воспользоваться свойством RANUNI с определенным аргументом генерировать всегда одну и ту же последовательность (псевдо)случайных чисел, но это не желательно. Лучше вначале создать в исходном файле новую переменную, которая будет содержать случайное число, а затем уже делить этот файл на желаемое количество частей. Создадим вначале дополнительную переменную в исходном файле:

DATA first;
 SET first;
 x=RANUNI();
RUN;

Обратите внимание на то, что одо и то же название файла использовано и в команде DATA и в команде SET. Это означает, что надо взять файл first, модифицировать его, и записать под именем first опять. Именно при помощи таких записей мы можем легко модифицировать файлы, уже внесенные в систему. Теперь деление файла first на два других - second и third будет выполнено так:

DATA second;
 SET first;
 IF x>0.5;
RUN;
DATA third;
 SET first;
 IF x<=0.5;
RUN;

Обратите внимание на то, что мы воспользовались отсупами, чтобы сделать текст программы более удобочитаемым. В первом шаге DATA мы создаем из файла first набор данных, у которых значение случайной переменной x больше 0.5, а во втором делаем то же самое, только отбираем значения x меньшие, либо равные 0.5.

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

/* Программа деления исходного файла
на две половины случайным образом. 
Исходные данные содержатся в файле first, 
разделенные в файлах second и third */
DATA first;
 SET first;
 LABEL x = 'случайное число';
 x=RANUNI();
RUN;
DATA second;
 SET first;
 IF x>0.5;
RUN;
DATA third;
 SET first;
 IF x<=0.5;
RUN;

Мы использовали уже знакомую команду LABEL для того, чтобы пометить переменную х как содержащую случайное число. Кроме того, мы использовали комментарии для описания работы программы. В SAS комментарии (т.е. строки, которые SAS игнорирует) можно вводить двояко. Если комментарий идет на одной строке, то такая строка начинается со знака звездочки, а заканчивается точкой с запятой. Для более длинных комментариев следует использовать знаки начала и конца комментариев: косую черту со зведочкой (/*) как открывающий знак и звездочку с косой чертой (*/) как закрывающий знак.

Научившись разделять файлы, у нас может появиться желание сделать обратное - слить два или более файлов в один. Здесь возможны два варианта: в файлах находятся данные разных пациентов, иными словами нам надо добавить одни данные в конец другого файла или же у нас есть два файла с разными показателями одних и тех же пациентов. Во втором случае должна существовать некая переменная, которая позволит SAS идентифицировать какая запись принадлежит какому пациенту и правильно “склеить” два файла.

В первом случае для слияния файлов используется уже знакомая нам команда SET:

DATA ngroup;
 SET second third;
RUN;

Мы просто перечислили после команды SET два файла (которые ранее сгенерировали расщеплением файла first). В результате будет создан файл, первую половину которого оставляют наблюдения из файла second, а вторую - из файла third.

Надо заметить, что количество переменных в новом файле будет равно сумме переменных в двух исходных файлах за вычетом переменных с одинаковыми именами. Если какая-то переменная отсутствует в одном из исходных файлов она будет иметь пропущенные значения во всех наблюдениях, пришедших из этого файла. Например, если у нас было два файла, один из которых содержад переменные x и y, а второй x и z, и мы выполним слияние файлов командой SET, то получим следующий файл:

x y z
1  2 .
3  4 .
5  6 .
7  . 8
9  . 10
11 . 12

В данных из первого файла (х от 1 до 5 отсутствуют значения z), а в данных из второго файла (х от 7 до 11) отсутствуют значения y. О таких особенностях команды SET следует помнить, если мы сливаем файлы из разных источников: одна и та же переменная может быть написана в них по-разному, например sbp и sbpd. SAS, естественно, не догадается, что это одна и та же переменная и поставит у пациентов из первого файла пропуски в переменной sbpd, а пациентов из второго - в переменной sbp. Анализ суммарных показателей, ради чего файлы и сливались, будет невозможен.

Если у вас имеются разные наименования переменных придется их переименовать используя все тот же шаг DATA:

DATA sbp2;
 SET sbp2;
 sbp=sbpd;
 DROP sbpd;
RUN; 

В принципе этот код очевиден, мы создаем новую переменную sbp и присваиваем ей значения перменной sbpd. Единственным новым компонентом является необязательная в данном случае команда DROP. Эта команда позволяет убрать из файла переменные с имененем, которое указано после команды (переменных может быть несколько). В данном случае, после присвоения значения переменной sbp, переменная sbpd становится избыточной и поэтому мы ее удаляем. В принципе, если бы мы ее оставили, ничего страшного бы не произошло, если только речь не идет о нескольких миллионах записей, когда лишняя переменная будет означать значительный объем занятого дискового пространтва.

Объединение файлов данных, принадлежащих одним и тем же пациентам осуществляется так же в шаге DATA при помощи команды MERGE. Эта команда требует, чтобы была указана идентифицирующая переменная, по которой SAS определяет одинаковые записи и оба соединяемых файлов были бы отсортированы по этой переменной. Для сортировки файлов используется процедура SORT. Мы можем записать отсортированный файл в новый (используя оператор OUT=), но чаще мы будем использовать тот же файл. Тогда процедура примет простой вид:

PROC SORT DATA=lpart;
 BY idn;
RUN;

Оператор DATA говорит процедуре, какой файл мы хотим отсортировать. Команда BY указывает переменную, по которой будет производиться сортировка. Если нам надо отсортировать сразу же по нескольким полям, то можно после команды BY привести имена нескольких переменных. Тогда система отсортирует наблюдения вначале по первой переменной, а затем расположит наблюдения с повторными значениями первой переменной в соответствии с отсортированными значениями второй. Отсюда следует, что нет особого смысла сортировать по двум или более переменнным, если первая переменная является уникальным идентификатором записи (пациента). Однако идентификатором часто является фамилия и она может повторяться, тогда можно отсортировать наблюдения по фамилии и, например, по возрасту.

После сортировки двух файлов по одной и той же переменной производится слияние файлов с указанием отсортированной переменной (переменных) в качестве ключевого поля:

DATA new;
 MERGE lpart rpart;
 BY idn;
RUN;

В данной ситуации создав файл new, система вносит в него данные из файлов lpart и rpart таким образом, что к каждому наблюдению из lpart добавляются данные из rpart, имеющие то же значение идентификатора idn.

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

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

Запишем теперь программу слияния файлов целиком:

* Слияние файлоы после сортировки по idn;
PROC SORT DATA=lpart;
 BY idn;
RUN;
PROC SORT DATA=rpart;
 BY idn;
RUN;
DATA new;
 MERGE lpart rpart;
 BY idn;
RUN;

В целом программа дублирует то, что было разобрано выше: вначале сортируется по переменной idn первый файл, затем второй, после чего отсортированные файлы сливаются в файле new командой MERGE ... BY. Обратите внимание на другой формат записи комментариев - начало строки звездочка, конец строки - точка с запятой, как и в случае обычных команд SAS.

Итак, мы разобрали, как в SAS, используя шаг DATA можно ввести данные (команда INPUT ... CARDS или INFILE), создать из них подвыборку на основании неких логических условий (команда IF), модифицировать данные, создав копию файла (команда SET) и скомбинировать несколько файлов в один (команды SET и MERGE ... BY).

Иногда исследователю, работающему в системе SAS требуется реструктурировать набор данных, например сделав из одного наблюдения несколько (три переменных, означающих три последовательных измерения у одного человека, а желательно их преобразовать в три наблюдения одной переменной). Иногда требуется выполнить обратную задачу, превратив несколько наблюдений в одно. Все это можно сделать при помощи операторов шага DATA. Разберемся вначале с расщеплением одного наблюдения на несколько. Предположим, что у нас есть файл, в котором находятся следующие данные: идентификационный номер пациента (idn), его пол (sex), обозначемый 1 для мужчин и 0 для женщин, возраст (age) и три последовательных измерения систолического артериального давления (sbp1-sbp3). Нам надо сделать так, чтобы у каждого наблюдения было только одно значение систолического АД (sbp), т.е. одно наблюдение исходного файла было бы расщеплено на три. Вот как мы можем это сделать:

DATA sbp;
INFILE 'C:\MYDAT\sbp.txt';
ARRAY abc[3] sbp1-sbp3;
INPUT idn sex age sbp1-sbp3;
DO i=1 TO 3;
 sbp=abc[i];
 measu=i;
 OUTPUT;
END;
DROP sbp1-sbp3 i;
RUN;

Данная программа использует несколько новых элементов. Во-первых, массивы (ARRAY), во вторых, циклы (DO... TO... END). Вначале мы определяем массив abc, состоящий из трех элементов и называем эти элементы. Для облегчения работы использована упрощенная запись, при помощи которой мы можем перечислить сразу несколько переменных, имена которых отличаются лишь номером. Так, вместо того, чтобы писать sbp1 sbp2 sbp3, мы пишем sbp1-sbp3. Если требуется перечилить с десяток переменных, экономия сил по набору будет значительной.

Далее мы вводим значения из файла используя такую же сокращенную форму записи.

Затем начинается цикл, который присваивает переменной i значения от одного до трех. Переменной sbp присваивается значение измерения, соответствующее значению счетчика цикла i, Мы также присваиваем значение счетчика переменной measu, для того, чтобы потом знать, какому измерению соответствует то или иное значение.

Далее мы выполняем команду OUTPUT, которая записывает настоящие значения всех переменных (включая sbp1-sbp3, idn, sex, age) в файл. В результате у нас формируется файл в три раза больший исходного, в котором содержатся все переменные, включая интересующие на новые sbp и measu. Поскольку во всех утроенных наблюдениях стоят одни и те же значения sbp1-sbp3, мы их удаляем из фанальной версии файла при помощи команды DROP. В результате мы имееем файл, содержащий переменные idn sex age sbp, где на каждый idn приходится по три различных значения sbp.

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

PROC SORT DATA=sbp;
 BY idn measu;
RUN;
DATA sbpc;
 ARRAY abc[3] sbp1-sbp3;
 SET sbp;
 RETAIN sbp1-sbp3;
 abc[i]=sbp;
 IF LAST.measu THEN OUTPUT;
 DROP sbp measu;
RUN;

В данном коде массив abc накапливает данные с первого по третье наблюдение. Команда RETAIN не позволяет SAS забыть значения sbp1-sbp3 при анализе следующего наблюдения. Служебная переменная LAST.measu становится истинной когда SAS просматривает все наблюдения данного пациента (для использования этой команды мы и сортировали исходный файл) и тогда данные заносятся в результирующий файл. Если исследователь точно знает, что для каждого пациента имеетя по три записи (т.е. всегда есть measu=3), можно вместо IF LAST.measu написать IF measu=3. Однако такая запись может сыграть злую шутку, если для каких-то пациентов было только два наблюдения - данные таких пациентов просто окажутся “выкинутыми” из результирующего файла.

В конце мы удаляем из файла ставшие ненужными переменные sbp и measu.

Еще один способ реструктурирования файла данных относится к его транспонированию, т.е. когда мы меняем местами строки и столбцы таблицы. Для этой операции существует специальная процедура, которая так и называется TRANSPOSE.

Для ее использования нам необходимо указать имя файла, который мы собираемся “перевернуть” и имя файла, в который мы запишем результат:

PROC TRANSPOSE DATA=first OUT=tfirst;
RUN;

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