В психологии для исследования когнитивного стиля диапазон эквивалентности применяются различные модификации методики «Свободная сортировка объектов» (Free Sorting Test) Гарднера. В методике обычно используется от 30 до 70 стимулов (понятий). В этой заметке я продеонмстрирую некоторые возможности обработки результатов проведения методики «Свободная сортировка объектов» в R.

Итак, для начала смоделируем результаты проведения методики. Предположим, у нас было 70 стимулов (как в оригинальном варианте, предложенном Гарднером), в исследовании приняли участие 100 человек. Зададим также максимальное число групп, которое могли выделить испытуемые (я взял 20 групп).

n <- 100 # количество испытуемых
k <- 70 # количество стимулов
l <- 3 # минимульное число групп
m <- 20 # максимальное число групп
dataset <- matrix(0, ncol = k, nrow = n)
for (i in 1:n) {
    groups <- sample(l:m, size = 1) # число групп, которое выделил испытуемый
    sorting <- sample(seq_len(groups), size = k, replace = TRUE) # симмулируем сортировку бъектов
    sorting <- paste0("G", sorting) # формируем названия групп
    dataset[i, ] <- sorting # заносим строку в сводную таблицу
}
# задаём имена столбцам
colnames(dataset) <- paste0("S", 1:k)
# преобразуем матрицу в таблицу данных
dataset <- as.data.frame(dataset, stringsAsFactors = FALSE)

Для наглядности приведём часть сгенерированных данных:

head(dataset[, 1:10])
#>    S1  S2 S3  S4  S5  S6  S7 S8  S9 S10
#> 1  G7  G5 G8  G4  G1  G5  G2 G8  G8  G3
#> 2  G4 G11 G9  G5 G11  G9  G4 G2  G4  G7
#> 3  G6  G3 G8  G1  G2  G8  G2 G1  G8  G6
#> 4  G3  G1 G3  G1  G4  G4  G4 G5  G5  G5
#> 5  G2 G15 G5  G7 G16  G4 G14 G9 G13 G12
#> 6 G10  G9 G1 G13 G11 G11  G4 G6 G14  G6

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

После проведения методики оценивают следующие показатели:

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

Для расчёта количественных показателей методики для наших данных напишем функцию, которая будет рассчитывать нужные нам параметры:

free_sorting_summary <- function(x) {
    if (is.list(x))
        x <- unlist(x)
    if (is.character(x))
        x <- as.factor(x)
    # рассчитываем показатели
    freqs <- tabulate(x)                   # количество элементов в каждой группе
    res <- c(n.groups = length(freqs),     # общее число групп
             max.elements = max(freqs),    # число элементов в наибольшей группе
             n1.groups = sum(freqs == 1))  # число групп с 1 элементом
    return(res)
}

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

summary_results <- as.data.frame(t(apply(dataset, 1, free_sorting_summary)))

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

head(summary_results)
#>   n.groups max.elements n1.groups
#> 1       11           10         0
#> 2       11           15         0
#> 3        8           15         0
#> 4        5           17         0
#> 5       16            8         0
#> 6       18            7         3

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

hist(summary_results$n.groups,
     main = "Histogram the number of groups",
     xlab = "Number of groups")

Теперь рассчитаем описательные статистики по всем показателям методики, например, при помощи функции describe() из пакета psych:

library(psych)
describe(summary_results, skew = FALSE)
#>              vars   n  mean   sd median trimmed  mad min max range   se
#> n.groups        1 100 11.84 5.07     12   11.91 5.93   3  20    17 0.51
#> max.elements    2 100 12.16 5.36     10   11.25 2.97   5  28    23 0.54
#> n1.groups       3 100  0.59 0.95      0    0.40 0.00   0   4     4 0.10

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

group_stimuli <- function(x) {
    if (is.list(x))
        x <- unlist(x)
    m <- outer(x, x, "==")  # сравниваем каждый элемент с каждым
    mode(m) <- "integer"    # преобразуем логичческие значения в числовые
    return(m)
}

Теперь попробуем применить эту функцию к данным сортировки. Для этого возьмём данные одного из испытуемых. Ниже приведена только часть вывода, т.к. матрица слишком большая.

