Осваиваем макросы
Выучив десяток команд, вы сможете автоматизировать генерацию тысяч строк синтаксиса!
Оглавление
- Основы
- Макросы и символ завершения команд
- Макрос — всего лишь (!) генератор строк
- Пример макроса с детальными комментариями (извлекаем n независимых выборок объема m)
- Замена символа во множестве строковых переменных
- Меняем формат для множества переменных (несколько примеров, от простых к сложным)
- Версия 1: меняем формат переменной со строчного на строчный, с числового на строчный и со строчного на числовой (имя переменной остаётся прежним)
- Версия 2: то же, что и выше, но так, чтобы обработать 5–10 переменных одним вызовом макроса
- Версия 3: то же, что и выше, но обрабатываем ВСЕ числовые или строковые переменные, которые находятся между 2-мя заданными переменными в файле данных.
- Арифметика с макропеременными (для опытных пользователей)
- Еще один пример макроса с детальными комментариями создаем столбцовые диаграммы, исключая категории, в которых меньше n наблюдений)
Сообщите мне, если вам есть что добавить к содержанию этой страницы, или если какой-то вопрос раскрыт недостаточно ясно.
Основы
Чтобы приступать к изучению макросов, вы должны иметь понятие о синтаксисе SPSS.
Идея макроса аналогична понятию функции. Вы определяете его, а затем — вызываете. Макрос достаточно определить один раз за сессию работы с SPSS Statistics. Это делается серией команд, заключённых между инструкциями DEFINE
и !ENDDEFINE
. Сессия начинается, когда вы запускаете SPSS Statistics и заканчивается когда вы закрываете это приложение. Таким образом, даже если вы работаете в рамках одной сессии с разными файлами данных, разными окнами выдачи и синтаксиса, макрос всё ещё определён в оперативной памяти и может быть вызван без переопределения. Однако, если вы закрыли SPSS Statistics и запустили его снова, макрос потребуется определять заново. Это несложно сделать, если он сохранён у вас в файле синтаксиса.
Для краткости, далее мы здесь будем именовать SPSS Statistics просто SPSS.
Определения макроса могут быть размещены в любой части файла синтаксиса, поскольку они получают «приоритет» на обработку, т.е. команды определения макроса обрабатываются перед тем, как будут обработаны все остальные команды.
Вот очень пример очень простого макроса:
DEFINE !stats (myvars=!CMDEND) FREQUENCY VARIABLES= !myvars /ORDER= ANALYSIS. !ENDDEFINE.
Вы можете вызвать этот макрос на выполнение следующей командой
!stats myvars = age sex status.
Здесь age sex и status – имена существующих в файле данных переменных, для которых вы хотите построить таблицы частот.
Инструкция DEFINE
(первая строка определения макроса) задаёт имя макроса (в данном случае – !stats); имя не может быть длиннее 8 символов. Эта инструкция также говорит, что при вызове макроса вы должны указать переменные, представляющие для вас интерес в качестве значения аргумента myvars. Это сделано при вызове макроса: «myvars=age sex status».
Важно, что хотя аргумент определён как «myvars» в инструкции DEFINE
, при обращении к нему внутри макроса следует пользоваться именем «!myvars».
Как только макрос определён, строка
!stats myvars = age sex status.
имеет тот же эффект, что и строка
FREQUENCY VARIABLES= age sex status /ORDER= ANALYSIS.
Вы можете сказать «Ну и в чём здесь преимущество?» Оно в том, что макросы могут делать куда более сложные вещи, чем генерация одной команды синтаксиса. Такие вещи, которые без макросов потребовали бы тысячи строк кода и долгие часы работы. При использовании макроса вам достаточно одной строки, чтобы вызвать выполнение сотни строк кода. Причём параметры (аргументы) макроса можно задавать каждый раз разные. На этом сайте находится около 140 различных макросов. Их проще всего искать, указывая ключевое слово !ENDDEFINE
в строке поиска по сайту. (Стоит отметить также отдельную страницу на этом сайте, "SPSS макросы Кирилла", с множеством продвинутых, тщательно разработанных макросов от Кирилл Орлова.)
Макросы и символ завершения команд
Точки как символы окончания команды должны заканчивать каждую команду в файле синтаксиса при работе в "интерактивном режиме" (через окно синтаксиса). (Существует еще "пакетный режим", возможный при использовании команды INSERT, но мы не будем обсуждать его здесь. "Интерактивный режим" подачи синтаксиса на исполнение применяется в SPSS по умолчанию, даже при использовании INSERT, и строгая рекомендация - всегда оканчивать команды синтаксиса точкой.)
Однако, последовательность макрокоманд, заключённых между DEFINE
и !ENDDEFINE
- то что называется телом макроса, - расценивается как словно одна команда синтаксиса. Естественно, она заканчивается точкой после !ENDDEFINE
. Но нет необходимости (и даже не рекомендуется) ставить точку после каждой макро-языковой инструкции в теле. В некоторых случаях постановка точки даже приведёт к сбою (см., например, этот макрос). Иногда это способно «сбить с толку» процессор SPSS. Отсюда — общее правило: не ставить точки после макроинструкций внутри макроса. Например, !DO !var=1 !TO !nbfiles
есть макроинструкция и не нуждается в постановке точки после неё.
Разумеется, внутри макроса часто встречаются обычные команды синтаксиса, которые необходимо заканчивать точкой, например:
SAVE OUTFILE=!QUOTE(!CONCAT("c:\Temp\new file ",!var,".sav")).
поскольку SAVE... это команда регулярного синтаксиса (оказавшаяся в теле макроса), не макроинструкция, а команды синтаксиса должны завершаться точкой. (Данная SAVE содержит в себе макровыражение, в форме макросной функции !QUOTE, верно, однако SAVE - это команда синтаксиса.)
Макрос — всего лишь (!) генератор строк
Основная вещь, которую надо понять о том, как работают макросы, это та, что макрос является простым синтаксическим анализатором («string parser»). Макрос выполняется целиком ещё до того, как начинается обработка первого наблюдения данных. Затем начинается видимая нам обработка синтаксиса, который сгенерировал макрос при своём исполнении. Иными словами, когда мы запускаем макрос, SPSS подставляет на место аргументов со значками «!» переданные параметры и генерирует код (синтаксис) (в английских текстах можно встреить термин macro's expansion, характеризующий этот процесс), который затем, после того как «выполнился» весь макрос, начинает исполняться, т.е. SPSS начинает непосредственную обработку данных.
Если мы это поняли, становится ясно, что макроинструкция вроде
!IF (!age !GT age) !THEN ... !IFEND
лишена смысла. лишена, ибо age - переменная, чье значение (обычно) варьирует по наблюдениям; а поскольку макрос «развёртывает» свой код ещё до начала обработки данных, то обработчику не ясно, выполнено условие !IF
в отношении того или иного конкретного наблюдения, или нет. Макрос ничего не знает о значениях ваших данных.
С другой стороны, такая команда в макросе приемлема
COMPUTE years2 = 65 - !age.
поскольку, "развертываясь", макрос способен подставить на место !age
значение (константный скаляр или имя некоторой переменной), приданное в качестве аргумента при вызове макроса либо присвоенное в теле макроса инструкцией !LET
). Здесь такая замена никак не обусловлена значением конкретного наблюдения переменной, которое неизвестно до начала выполнения синтаксиса.
Пример макроса с детальными комментариями
Я привожу макрос, а затем описываю назначение каждой строки в соответствии с её номером. Синтаксис составлен в ответ на вопрос «Как получить n независимых выборок объёма m из одного и того же файла?». Размещён в SPSSX-L, автор rlevesque@videotron.ca, 14.04.2001.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 1 PRESERVE. 2 SET MPRINT=no. 3 *///////////////////////////////. 4 DEFINE !getsamp (nbcases=!TOKENS(1) /nbfiles=!TOKENS(1) ) 5 !DO !var=1 !TO !nbfiles 6 GET FILE='C:\Program Files\SPSS\GSS93 subset.sav'. 7 COMPUTE draw=UNIFORM(1). 8 COMPUTE dummy=1. 9 RANK VARIABLES=draw BY dummy /RANK INTO rank1. 10 SELECT IF rank1 LE !nbcases. 11 SAVE OUTFILE=!QUOTE(!CONCAT("c:\Temp\new file ",!var,".sav")). 12 !DOEND 13 !ENDDEFINE. 14 *///////////////////////////////. 15 *Следующая строка начинает весь процесс. 16 SET MPRINT=yes. 17 !getsamp nbcases=300 nbfiles=4. 18 RESTORE. |
Определение макроса начинается с четвёртой строки и заканчивается строкой 13 (содержимое между ними это тело макроса). Макрос вызывается (пускается на исполнение) в 17-й строке. Когда макрос вызывается, он "развертывается", и регулярный SPSS-синтаксис, получающийся в развертке, запускается процессором.
Номера строк и комментарии к строкам:
- Данная команда работает в паре с командой RESTORE из строки 18. Читаем в Syntax Guide про эту команду: «Инструкция PRESERVE запоминает установки команды SET; Запомненные установки потом могут быть восстановлены с помощью RESTORE. Использование этих команд особенно полезно при написании макросов. Пары PRESERVE—RESTORE могут иметь вложения до 5 уровней». Дальше мы будем менять параметры SET, поэтому лучше запомнить исходные.
- Я использую установку SET MPRINT всякий раз когда пишу или отлаживаю макрос. Для большей читаемости содержимого окна результатов я предпочитаю устанавливать этот параметр в
NO
во время определения макроса и вYES
прямо перед вызовом макроса (см. строку 16). - Я привык«огораживать» определения макросов последовательностью из однотипных символов. См. строки 3 и 14. Это позволяет легко находить определения макросов среди длинных файлов синтаксиса.
- Начало определения макроса. Даём ему имя !getsamp; Я привык давать имена макросам, начинающиеся с «!», что даёт легко отличать вызов макросов среди прочих команд синтаксиса. Макрос требует задания 2 аргументов, которые называются nbcases и nbfiles. Так, если nbcases=500 и nbfiles=40, макрос сгенерирует 40 файлов с независимыми выборками по 500 объектов в каждой. Каждая выборка пишется в отдельный файл.
- Данная строка (макроинструкция !DO..., означающая циклы) образует пару со строкой 12 (завершение циклов). Макропеременная !var «пробежит» все значения от 1 до значения аргумента !nbfiles, которое задаётся при вызове макроса.
NB!: Имя аргумента в строке 4 не начинается с «!», но все ссылки на этот аргумент внутри макроса должны начинаться с «!» перед его именем. - Загружает файл данных в оперативную память. В том виде, как есть, макрос потребуется изменять всякий раз, когда изменится имя файла или путь к нему. Было бы удобно определить путь к файлу в качестве дополнительного параметра. В таком случае макрос бы имел 3 аргумента вместо 2. Потом мы могли бы запускать этот макрос, не открывая его в окне синтаксиса через команду INSERT и указывать нужное имя файла в качестве аргумента, например.
- Эта строка создаёт новую переменную draw, в которую записывает случайное число между 0 и 1 для каждого наблюдения из файла данных.
- Эта строка определяет вспомогательную переменную dummy, которая равна 1 для каждого наблюдения из файла. Она понадобится позже, в строке 9.
- Команда ранжирует наблюдения по значениям переменной draw в порядке возрастания. Для каждого наблюдения его порядок сохраняется в переменной rank1. Так, наблюдение с наименьшим значением переменной draw получает ранг 1, второе – ранг 2 и т.д.
- Ясно, что поскольку был использован генератор случайных чисел, каждое наблюдение имело одинаковые шансы оказаться первым по рангу, вторым и т.д. Поэтому, когда в строке номер 10 мы отбираем только те переменные, которые имеют ранг между 1 и значением аргумента
!nbcases
(т.е., фактически берём первые!nbcases
наблюдений из файла данных), мы делаем случайный отбор из файла, представляющего собой совокупность. - Этой командой мы сохраняем результат выборки во внешний файл данных. Когда макропеременная !var1 равна 1, файл с первой выборкой получает имя «new file 1.sav», когда !var2=2 – имя «new file 2.sav» и так далее до 4. См. Syntax Reference Guide для более полного комментария к использованию !QUOTE и !CONCAT.
- Конец цикла по !var.
- Конец определения макроса.
- См. комментарии к строке 3.
- Эта строка – просто комментарий.
- Если макрос отлажен, можно заменить «yes» на «no», чтобы сократить объём лога (LOG – журнальные записи) в окне результатов (OUTPUT).
- Вызов макроса:
!getsamp nbcases=300 nbfiles=4.
Этот вызов задаёт создание 4 файлов со случайными выборками по 300 объектов в каждой. Аналогично запрос!getsamp nbcases=300 nbfiles=1000.
вернёт 1000 файлов со случайными выборками.
Вот так вот. Определение макроса (10 строк) плюс строка вызова макроса могут заменить собой 10 000 строк «обычного» синтаксиса, которые бы потребовались без макросов. Теперь должно быть понятно, что макросы очень полезны.
Обратите внимание, что если целью извлечения выборок было подсчитать средние, стандартные отклонения или какие-то другие статистики по каждой выборке и затем сопоставить их, этот макрос мог бы быть переписан таким образом, что эти статистики записались бы в качестве наблюдений в один файл, т.е. макрос бы вернул не 4, 100 или 1000 файлов, а 1 файл с результатами.
Определение макроса (строки с 4 по 13) достаточно запустить (run) однократно в сессии работы с SPSS. Конечно, если вы что-то измените в теле макроса, нужно будет перезапусить ("перезагрузить") определение макроса. Совет: если вы щелкните трижды правой кнопкой мыши где-либо внутри тела макроса, все определение макроса с DEFINE по !ENDDEFINE окажется выделено. Это очень удобно для того, чтобы запустить определение макроса.
Замена символа во множестве строковых переменных
В упражнениях на синтаксис по преобразованию строк для замены точек на запятые был использован следующий синтаксис:
LOOP IF CHAR.INDEX(name2,".")>0. COMPUTE SUBSTR(name2,CHAR.INDEX(name2,"."),1)=",". END LOOP.
Использование такого метода для 100 переменных потребует 300 строк кода! Рассмотрим несколько похожих ситуаций и подыщем для каждой подходящее решение.
- Пример 1. Допустим, что строковые переменные, нуждающиеся в замене символов, имеют связанные имена (например, var1, var2, var3, …, или любые другие символы вместо var), но необязательно следуют одна за другой в файле.
- Пример 2 Переменные имеют несвязанные имена, но расположены последовательно в файле данных (например, vara, name, address, …, product – следуют в файле одна за другой).
- Пример 3. Предположим, что переменные имеют несвязанные имена и расположены непоследовательно в файле данных, однако все строковые переменные между первой из указанных и последней из указанных должны быть обработаны.
- Пример 4 . Условия — как в предыдущем примере, кроме этого некоторые из строковых переменных не должны подвергнуться обработке.
Файл Замена символов во многих строковых переменных.SPS включает все 4 примера, они разобраны ниже.
Замена символов, пример 1
Строковые переменные, нуждающиеся в замене символов, имеют связанные имена (например, var1, var2, var3, …, или любые другие символы вместо var), но необязательно следуют одна за другой в файле.
* Сгенерируем данные. INPUT PROGRAM. VECTOR var(100A8). LOOP id=1 TO 3. LOOP cnt=1 TO 100. COMPUTE var(cnt)="txt.abc". END LOOP. END CASE. END LOOP. END FILE. END INPUT PROGRAM. EXECUTE. * Следующий метод не будет работать, поскольку функция SUBSTR требует имени реальной переменной. * Элемент вектора v(cnt) она как имя переменной не распознаёт. VECTOR v=var1 TO var100. LOOP cnt=1 TO 100. LOOP IF CHAR.INDEX(v(cnt),".")>0. COMPUTE SUBSTR(v(cnt),CHAR.INDEX(v(cnt),"."),1)=",". END LOOP. END LOOP. EXECUTE. * Тут поможет использование DO REPEAT. DO REPEAT v=var1 TO var100. LOOP IF CHAR.INDEX(v,".")>0. COMPUTE SUBSTR(v,CHAR.INDEX(v,"."),1)=",". END LOOP. END REPEAT. EXECUTE. * Использование следующего макроса также возможно. SET MPRINT=no. */////////////////////. DEFINE !replace(vname=!TOKENS(1) /nbvars=!TOKENS(1) /oldchar=!TOKENS(1) /newchar=!TOKENS(1)) * Для замены символа в множестве строковых переменных. !DO !cnt=1 !TO !nbvars LOOP IF CHAR.INDEX(!CONCAT(!vname,!cnt),!oldchar)>0. COMPUTE SUBSTR(!CONCAT(!vname,!cnt),CHAR.INDEX(!CONCAT(!vname,!cnt),!oldchar),1) =!newchar. END LOOP. !DOEND EXECUTE. !ENDDEFINE. */////////////////////. SET MPRINT=yes. * Макрос вызывается следующим образом. !replace vname=var nbvars=100 oldchar='.' newchar=','. * В данном случае решение с DO REPEAT оказывается более простым, чем макрос, но стоит помнить, что в цикле DO REPEAT невозможно использование процедур с пересылкой аргументов, т.е. решение через макрос оказывается более общим.
Замена символов, пример 2
Переменные имеют несвязанные имена, но расположены последовательно в файле данных (например, vara, name, address, …, product – следуют в файле одна за другой).
* Очень удобно было бы в данном случае просто указать макросу имена первой и последней переменных. * Один из «избранных» макросов (см. «Задать список переменных указанием первой и последней переменной») может нам в данном случае помочь. * Сгенерируем данные. INPUT PROGRAM. STRING strone text34 str2 beta gamma alpha (A8). LOOP id=1 TO 3. DO REPEAT var=strone text34 str2 beta gamma alpha. COMPUTE var="txt.ab.c". END REPEAT PRINT. END CASE. END LOOP. END FILE. END INPUT PROGRAM. EXECUTE. SAVE OUTFILE='c:\temp\testdata.sav'. * Допустим, мы хотим обработать все переменные между text34 и gamma включительно. Данное решение предполагает, что у нас на самом деле есть сотни переменных между этими двумя и нам не очень удобно перечислять их все при вызове макроса. * Обратите внимание, что данное решение может быть использовано и когда вам нужно обработать все переменные, начиная с некоторой, но вы по каким-то причинам не можете указать имя последней переменной. Тогда вы просто создаёте ещё одну строковую переменную (она размещается в конце файла) и указываете её имя в качестве последней переменной. * Note: when you save the macro Gem in your macro folder, keep ONLY the macro definition, in other words, delete the data definition and the example of macro call, INSERT FILE='c:\Program Files\SPSS\macros\DefineListOfVariablesBetweenTwoVariables.sps'. * Вызовем макрос, который даст нам список всех переменных, расположенных между двумя указанными нами. !DefList var1=text34 var2=gamma fname='c:\temp\testdata.sav'. * Макрос вызывается следующим образом. SET MPRINT=yes. !rep_chr oldchar='.' newchar=',' vnames=!list1. * Определение макроса !rep_chr дано в примере 4 ниже.
Замена символов, пример 3
Предположим, что переменные имеют несвязанные имена и расположены непоследовательно в файле данных, однако все строковые переменные между первой из указанных и последней из указанных должны быть обработаны.
* Используем решение, аналогичное примеру 2. * Сгенерируем данные. INPUT PROGRAM. STRING strone text34 str2 beta gamma (A8). LOOP id=1 TO 3. DO REPEAT var=strone text34 str2 beta gamma. COMPUTE var="txt.ab.c". END REPEAT PRINT. END CASE. END LOOP. END FILE. END INPUT PROGRAM. EXECUTE. * Вставим числовую переменную перед последней строковой. COMPUTE numvar=2. STRING alpha(A8). COMPUTE alpha="txt.a.b.c". SAVE OUTFILE='c:\temp\testdata.sav'. INSERT FILE='c:\Program Files\SPSS\macros\DefineListOfVariablesBetweenTwoVariables.SPS'. * Вызовем макрос, который даст нам список всех переменных, расположенных между двумя указанными нами. !DefList var1=text34 var2=alpha fname='c:\temp\testdata.sav'. * Макрос вызывается следующим образом. SET MPRINT=yes. !rep_chr oldchar='.' newchar=',' vnames=!list1. * Обратите внимание, что будут возникать «ошибки» когда макрос попытается модифицировать числовые переменные (в частности, переменную numvar). Для них преобразования не пройдут. Однако на эти ошибки можно не обращать внимание, т.к. числовые переменные мы и так трогать не хотели. * Определение макроса !rep_chr дано в примере 4 ниже.
Замена символов, пример 4
Условия — как в предыдущем примере, кроме этого некоторые из строковых переменных не должны подвергнуться обработке.
* В данном случае имена тех переменных, которые должны быть обработаны, должны в явном виде быть заданы в макросе.< INPUT PROGRAM. STRING strone text34 str2 name alpha (A8). LOOP id=1 TO 3. DO REPEAT var=strone text34 str2 name alpha. COMPUTE var="txt.ab.c". END REPEAT PRINT. END CASE. END LOOP. END FILE. END INPUT PROGRAM. EXECUTE. * Определяем следующий макрос. SET MPRINT=no. */////////////////////. DEFINE !rep_chr(oldchar=!TOKENS(1) /newchar=!TOKENS(1) /vnames=!CMDEND) /* To replace a character in many string variables */ !DO !vname !IN (!vnames) LOOP IF CHAR.INDEX(!vname,!oldchar)>0. COMPUTE SUBSTR(!vname,CHAR.INDEX(!vname,!oldchar),1)=!newchar. END LOOP. !DOEND EXECUTE. !ENDDEFINE. */////////////////////. * Допустим, что мы хотим заменить точки на запятые в переменных text34, str2 и alpha. При этом они не последовательно расположены в файле данных. * Макрос вызывается следующим образом. SET MPRINT=yes. !rep_chr oldchar='.' newchar=',' vnames=text34 str2 alpha. * Разумеется, если нужно заменить символы «a» на «b», макрос следует вызвать в следующем виде. !rep_chr oldchar='a' newchar='b' vnames=text34 str2 alpha.
Меняем формат для множества переменных (несколько примеров, от простых к сложным)
Версия 1: меняем формат переменной со строчного на строчный, с числового на строчный и со строчного на числовой (имя переменной остаётся прежним)
* Этот макрос полезен, когда вам нужно преобразовать… - строки одного типа в строки другого типа (ss); - числовые переменные в строчные (ns); - строковую переменную в числовую (sn), - cохраняя при этом исходное имя переменной. * rlevesque@videotron.ca 23.05.2001. */////////////////////. * nformat = the new format. * type of change = ss (string to string), ns or sn where n=numeric and s=string. !IF (!type='ss') !THEN STRING temp1234(!nformat). COMPUTE temp1234=!vname. !IFEND !IF (!type='ns') !THEN STRING temp1234(!nformat). COMPUTE temp1234=LTRIM(STRING(!vname,F18.0)). !IFEND !IF (!type='sn') !THEN COMPUTE temp1234=NUMBER(!vname,F18.0). FORMAT temp1234(!nformat). !IFEND MATCH FILES FILE=* /DROP=!vname. RENAME VARIABLE (temp1234=!vname). !ENDDEFINE. */////////////////////. * Генерируем данные, чтобы продемонстрировать работу макроса. DATA LIST LIST /var1(A8) var2(A8) var3(F8.0). BEGIN DATA abcdefg 254 235 adadad 128 265 END DATA. LIST. * Пример 1. !convert vname=var1 nformat=A5 type=ss. * Эта команда конвертирует переменную var1 из строкового формата A8 в строковый же формат A5 * Пример 2. !convert vname=var2 nformat=F8.2 type=sn. * Эта команда конвертирует переменную var2 из строкового формата A8 в числовой формат F8.2. * (Разумеется, если в строковой переменной содержатся строки, которые не могут быть интерпретированы как числа, соответствующее значение в числовой переменной будет пропущено). * Пример 3. !convert vname=var3 nformat=A8 type=ns. * Эта команда конвертирует переменную var3 из числового формата F8.2 в строковый формат A8
Версия 2: то же, что и выше, но так, чтобы обработать 5–10 переменных одним вызовом макроса, обобщение
Допустим, нам нужно поменять формат у нескольких переменных (5–10–20) с одним и тем же преобразованием формата (ss, sn или ns). Мы сейчас изменим предыдущий макрос так, чтобы он за один вызов мог преобразовать все эти переменные.
SET MPRINT=yes. */////////////////////. DEFINE !conver2 (type=!TOKENS(1) /cformat=!TOKENS(1) /nformat=!TOKENS(1) /vnames=!CMDEND) где… * type = тип преобразования (см. предыдущий случай). * cformat = предыдущий формат (требуется лишь когда type=ns чтобы точнее преобразовать числа в строки без лишних нулей после запятой и т.д.). * nformat = новый формат. * vnames= список имён переменных для замены формата. !DO !vname !IN (!vnames) !IF (!type='ss') !THEN STRING temp1234(!nformat). COMPUTE temp1234=!vname. !IFEND !IF (!type='ns') !THEN STRING temp1234(!nformat). COMPUTE temp1234=LTRIM(STRING(!vname,!cformat)). !IFEND !IF (!type='sn') !THEN COMPUTE temp1234=NUMBER(!vname,!nformat). FORMAT temp1234(!nformat). !IFEND MATCH FILES FILE=* /DROP=!vname. RENAME VARIABLE (temp1234=!vname). !DOEND !ENDDEFINE. */////////////////////.
* Проверяем макрос (используем только 4 переменные вместо потенциально десятков или сотен). DATA LIST LIST /var1(A8) vnum2(A8) varx(A8) vary(A8). BEGIN DATA 1235.23 254.13 235.00 6532.20 53261.32 128.09 265.85 2591.99 END DATA. LIST. * Изменяем формат так, чтобы в результирующей числовой переменной было 3 знака после запятой. !conver2 type=sn nformat=F8.3 vnames=var1 vnum2 varx vary.
* Вот листинг работы макроса из окна результатов. Обратите внимание, что это своего рода «отладка» — вместо макропеременных, имена которых начинаются с «!», подставлены значения конкретных переменных, имена которых были переданы в качестве аргументов — А.Б. 199 M> 200 M> COMPUTE TEMP1234=NUMBER( var1 , F8.3 ). 201 M> FORMAT TEMP1234( F8.3 ). 202 M> 203 M> MATCH FILES FILE=* /DROP= var1. 204 M> RENAME VARIABLE (TEMP1234= var1 ). 205 M> 206 M> 207 M> COMPUTE TEMP1234=NUMBER( vnum2 , F8.3 ). 208 M> FORMAT TEMP1234( F8.3 ). 209 M> 210 M> MATCH FILES FILE=* /DROP= vnum2. 211 M> RENAME VARIABLE (TEMP1234= vnum2 ). 212 M> 213 M> 214 M> COMPUTE TEMP1234=NUMBER( varx , F8.3 ). 215 M> FORMAT TEMP1234( F8.3 ). 216 M> 217 M> MATCH FILES FILE=* /DROP= varx. 218 M> RENAME VARIABLE (TEMP1234= varx ). 219 M> 220 M> 221 M> COMPUTE TEMP1234=NUMBER( vary , F8.3 ). 222 M> FORMAT TEMP1234( F8.3 ). 223 M> 224 M> MATCH FILES FILE=* /DROP= vary. 225 M> RENAME VARIABLE (TEMP1234= vary ). 226 M> . 227 M>
325 326 | * Конвертируем результат обратно в строки (но лишь с одним знаком после запятой).
!conver2 type=ns cformat=F8.1 nformat=A8 vnames=var1 vnum2 varx vary.
|
* Вот листинг работы макроса из окна результатов. Обратите внимание, что это своего рода «отладка» — вместо макропеременных, имена которых начинаются с «!», подставлены значения конкретных переменных, имена которых были переданы в качестве аргументов — А.Б.
233 M> 234 M> STRING TEMP1234( A8 ). 235 M> COMPUTE TEMP1234=LTRIM(STRING( var1 , F8.1 )). 236 M> 237 M> 238 M> MATCH FILES FILE=* /DROP= var1. 239 M> RENAME VARIABLE (TEMP1234= var1 ). 240 M> 241 M> STRING TEMP1234( A8 ). 242 M> COMPUTE TEMP1234=LTRIM(STRING( vnum2 , F8.1 )). 243 M> 244 M> 245 M> MATCH FILES FILE=* /DROP= vnum2. 246 M> RENAME VARIABLE (TEMP1234= vnum2 ). 247 M> 248 M> STRING TEMP1234( A8 ). 249 M> COMPUTE TEMP1234=LTRIM(STRING( varx , F8.1 )). 250 M> 251 M> 252 M> MATCH FILES FILE=* /DROP= varx. 253 M> RENAME VARIABLE (TEMP1234= varx ). 254 M> 255 M> STRING TEMP1234( A8 ). 256 M> COMPUTE TEMP1234=LTRIM(STRING( vary , F8.1 )). 257 M> 258 M> 259 M> MATCH FILES FILE=* /DROP= vary. 260 M> RENAME VARIABLE (TEMP1234= vary ). 261 M> .
Версия 3: то же, что и выше, но обрабатываем ВСЕ числовые или строковые переменные, которые находятся между 2-мя заданными переменными в файле данных
Фактически, это то же, что и версия 2, но здесь мы его используем вместе с одним из «Избранных» макросов. Допустим, нам регулярно надо менять формат 600 переменных с одним и тем же типом трансформаций ((ss, sn или ns).
!DEfList var1=g var2=k fname="c:/temp/mydata.sav". * Определение макроса !DefList (задать список переменных) можно найти здесь. * Макрос, приведённый выше, создаёт макропеременную !list1, содержащую список * всех переменных в файле c:/temp/mydata.sav между переменными var1 и var2. * Этот список затем используется в качестве аргумента для макроса !conver2: !conver2 type=ns cformat=F8.1 nformat=A8 vnames=!list1.
Этот вызов макроса конвертирует все переменные между g и k (предполагается, что они числовые) в строки. Имена переменной остаются такими же. Меняется только формат.
Замечание: если лишь некоторые переменные между g и k являются числовыми, мы можем использовать макрос !vartype (из той же группы «избранных» макросов) для получения списка только числовых (или только строковых) переменных. С его помощью мы укажем «правильный» список переменных в вызове !conver2
.
Арифметика с макропеременными (для опытных пользователей)
Напрямую выполнять арифметические действия с макропеременными нельзя. Допустим, при условии, что есть следующие макроинструкции:
!LET !a=1 !LET !b=2
следующая инструкция недопустима:
!LET !c = !a + !b
Вот обходной путь получения суммы !a и !b.
!LET !c = !LENGTH(!CONCAT(!BLANK(!a),!BLANK(!b))
Мы создаём две пустые строки с длинами !a и !b, затем объединяем их и с помощью функции !length записываем в !с длину итоговой строки – А.Б. Не вполне человечный способ… Попробуйте выполнить деление (или вычислить логарифм !b) таким образом!
Допустим, нам требуется, чтобы макропеременная !myvar содержала результат достаточно сложных вычислений, например, ln(1), ln(2) или ln(3). Вообще – любое количество вычислений и любая функция могут быть использованы, эти 3 просто иллюстрируют метод. Обходной маневр заключается в том, что мы сначала вычисляем числовые переменные в файле данных, затем записываем их во вспомогательный макрос, который затем запускаем на выполнение и извлекаем из него значения.
SET MPRINT=yes. *///////////////////////////////////. DEFINE !doit(nbval=!TOKENS(1)) INPUT PROGRAM. LOOP cnt=1 TO !nbval. COMPUTE myval=LN(cnt). END CASE. END LOOP. END FILE. END INPUT PROGRAM. * Допустим, мы хотим сохранить 5 знаков после запятой. FORMAT myval(F8.5). * Запишем значения макропеременной. DO IF cnt=1. WRITE OUTFILE='c:\temp\macro var.sps' /"DEFINE !values()" /myval. ELSE IF cnt<!nbval. WRITE OUTFILE='c:\temp\macro var.sps' /myval. ELSE. WRITE OUTFILE='c:\temp\macro var.sps' /myval /"!ENDDEFINE.". END IF. EXECUTE. * Запустим только что созданный синтаксис чтобы поместить в оперативную память его тело (содержащее ничто иное как значения переменной myval). INSERT FILE='c:\temp\macro var.sps'. EXECUTE. !ENDDEFINE. *///////////////////////////////////. !doit nbval=3.
В следующем макросе переменная !myvar поочерёдно принимает значения ln(1), ln(2) и ln(3).
DEFINE !test() !LET !cnt=!NULL !LET !val=!EVAL(!values) !DO !myvar !IN (!val) !LET !cnt=!CONCAT(!cnt,!BLANK(1)) COMPUTE !CONCAT(var,!LENGTH(!cnt))=!myvar. !DOEND EXECUTE. !ENDDEFINE. !test. * Изменим формат переменной чтобы увидеть все 5 знаков. FORMAT var1 TO var2 (F8.5).
Вот результат работы (листинг) приведённого выше синтаксиса.
Еще один пример макроса с детальными комментариями
Этот макрос создает столбцовые диаграммы, скрывая столбцы, частота для которых составляет не менее, чем n наблюдений.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | SET MPRINT=no. *////////////////////. DEFINE !chart2 (minnb=!TOKENS(1) /cats=!CMDEND) /* minnb = минимальная частота */. /* cats = имена категориальных переменных, для которых нужны диаграммы */. COMPUTE dummy=1. !DO !cat !IN (!cats) FILTER OFF. * Sorting is necessary for the MATCH FILES command. SORT CASES BY !cat. !LET !fname=!QUOTE(!CONCAT('C:\temp\',!cat,'.SAV')) AGGREGATE /OUTFILE=!fname /PRESORTED /BREAK=!cat /nb = N(dummy). MATCH FILES /FILE=* /TABLE=!fname /BY !cat. FREQUENCY VARIABLES=!cat. LIST. COMPUTE flag=(nb>=!minnb). FILTER BY flag. GRAPH /BAR(SIMPLE)=COUNT BY !cat /MISSING=REPORT. * Удаляем переменную nb. MATCH FILES FILE=* /DROP=nb. !DOEND * Удаляем переменную dummy. MATCH FILES FILE=* /DROP=dummy. !ENDDEFINE. */////////////////// SET MPRINT=yes. !chart2 minnb=5 cats= age prog year. EXECUTE. |
Определение макроса начинается на 3 строке и заканчивается на 32. Макрос вызывается в 35 строке.
- Я использую установку
SET MPRINT
всякий раз когда пишу или отлаживаю макрос. Для большей читаемости содержимого окна результатов я предпочитаю устанавливать этот параметр вNO
во время определения макроса и вYES
прямо перед вызовом макроса (см. строку 34). - Я привык«огораживать» определения макросов последовательностью из однотипных символов. См. строки 2 и 33. Это позволяет легко находить определения макросов среди длинных файлов синтаксиса.
- Начало макроса. Его имя: !chart2. Я привык давать имена макросам, начинающиеся с «!», что даёт легко отличать вызов макросов среди прочих команд синтаксиса. Макрос требует задания двух аргументов: minnb и cats. Если, например, minnb=5 и cats=var1 var2 var3, макрос построит столбцовые диаграммы для переменных var1, var2 и var3, в которых будут отображены категории, имеющие частоту, не меньшую, чем 5. Обратите внимание, что точка в конце команды определения макроса - требуется (
!ENDDEFINE
больше похожа на команду синтаксиса, чем макроинструкцию). Поскольку у нас три значения аргумента (три переменных) в !cats, синтаксис внутри цикла !DO будет исполнен 3 раза. - Просто комментарий. Внутри макроса надёжнее начинать комментарии символами /* и заканчивать символами */.
- Вторая строка комментария.
- Переменная dummy будет использована в 16-й строке.
- Инструкция
!DO
работает в паре с инструкцией!DOEND
из строки 29. Вместе они формируют циклDO
. Это выражение означает, что макропеременная!cat
поочерёдно примет каждое значение из тех, что мы указали в аргументе!cats
в 35 строке. В данном случае сначала цикл будет исполнен со значением!cat=age
, затем –prog
, затем –year
. - Выключает фильтр. Все наблюдения становятся видимыми для процедур SPSS. В строке 23 некоторые наблюдения отфильтровываются. Строка 8 отменяет для следующего цикла то, что делала строка 23 для предыдущего.
- Комментарий.
- Сортирует файл по возрастанию значений переменной
!cat
. То есть, в первом цикле наблюдения будут сортироваться по значению переменнойage
, во втором – по значениямprog
, и т.д. - Определяем макропеременную
!fname
. Когда!cat=age
, мы имеем
!fname=!QUOTE(!CONCAT('C:\temp\',age,'.SAV'))
!fname=!QUOTE(C:\temp\age.SAV)
!fname="C:\temp\age.SAV" - Команда
Aggregate
(строки с 12 по 16) подсчитывает частоту каждой категории переменной!cat
. Например, у нас может быть 3 человека в возрасте 25, 7 – в возрасте 26 и т.д. Результат сохраняется в файл, чьё имя мы определили выше в макропеременной!fname
. - Эта подкоманда задаёт имя файла в который процедура
AGGREGATE
сохранит результат. - Поскольку файл был уже отсортирован в строке 10, это ключевое слово уведомляет SPSS, что заново сортировать не надо. Когда файл заранее отсортирован, пакету требуется меньше ресурсов памяти для выполнения агрегирования. И сама команда, кстати, выполнится быстрее.
- Эта инструкция говорит, что нужно использовать переменную, которую представляет
!cat
в качестве группирующей (BREAK
). - В дополнение к группирующей переменной результирующий файл будет содержать переменную nb с числом наблюдений для каждой категории. Например, если
!cat=age
, результирующий файл может содержать такие наблюдения, как
Наблюдение 1: age=25, nb=5
Наблюдение 2: age=26, nb=7
и т.д. - Сопоставляет рабочий файл с…
- файлом, имя которого содержится в макропеременной
!fname
(мы туда только что записали результаты). - Мы сопоставляем по ключу, которым являются категории переменной, чьё имя содержится в
!cat
. Переменная nb добавляется к текущему файлу. Так, если для наблюдения 1 исходного файла age=25, значение nb будет равно 5. Если для наблюдения 2 исходного файла age=26, значение nb будет равно 7. Если для наблюдения 3 исходного файла age=25, значение nb будет снова равно 5, и так далее. - Формируем таблицу частот переменной !cat. Просто чтобы проверить, что впоследствии в построенный график войдут лишь категории с частотой, не меньше 5.
- Печатаем все наблюдения просто для уверенности. Эта линия должна быть прокомментирована при выполнении реальной работы.
- Рассчитываем переменную
flag
таким образом, что она принимает значение 1 для наблюдений, для которыхnb >= !minnb
. Другими словами, те наблюдения, которые должны войти в построение столбцовой диаграммы, помечаются единицей. Остальные будут иметь значение 0. - Эта строка делает наблюдения, у которых
flag=0
«невидимыми» для SPSS. Как будто их вовсе нет в файле данных. Но они снова станут «видимыми» как только мы выполним командуFILTER OFF
. - Эти
- строки
- задают формирование столбцовой диаграммы. Разумеется, можно заменить этот график любым другим.
- Комментарий.
- Здесь необходимо удалить переменную
nb
, иначе в следующей итерации цикла строки 17-19 не смогут добавить переменнуюnb
(поскольку она уже имеется). - Конец цикла DO. Программа переходит к строке 7. И следующий цикл обрабатывает следующую переменную, заданную в
!cats
. - Комментарий.
- Удаляем переменную
dummy
, которая больше не нужна. - Конец определения макроса.
- См. комментарий к строке 2.
- См. комментарий к строке 1.
- Вызов макроса. Тут уже всё должно быть ясно.
...