Алгоритм сравнения социально-демографических групп: на примере установок на гражданскую активность

Stacks Image 3
Далее представлен пример визуализации результатов проверки множественных гипотез (тест Вилкоксона) после контроля методом Беньямин-Хохберта.

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

В качестве параметра, подлежащего сравнению, использован индекс построенный на основании следующих индикаторов:
  • Мне все равно какая будет власть, только бы не стало хуже
  • Я свой выбор давно уже сделал, поэтому не хочу принимать
    участие в нынешней политической жизни
  • Какой смысл бороться за свои права, если власть
    своими действиями откровенно их игнорирует

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

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

В этой заметке я сосредоточился на технической реализации, поэтому будет неудивительно, если у Вас возникнут вопросы и критические заменчания.

Анализ данных проведен на массиве данных социологического мониторинга «Украинское общество» 2016 года (Институт социологии НАН Украина).

Для анализа и визуализации использован язык программирования R.

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

US2016$demog <- paste(US2016$r3, US2016$vg, sep = ".")
US2016$demog <- paste(US2016$demog, US2016$V278, sep = ".")
US2016$demog <- paste(US2016$demog, ".", sep = "")
US2016$demog <- as.factor(US2016$demog)
table(US2016$demog)

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

oind <- order(as.numeric(by(US2016$gp1, US2016$demog, median, na.rm = T)),
              as.numeric(by(US2016$gp1, US2016$demog, 
                            function(x)quantile(x, na.rm = T)[2])))    
US2016$demog1 <- ordered(US2016$demog, levels=levels(US2016$demog)[oind])
levels(US2016$demog1)

Осуществляем попарные сравнения всех групп (153 сравнения) с мопощью теста Вилкоксона и записываем их в вектор testM. Важно, чтобы вектор был наименованный. Иначе в дальнейшем будет очень сложно понять, какие пары показали статистически значимые различия. Это реализовано с помощью функции paste().

testM <- c()
x <- 1
while(x < 19) {
    for(i in x:18) {
        if(i != x) {
            testM[paste(levels(US2016$demog1)[i], levels(US2016$demog1)[x], sep = " VS ")] <- 
                wilcox.test(US2016$gp1[US2016$demog1 == levels(US2016$demog1)[i]],
                            US2016$gp1[US2016$demog1 == levels(US2016$demog1)[[x]]])$p.value
        }
    }
    x <- x+1
}

Осуществляем контроль множественных проверок с помощью метода Беньямини-Хохберта. При этом устанавливаем допустимую долю ложно отклоненных гипотез на уровне 5%. Записываем в вектор testF результаты, для которых принята альтернативная гипотеза.

testF <- p.adjust(testM, method = "BH")
testF <- testF[testF < 0.05]

Создаем матрицу forPlot 18 на 18, состоящую из 0. Наименуем строки и столбцы согласно 18 соц-дем группам, которые сравнивались. Создаем список labForMatr, элементы которого содержат информацию о группах, для которых была принята альтернативная гипотеза. Используем этот список для внесения в матрицу единиц для тех пар, по которым была принята альтернативная гипотеза.

forPlot <- matrix(0, ncol = 18, nrow = 18)
rownames(forPlot) <- levels(US2016$demog1)
colnames(forPlot) <- levels(US2016$demog1)

labForMatr <- strsplit(labels(testF), " VS ")

for (i in 1:length(labForMatr)) {
    forPlot[labForMatr[[i]][1],labForMatr[[i]][2]] <- 1
    forPlot[labForMatr[[i]][2],labForMatr[[i]][1]] <- 1 
}

Осуществляем на основании финальной матрицы многомерное шкалирование и готовим массив fpts для визуализации.

distanciya <- dist(forPlot)
fit <- cmdscale(distanciya,eig=TRUE, k=2)
fit # view results
fpts <- as.data.frame(fit$points)
fpts$V1 <- fpts$V1 * -1
fpts$V3 <- row.names(forPlot)

Строим диаграмму (см. рис. ниже) на основании результатов многомерного шкалирования, где точки представляют различные соц-дем группы (понадобятся библиотеки ggplot2 и ggrepel). Делим ее на сектора, позволяющие осуществить интерпретацию различий. Одна вертикальная линия проходит через 0 по оси Y и пересекает координату групп, которые вообще не показали статистически значимых отличий с другими. На диаграмме это молодые мужчины на юго-востоке (СМЧ) и молодые мужчины на западе (ЗМЧ). В свою очередь для демонстрации статистически значимых различий использованы стрелки. Если стрелка уходит от определенной координаты к вертикальной линии, то соответствующая демографическая(-ие) группа(-ы) является статистически отличной(-ыми) от всех групп, находящихся слева от этой линии. Если же стрелка идет к горизонтальной линии, то соответствующая демографическая(-ие) группа(-ы) являются статистически отличной(-ыми) от всех групп, находящихся ниже этой линии.

par(mar=c(2.5,7,2,1))

p <- ggplot(data = fpts, aes(x = V1, y = V2, label=V3))


pfin <- p + geom_point(size = 5, colour = "black", alpha = 0.5) + 
    ggtitle("Індекс: індикатори №1,3,4") + xlab("Вісь Х: активність <=> пасивність") + 
    ylab("Вісь Y: більша відмінність <=> менша відмінність")
    
pfin <- pfin + geom_errorbarh(aes(y=-0.25, xmin=-1.4, xmax=0, height = 0)) +
    geom_errorbarh(aes(y=-0.6, xmin=-1.4, xmax=0, height = 0)) +
    geom_vline(aes(xintercept=0)) +
    geom_curve(aes(x = 1.2, y = 0.5, xend = -0.7, yend = -0.6), linetype="twodash", 
               arrow = arrow(length = unit(0.015, "npc"))) +
    geom_curve(aes(x = 1.75, y = 0.21, xend = -0.5, yend = -0.25), linetype="twodash", 
               arrow = arrow(length = unit(0.015, "npc"))) +
    geom_segment(aes(x = 2.56, y = -0.815, xend = 0, yend = -0.815), linetype="twodash", 
                 arrow = arrow(length = unit(0.015, "npc")))
pfin + 
    geom_label_repel(aes(label = V3),
                     box.padding   = 0.1, 
                     point.padding = 0.2,
                     segment.color = 'grey50',
                     size = 4)
Stacks Image 10
blog comments powered by Disqus