group_stimuli(dataset[1, ])[1:10, 1:10]
#>     S1 S2 S3 S4 S5 S6 S7 S8 S9 S10
#> S1   1  0  0  0  0  0  0  0  0   0
#> S2   0  1  0  0  0  1  0  0  0   0
#> S3   0  0  1  0  0  0  0  1  1   0
#> S4   0  0  0  1  0  0  0  0  0   0
#> S5   0  0  0  0  1  0  0  0  0   0
#> S6   0  1  0  0  0  1  0  0  0   0
#> S7   0  0  0  0  0  0  1  0  0   0
#> S8   0  0  1  0  0  0  0  1  1   0
#> S9   0  0  1  0  0  0  0  1  1   0
#> S10  0  0  0  0  0  0  0  0  0   1

Как видим, если на пересечении столбца и строки стоит 1, то эти стимулы были объединениы в одну группу, если 0, соответственно, стимулы находились в разных группах.

На основе полученной матрицы, мы можем визуализировать результаты сортировки отдельного испытуемого. Для этого воспользуемся функцией qgraph() из одноимённого пакета.

library(qgraph)
qgraph(group_stimuli(dataset[1,]), layout = "spring")

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

qgraph(group_stimuli(dataset[1,]), layout = "spring",
       groups = list(C1 = 1:20, C2 = 21:40, C3 = 41:60, C4 = 61:70),
       color = heat.colors(4))

Итак, у нас есть всё, чтобы перейти к анализу групповых результатов. Чтобы получить обобщённые результаты по выборке испытуемых, просуммируем индивидуальные результаты группировки (матрицы совпадения групп стимулов):

# подготавливаем матрицу для заполнения в цикле
grouping_results <- matrix(0, ncol = ncol(dataset), nrow = ncol(dataset),
               dimnames = list(colnames(dataset), colnames(dataset)))
for (i in 1:nrow(dataset))
    grouping_results <- grouping_results + group_stimuli(dataset[i, ]) # суммируем результаты
diag(grouping_results) <- 0

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

qgraph(grouping_results, layout = "spring")

График, прямо скажем, не очень информативный. Для исправления ситуации нам нужно отфильтровать «случаные» связи стимулов. Для начала посмотрим какой диапазон частотк объединения стимулов у нас получился по выблорке:

table(grouping_results)
#> grouping_results
#>   0   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  24 
#>  70   8  16  24  46 146 278 382 508 658 578 614 482 360 330 212 102  38  20  20   6   2

Выполнить фильтрацию при построении графика с помощью функции qgraph() можно, указав аргумент minimum.

qgraph(grouping_results, layout = "spring", minimum = 15)

Последним штрихом в нашем обзоре будет визуализация степени близости стимулов по сумманы выборочным данным. Для этого нам нужно инвертить матрицу группировки, которую мы получали при помощи функции group_stimuli(). Сделать это довольно просто: нам нужно просто заменить оператор ==, которую мы передавали в функцию outer(), на его противополжность, т.е. !=. Затем мы повторяем суммирование по всей выборке.

group_stimuli2 <- function(x) {
    if (is.list(x))
        x <- unlist(x)
    m <- outer(x, x, "!=")  # сравниваем каждый элемент с каждым
    mode(m) <- "integer"    # преобразуем логичческие значения в числовые
    return(m)
}

grouping_results2 <- matrix(0, ncol = ncol(dataset), nrow = ncol(dataset),
               dimnames = list(colnames(dataset), colnames(dataset)))
for (i in 1:nrow(dataset))
    grouping_results2 <- grouping_results2 + group_stimuli2(dataset[i, ]) # суммируем результаты

Для визуализации близости стимулов применив метод многомерного шкалирования, чтобы получить координаты стимулов:

grouping_results2 <- cmdscale(grouping_results2)

Теперь нам осталось только построить график.

library(ggplot2)
#> 
#> Attaching package: 'ggplot2'
#> 
#> The following object is masked from 'package:psych':
#> 
#>     %+%
ggplot(as.data.frame(grouping_results2), aes(x = V1, y = V2, label = colnames(dataset))) +
    geom_point(size = 3) +
    geom_text(hjust = -1) +
    theme(axis.title = element_blank(),
          axis.text = element_blank())