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
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
SPSS AnswerNet

Код решения:	 	100000760	
ПО:		 	SPSS Base 	
Версия:	 		
ОС:	 		
Тип вопроса:		 	Синтакс/пакетный режим/Скрипты
Подтип вопроса:		 	Преобразования данных
Заголовок:
Сложная выборка "с близнецами" без возвращения. 

Описание:

Вопрос. 
В файле данных есть записи о трёх типах испытуемых. Первый тип
представляет для меня особый интерес в моём исследовании. 
Второй испытуемых - это члены семьи первых. Третий тип - люди, которые не
связаны родственными отношениями с первыми двумя типами. 
Мне требуется сопоставить записи первого типа с записями первых двух типов
так, чтобы они подходили друг другу по полу и возрасту. При этом нужно 
гарантировать, что ни в какой паре не окажутся люди из одной и той же
семьи. А кроме того - что ни один человек не попадёт в выборку дважды.
Просто не знаю, в каком направлении начать поиски.

Ответ. 
Задача нетривиальная, но способ есть. Идея решения - 
для начала сопоставить каждому ID человека все ID других людей, 
которые подходят по комбинации пол/возраст.
Затем пары ID из таких перечней случайным образом отбираются в
выборку, после чего соответствующие ID исключаются из рассмотрения.
Найденные пары записываются в файл 'YOKED.SAV'. Те, для кого
пары не нашлось, записываются в файл 'UNPAIRED.SAV'.
Следующая программа может быть изменена таким образом, чтобы
допускать более, чем 20 записей в каждой различимой комбинации пол/возраст.

data list FREE/ FAMILY AGE GENDER Z PRIMARY. 

BEGIN DATA 
1 1 1 2 1 1 2 1 4 2 2 3 2 2 1 2 4 2 3 2 3 3 2 3 1 3 2 1 2 2 
4 4 2 3 1 4 3 2 1 2 5 1 1 3 1 5 2 2 2 2 6 3 1 2 1 6 4 1 2 2 
7 2 2 2 1 7 3 2 1 2 8 2 1 2 1 8 3 2 1 2 9 1 1 1 1 9 2 2 3 2 
10 3 2 9 1 10 2 1 1 2 11 2 1 2 1 11 2 1 2 2 12 3 1 2 1 12 3 2 1 2 
13 1 1 3 1 14 2 2 1 1 15 1 2 2 1 16 2 1 5 1 17 4 2 2 1 18 3 2 1 1 
19 4 1 5 1 20 4 1 1 1 21 1 2 5 1 22 3 2 4 1 23 4 1 5 1 24 2 2 8 1 
25 4 1 2 1 26 3 2 1 1 27 2 1 5 1 28 4 1 1 1 29 3 2 3 1 30 2 2 6 1 
31 4 1 4 1 32 3 1 2 1 33 1 2 2 1 34 3 1 1 1 35 4 2 1 1 36 2 1 4 1 
END DATA . 
COMPUTE OLDSEQ=$CASENUM. 
SAVE OUTFILE 'RAWDATA.SAV' . 

* Сделаем так, чтобы записи первого типа содержали информацию об объекте второго типа из той же семьи *. 
* Создадим для этого дополнительную переменную *. 
SORT CASES BY FAMILY (A) PRIMARY (D). 
IF FAMILY=LAG(FAMILY) TWIN=LAG(OLDSEQ). 

* Пересчитаем все записи внутри каждой страты ВОЗРАСТ/ПОЛ (AGE/GENDER) *. 
SORT CASES BY AGE GENDER . 
IF (MISSING(LAG(OLDSEQ))) STCNT=1. 
IF (AGE=LAG(AGE) AND GENDER=LAG(GENDER)) STCNT=LAG(STCNT) +1. 
IF (MISSING(STCNT)) STCNT=1. 

* Распределим информацию об ID-номерах всех кандидатов на "спаривание" по всем наблюдениям в пределах категории пол/возраст *. 
* Оставим только записи первого типа *. 
VECTOR ID_(20). 
COMPUTE ID_(STCNT)=OLDSEQ. 
AGGREGATE OUTFILE 'C:\\TEMP\\TMP.SAV' 
/ PRESORTED/ BREAK=AGE GENDER 
/ TWIN=FIRST(TWIN) / ID_X01 TO ID_X20 = MAX(ID_1 TO ID_20). 
SELECT IF PRIMARY=1. 
MATCH FILES FILE=* / TABLE='C:\\TEMP\\TMP.SAV' / BY AGE GENDER. 

