Рассылка закрыта
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
Статистика в SPSS: за пределами кнопочного интерфейса. Выпуск 23
В рассылке используются материалы веб-сайта
www.spsstools.ru
Содержание выпускаСтарый синтаксис в новом обличье, или "Даёшь обобщения"! Новое на сайте www.spsstools.ru Здравствуйте, уважаемые подписчики! Старый синтаксис в новом обличье, или "Даёшь обобщения"!Посвятим нынешний выпуск примерам того, как с помощью макросов можно улучшить (обобщить, сделать более удобным в эксплуатации и повторном использовании) удачный синтаксис, который вам как-то раз довелось написать. Писался синтаксис под конкретную задачу (фиксированное число и имена переменных, фиксированные метки и значения, заданное число параметров). Но если вы чувствуете, что в ходе решения некоторой частной проблемы вам удалось найти удачное решение целого класса потенциально возможных задач, имеет смысл позаботиться о том, чтобы сделать разработанный синтаксис более удобным в дальнейшем использовании. Более удобным - значит, таким, в котором вам не надо будет предварительно разбираться и вспоминать "дела давно минувших дней" для внесения необходимых исправлений под очередную задачу. Более удобный синтаксис - тот, в котором вы, прочтя краткий комментарий к его использованию и изменив пару параметров, можете произвести необходимый анализ или трансформации. Иными словами, это синтаксис, оформленный в своего рода "пользовательскую функцию". Во многих случаях на роль инструмента, позволяющего "обобщить" синтаксис, идеально подходят МАКРОСЫ. Для иллюстрации этого тезиса мы обратимся к двум предыдущим выпускам рассылки. Так решим сразу две задачи: повторение некоторых идей, изложенных в ранее рассмотренных примерах и разбор вариантов того, как сделать эти идеи более универсальными с использованием макроязыка SPSS.
Первый из ранее разбиравшихся примеров относится к 18-му выпуску рассылки: "Подсчёт уникальных значений по большому числу переменных". В качестве "большого числа" мы тогда рассмотрели всего 4 переменные. Рассмотрение иного числа переменных потребовало бы внесения исправлений по всему синтаксису. Не лучше ли узлы, нуждающиеся в правке, заменить на макропеременные, которые достаточно будет определить один раз при вызове макроса? Исходный синтаксис (Число уникальных значений по 400 переменным.SPS), идею которого мы взяли за основу, был написан как раз с использованием макроязыка. При вызове макроса требовалось указать лишь имя первой и последней переменных, а также их общее количество. В 18-м выпуске мы удалили макропеременные и сделали пример более частным, однако сейчас мы готовы прокомментировать исходное решение. Напомню, что суть задачи была - подсчитать количество уникальных значений, которые наблюдались для каждого наблюдения в нескольких переменных. Допустим, если каждая переменная отражает состояние объекта в отдельный момент времени, количество уникальных значений по всем переменным будет означать количество уникальных состояний, в которых побывал объект за исследуемый период. Идея решения состоит в том, что мы создаём копии k переменных, по которым надо подсчитать количество уникальных значений. Затем в рамках цикла рассматриваем значение в i-й исходной переменной, а в рамках вложенного цикла удаляем эквивалентные рассматриваемому значения i+1-й и последующих переменных в наборе дубликатов. Таким образом, к концу общего цикла для каждого наблюдения в наборе дубликатов остаются значения без повторов. Их легко подсчитать посредством функции NMISS (количество пропущенных значений). Более подробные объяснения читатели могут получить непосредственно в 18-м выпуске рассылки из архива. Реализованная на макроязыке задача будет состоять из определения
макроса и его вызова. DATA LIST LIST /var1 var2 var3 varn. BEGIN DATA END DATA. LIST. Теперь определяем макрос. Для того, чтобы решить задачу, потребуется 3 параметра: число переменных, в которых считаем уникальные значения (nbvars), первая переменная из набора переменных (v1) и последняя переменная из набора переменных (v2). То есть, nbvars должен быть равен числу переменных между v1 и v2 включительно. Строго говоря, это число можно было бы и подсчитать в рамках синтаксиса, но это - отдельная задача, которую мы не разберём позже. Сейчас считаем, что пользователь справился с задачей подсчёта переменных самостоятельно. Как следует из описания аргументов, каждый из них принимает одну порцию информации. Аргументы различаем по именам. Имена аргументов указываются в вызове макроса. Напомню, что внутри тела макроса к именам аргументов добавляется восклицательный знак. DEFINE !nb(nbvars=!TOKENS(1) /v1=!TOKENS(1) /v2=!TOKENS(1)) Командой VECTOR создаются два вектора переменных: первый (v) - на основе существующих переменных, от первой, указанной в вызове макроса и до последней, указанной в вызове макроса (на место имён аргументов !v1 и !v2 подставляются их значения - имена переменных), а второй (val) - новый вектор из nbvars переменных. Его размерность совпадает с размерностью вектора v, если при вызове макроса пользователь правильно указал количество. В примере вызова эта строчка с макроаргументами развернётся в следующий синтаксис: "VECTOR v=var1 TO varn / val(4)". VECTOR v=!v1 TO !v2 /val(!nbvars). Больше макроаргументы v1 и v2 не используются: они уже выполнили своё предназначение - создали вектор. Несколько раз по ходу решения встречается макроаргумент nbvars, на место которого подставляется число переменных. В остальном решение полностью совпадает с рассмотренным в 18-м выпуске рассылки. LOOP #cnt=1 TO !nbvars. - COMPUTE val(#cnt)=v(#cnt). END LOOP.
LOOP #cnt1 = 1 TO !nbvars - 1. - DO IF ~MISSING(val(#cnt1)). - LOOP #cnt2 = #cnt1 + 1 TO !nbvars. - IF val(#cnt2)=v(#cnt1) val(#cnt2)=$SYSMIS. - END LOOP. - END IF. END LOOP. Отдельно необходимо сказать про макрофункции. В данном случае нам встретилась весьма часто используемая в макросах функция - !CONCAT. Она используется в тех случаях, когда из имеющихся макроаргументов надо "слепить" какое-то выражение, которое будет "понятно" интерпретатору синтаксиса SPSS. В данном случае мы "на лету" определяем второй аргумент для функции NMISS, состоящий из постоянной части и значения аргумента nbvars. От своего старшего брата - текстовой функции CONCAT, макрофункция !CONCAT отличается тем, что может быть подставлена практически в любое место кода и производит результат, который интерпретируется программой не как текст, а как полноценная (исполняемая) часть кода. Аргументами макрофункций могут быть не только строки, числа и макроаргументы, но и ранее определённые макросы. Последний вопрос, однако, заслуживает отдельного обсуждения. COMPUTE distinct=!nbvars - NMISS(val1 TO CONCAT('val',!nbvars)). Напомним, что последняя команда перед !ENDDEFINE (ADD FILES) используется нами для удаления лишних переменных. Поиск последней лишний переменной, опять же, осуществляется с использованием макрофункции. ADD FILES FILE=* /DROP=val1 TO !CONCAT('val',!nbvars). !ENDDEFINE. Ещё раз привожу всё тело макроса без комментариев, чтобы не запутаться при его определении. Этот код лучше выполнить разом: DEFINE !nb(nbvars=!TOKENS(1) /v1=!TOKENS(1) /v2=!TOKENS(1)) VECTOR v=!v1 TO !v2 /val(!nbvars). LOOP #cnt=1 TO !nbvars. - COMPUTE val(#cnt)=v(#cnt). END LOOP. LOOP #cnt1 = 1 TO !nbvars - 1. - DO IF ~MISSING(val(#cnt1)). - LOOP #cnt2 = #cnt1 + 1 TO !nbvars. - IF val(#cnt2)=v(#cnt1) val(#cnt2)=$SYSMIS. - END LOOP. - END IF. END LOOP. COMPUTE distinct=!nbvars - NMISS(val1 TO !CONCAT('val',!nbvars)). ADD FILES FILE=* /DROP=val1 TO !CONCAT('val',!nbvars). !ENDDEFINE. Теперь можно запустить макрос. Подсчитаем число уникальных значений между переменными var1 и varn включительно. Число переменных равно 4. Попробуйте запустить макрос и с другими параметрами. Таким образом, мы получили возможность выполнять схожие задачи без изменения всего программного кода. !nb nbvars=4 v1=var1 v2=varn.
Второй пример "генерализации" синтаксиса посредством макросов - вычисление процента объяснённой дисперсии в кластерном анализе методом k-средних. Идея описывалась в 15-м выпуске рассылки, однако её воплощение потребовало значительного объема программного кода. Предварительно требовалось переименование и переупорядочивание переменных, а кроме того, для получения разных кластерных решений данный код приходилось каждый раз исправлять. Посмотрим, как можно оптимизировать решение с помощью макросов. Сам алгоритм остаётся прежним (для справки см. 15-й выпуск рассылки). Обратим внимание на то, какая специфика появляется в макрокоде. Открытие данных и сохранение их во временном файле произведём за пределами макроса. GET FILE='C:\Program Files\SPSS\Employee data.sav'. SAVE OUTFILE='c:\temp\mydata1.sav'. Все остальные действия - подсчёт суммарной дисперсии и внутрикластерной дисперсии для каждого кластерного решения - производятся в одном и том же макросе. Для выполнения работы макросу достаточно передать 3 параметра: минимальное число кластеров (kmin), максимальное число кластеров (kmax) и переменные, в которых производится кластеризация (vars). Последний аргумент может принимать заранее неопределённое число составляющих (токенов), так как введён через ключевое слово !CMDEND, т.е. всё, вплоть до окончания команды вызова макроса. Это удобно, так как позволяет вводить любое число переменных. Обратите внимание, что при вызове макроса имена переменных вводятся через запятые - это необходимо, чтобы сработала функция NMISS при отсеве наблюдений с пропущенными данными. Если фильтрацию не производить, вполне можно обойтись и без запятых. С другой стороны, запятые помогают нам подсчитать количество переданных в макрос переменных. Остановимся на вопросе подсчёта переменных подробнее. Знать их число необходимо нам потому, что для каждой мы сохраняем свою статистику и именуем создаваемые переменные в виде x_s1, x_s2 и так далее. Вводить вспомогательные переменные мы можем как x_s1 to x_sn, но для этого n требуется знать. Мы могли бы попросить пользователя предоставлять количество переменных в дополнительном аргументе при вызове макроса, но решили не утруждать его. Сразу после команды DEFINE мы определяем вспомогательный макроаргумент nvars, содержащий пустую строку. Далее мы будем добавлять к этой пустой строке пробелы на каждую встреченную запятую в аргументе vars и по длине результирующей строки определим общее количество переменных. Действуем мы через строки потому, что с арифметикой у макросов туго. Эта тема освещена на сайте www.spsstools.ru достаточно подробно. См., например, http://www.spsstools.ru/MacroTutorial.htm#DoingArithmetic http://www.spsstools.ru/Macros/MacrosVariable/ArithmeticWithMacroVariables.txt http://www.spsstools.ru/Macros/MacrosVariable/MultiplyTwoMacroVariables.txt http://www.spsstools.ru/Macros/MacrosVariable/OperationsWithMacroVariables.txt Далее мы запускаем макроцикл !DO ... !IN ... !DOEND, в котором ещё один дополнительный аргумент tok пробегает по всем токенам аргумента vars. В случае, если в некоторый момент значением tok оказывается запятая, мы увеличиваем количество пробелов в макростроке nvars на единицу. Делается это посредством комбинации макрофункций !CONCAT и !BLANKS. Последняя функция генерирует строку, состоящую из того количества пробелов, которое задано в качестве её аргумента. В данном случае в качестве аргумента мы использовали логическое выражение (!tok=','), которое равно 1, если оно истинно и 0 в противном случае. (Честно говоря, я не думал, что эта конструкция будет дееспособна, однако... РАБОТАЕТ! :-) ) Соответственно, пробел либо добавляется, либо не добавляется. После окончания цикла мы добавляем ещё один пробел (т.к. количество переменных всегда на 1 больше, чем запятых), функцией !LENGTH считаем общую длину строки и записываем результат в ту же макропеременную nvars. Кому-то покажется странным, что аргумент, раньше содержащий строковое значение, безболезненно принимает числовой результат. В случае с макропеременными это - факт. По сути, у макропеременных нет текстовых или числовых значений, а есть просто значение, которое является частью программного кода. Интерпретироваться на наличие числа или текста этот код будет уже позднее, во время исполнения синтаксиса. А макроаргументы работают до исполнения синтаксиса, на этапе его формирования. Подумайте над этим вопросом. Понимание этого тезиса заметно упрощает дальнейшее освоение макросов. Этим, кстати, объясняется и невозможность произведения с макропеременными арифметических действий: как прикажете складывать, вычитать, делить и умножать куски программного кода?! На что ещё следует обратить внимание в предлагаемом макросе... Посмотрите, как мы используем полученное число переменных nvars, а также аргументы kmin и kmax для создания переменных (с помощью макрофункции !CONCAT). Тот же приём, что и в первом примере. Вот что ещё любопытно. Часть кода макроса следует исполнять один раз (ту, которая считает суммарную дисперсию), а часть - несколько раз (по числу запрошенных кластерных решений). Для этого часть с повторениями оформляется в виде макроцикла !DO ... !TO ... !DOEND. В конце макроса (после завершения цикла) мы строим график, отображающий проценты объяснённой дисперсии для каждого варианта решения и удаляем все созданные в ходе вычислений переменные. DEFINE !mac1 (kmin=!TOKENS(1)/ kmax=!TOKENS(1) /vars=!CMDEND) /*Подсчёт числа переданных переменных*/ /*Полагаем, что число переменных = числу запятых + 1*/ !LET !nvars=!NULL !DO !tok !IN (!vars) - !LET !nvars=!CONCAT(!nvars, !BLANKS(!tok=',')) !DOEND !LET !nvars=!LENGTH(!CONCAT(!nvars, !BLANKS(1)))
/*Отфильтровываем наблюдения с пропущенными значениями*/ SELECT IF NMISS(!vars)=0.
/*Считаем стандартные отклонения для каждой переменной и суммарную дисперсию*/ COMPUTE nobreak=1. AGGREGATE DO REPEAT x=x_s1 to !CONCAT('x_s',!nvars) /x_sq=#x_sq1 to !CONCAT('#x_sq',!nvars). - COMPUTE x_sq=x**2. END REPEAT. COMPUTE vartot=SUM(#x_sq1 to !CONCAT('#x_sq',!nvars))*(ncases-1)/ncases. EXECUTE.
/*Строим серию кластерных решений с числом кластеров от kmin до kmax*/ /*Для каждого решения считаем % объяснённой дисперсии (dk)*/ !DO !k=!kmin !TO !kmax QUICK CLUSTER !vars COMPUTE csq=qcl_1**2. AGGREGATE COMPUTE !CONCAT('d',!k)=(vartot-csqs/ncases)*100/vartot. EXECUTE. DELETE VARIABLES csq qcl_1 csqs. !DOEND.
/*Выводим показатели dk на график*/ GRAPH
/*Удаляем лишние переменные*/ DELETE VARIABLES x_s1 to !CONCAT('x_s',!nvars) vartot !CONCAT('d',!kmin) to !CONCAT('d',!kmax) nobreak ncases. !ENDDEFINE.
/*Для сравнения % объяснённой дисперсии для разбиений на kmin, kmin+1, ..., kmax кластеров,*/ /*укажите соответствующие параметры при вызове макроса.*/ /*В параметре vars через запятую перечислите переменные, участвующие в кластеризации.*/
!mac1 kmin=2 kmax=15 vars=salary, salbegin, prevexp,jobtime,educ. NB! Подобная автоматизация кластерного решения, безусловно, удобна и полезна, однако не освобождает исследователя от необходимости думать. Вопросы адекватности самой процедуры кластеризации, выбора подходящих наблюдений и переменных, их стандартизации или иных трансформаций, выбора окончательного варианта решения и его интерпретации, по-прежнему остаются за человеком.
Приятных выходных!
Ведущий рассылки, Балабанов Антон Новое на сайте www.spsstools.ruПереведены и добавлены примеры синтаксиса: Добавить в имена переменных первый символ (или заменить его).SPS Закончить имена переменных обозначением "_99".SPS Создать метки значений из значений строковой переменной.sps Приписать одну и ту же метку нескольким переменным.SPS Приписать порядковые метки значений вектору переменных.SPS Скопировать метку переменной и метки её значений на другие переменные.SPS Изменить формат переменной.SPS
© См. www.spsstools.ru, 2005-2006 |
В избранное | ||