* Проверяем, находится ли текущая запись в той же страте, что и предыдущая *.
* Если так, эта запись "наследует" перечень ID-номеров кандидатов на "спаривание" *. 
* (IMHO, проверка и копирование лишние - предыдущая команда MATCH FILES уже размножила нужные ID - А.Б.)
COUNT MAXVALID=ID_X01 TO ID_X20 (LO THRU HI). 
DO IF AGE=LAG(AGE) AND GENDER=LAG(GENDER) . 
+ DO REPEAT ID=ID_X01 TO ID_X20 . 
+ COMPUTE ID = LAG(ID) . 
+ END REPEAT. 
END IF. 

* Зададим вектор с ID возможных кандидатов *. 
VECTOR ID_X=ID_X01 TO ID_X20 . 

* Важно не сопоставить запись самой себе! *. 
DO IF NOT MISSING (ID_X(STCNT) ). 

* Обнулим флаги состояния *. 
+ DO REPEAT INIT=TAKEN FOUND NTRYS . 
+ COMPUTE INIT=0. 
+ END REPEAT. 

* Ведём случайный отбор пар ID в векторе, копируем в YOKE и уничтожаем оригинал *. 
+ LOOP. 
+ COMPUTE WHICH=TRUNC(UNIFORM(MAXVALID))+1. 
+ IF WHICH <> STCNT YOKE=ID_X(WHICH). 
+ DO IF (NOT (ANY(YOKE,TWIN,OLDSEQ,$SYSMIS))) . 
+ COMPUTE ID_X(WHICH)=$SYSMIS . 
+ COMPUTE ID_X(STCNT)=$SYSMIS . 
+ COMPUTE FOUND=1. 
+ END IF . 
+ END LOOP IF FOUND. 
ELSE. 
COMPUTE TAKEN=1. 
END IF. 

* Разделим все записи на нашедшие пару и ненашедшие. Оставим информацию об их ID * . 
TEMPORARY . 
SELECT IF (MISSING (YOKE) AND NOT(TAKEN) ). 
SAVE OUTFILE 'UNPAIRED.SAV' . 
SELECT IF (NOT (TAKEN) AND NOT MISSING (YOKE) ). 
SAVE OUTFILE 'TMP' / KEEP OLDSEQ YOKE. 
GET FILE 'TMP'. 
COMPUTE GROUP=$CASENUM . 

* Преобразуем пары из одной записи в пары записей *. 
VECTOR V = OLDSEQ TO YOKE . 
LOOP X=1 TO 2. 
COMPUTE OLDSEQ=V(X). 
XSAVE OUTFILE 'TMP2' / KEEP GROUP X OLDSEQ . 
END LOOP. 
EXECUTE. 

* Добавим к полученным ID исходные данные, не участвовавшие в отборе * В ДАННОМ СЛУЧАЕ - ПЕРЕМЕННАЯ Z * . 
GET FILE 'TMP2'. 
SORT CASES BY OLDSEQ. 
MATCH FILES FILE = * / TABLE = 'RAWDATA.SAV' / BY OLDSEQ . 
SORT CASES BY GROUP. 
******************************************************** . 
* Распространим значения данных на пары записей: * . 
* вам потребуется сделать обобщение этой процедуры на несколько переменных * . 
* КАК-ТО ТАК... * . 

* VECTOR ID(2). * . 
* DO REPEAT VAR = Y Z . * . 
* VECTOR VAR(2) . * . 
* COMPUTE VAR(X) = VAR . * . 
* END REPEAT . * . 
* AGGREGATE ...../ Y1 Y2 Z1 Z2 = MAX(Y1 Y2 Z1 Z2).* . 
******************************************************** . 
VECTOR Z(2) / ID(2). 
COMPUTE Z(X) = Z. 
COMPUTE ID(X)=OLDSEQ. 
AGGREGATE OUTFILE * / PRESORTED / BREAK=GROUP GENDER AGE 
/ Z1 TO Z2 = MAX(Z1 TO Z2) / ID1 TO ID2 = MAX(ID1 TO ID2). 
SAVE OUTFILE 'YOKED.SAV'. 
LIST. 

* Результаты! * . 
GROUP GENDERAGE Z1 Z2 ID1 ID2 
1 1 1 2 3 1 9 
2 1 1 1 3 17 25 
3 2 1 2 5 27 33 
4 1 2 2 4 15 48 
5 1 2 2 1 21 20 
6 1 2 5 4 28 2 
7 1 2 5 2 39 6 
8 2 2 2 2 13 10 
9 2 2 1 3 26 18 
10 2 2 8 6 36 42 
11 1 3 2 2 11 23 
12 1 3 2 1 44 46 
13 2 3 2 3 3 5 
14 2 3 9 3 19 41 
15 2 3 1 1 30 8 
16 2 3 4 1 34 38 
17 1 4 5 2 31 12 
18 1 4 1 5 32 35 
19 1 4 2 1 37 40 
20 2 4 3 1 7 47 
21 2 4 2 3 29 4 
Number of cases read: 21 Number of cases listed: 21