Подготовка данных

В этом практикуме мы пока не будем работать с готовыми данными, загруженными из файла, мы создадим свои! И заодно посмотрим на то, как в R можно (псевдо)случайным образом сгенерировать выборки.

Для генерации выборок в R есть функция sample(). Она позволяет выбрать из некоторого набора (вектора) желаемое число элементов.

# три случайных числа от 1 до 10
sample(1:10, 3)
## [1]  9 10  4

Если мы хотим получать выборки с повторениями, нам понадобится аргумент replace = TRUE. Особенно актуально его использовать, если мы выбираем число элементов, которое превосходит объем вектора, из которого мы осуществляем выбор:

# без replace никак
# нельзя выбрать 10 уникальных значений из 5
sample(1:5, 10, replace = TRUE)
##  [1] 4 1 1 5 2 1 1 3 1 5

Мы же пойдем еще дальше и зафиксируем вероятности, с которыми те или иные значения должны встречаться в нашей выборке. Эти вероятности вписываются в аргумент prob:

answer01 <- sample(c("Да", "Нет", "Нет ответа"), 200, 
                   replace = TRUE, prob = c(0.4, 0.5, 0.1))
answer01
##   [1] "Нет ответа" "Нет"        "Да"         "Да"         "Да"        
##   [6] "Нет"        "Нет ответа" "Нет"        "Да"         "Да"        
##  [11] "Да"         "Нет"        "Нет"        "Нет ответа" "Нет"       
##  [16] "Да"         "Нет"        "Да"         "Нет"        "Да"        
##  [21] "Да"         "Нет"        "Нет"        "Нет"        "Да"        
##  [26] "Нет"        "Нет"        "Да"         "Да"         "Да"        
##  [31] "Нет"        "Нет"        "Нет"        "Нет"        "Нет"       
##  [36] "Нет"        "Нет"        "Да"         "Нет"        "Да"        
##  [41] "Нет"        "Да"         "Да"         "Да"         "Нет"       
##  [46] "Нет"        "Да"         "Да"         "Нет"        "Нет"       
##  [51] "Нет ответа" "Да"         "Да"         "Да"         "Да"        
##  [56] "Да"         "Нет"        "Нет"        "Нет ответа" "Нет"       
##  [61] "Нет"        "Да"         "Да"         "Да"         "Нет"       
##  [66] "Да"         "Нет ответа" "Нет"        "Нет"        "Да"        
##  [71] "Нет ответа" "Нет"        "Нет"        "Да"         "Нет"       
##  [76] "Нет ответа" "Да"         "Да"         "Да"         "Нет"       
##  [81] "Нет"        "Нет"        "Нет"        "Да"         "Да"        
##  [86] "Нет ответа" "Да"         "Да"         "Да"         "Нет"       
##  [91] "Нет"        "Нет"        "Нет"        "Нет"        "Нет"       
##  [96] "Да"         "Нет"        "Да"         "Нет ответа" "Нет"       
## [101] "Да"         "Нет"        "Нет"        "Нет"        "Нет"       
## [106] "Нет"        "Нет"        "Да"         "Нет"        "Да"        
## [111] "Да"         "Нет"        "Да"         "Да"         "Да"        
## [116] "Нет"        "Да"         "Да"         "Нет ответа" "Нет ответа"
## [121] "Нет ответа" "Нет"        "Нет ответа" "Да"         "Нет"       
## [126] "Нет"        "Нет ответа" "Нет"        "Нет"        "Нет"       
## [131] "Нет"        "Нет"        "Нет"        "Нет"        "Да"        
## [136] "Нет"        "Нет"        "Да"         "Нет ответа" "Нет"       
## [141] "Нет"        "Нет"        "Нет"        "Да"         "Нет"       
## [146] "Нет"        "Да"         "Нет"        "Нет"        "Да"        
## [151] "Да"         "Да"         "Да"         "Да"         "Да"        
## [156] "Нет"        "Да"         "Нет"        "Да"         "Нет"       
## [161] "Нет"        "Да"         "Нет"        "Нет"        "Нет"       
## [166] "Да"         "Нет"        "Да"         "Да"         "Нет"       
## [171] "Нет ответа" "Нет"        "Да"         "Да"         "Нет"       
## [176] "Да"         "Да"         "Нет ответа" "Нет"        "Да"        
## [181] "Нет ответа" "Нет"        "Да"         "Нет"        "Нет"       
## [186] "Нет"        "Нет"        "Да"         "Нет"        "Да"        
## [191] "Да"         "Да"         "Да"         "Нет"        "Нет"       
## [196] "Нет"        "Нет ответа" "Нет ответа" "Да"         "Да"

Что мы сейчас сделали? Сгенерировали выборку объема 200 из ответов «Да», «Нет», «Нет ответа» таким образом, чтобы доля ответивших «Да» была примерно равна 0.4, доля ответивших «Нет» — равна 0.5, а доля отсутствующих ответов — равна 0.1.

Создадим аналогичным образом еще три выборки. Предположим, что мы имитируем проведение одного и того же опроса в четырех временных точках:

answer02 <- sample(c("Да", "Нет", "Нет ответа"), 200, 
                   replace = TRUE, prob = c(0.68, 0.22, 0.1))
answer03 <- sample(c("Да", "Нет", "Нет ответа"), 200, 
                   replace = TRUE, prob = c(0.5, 0.35, 0.15))
answer04 <- sample(c("Да", "Нет", "Нет ответа"), 200, 
                   replace = TRUE, prob = c(0.5, 0.4, 0.1))

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

Важно: строки с set.seed() и кодом для генерации выборок нужно запускать одновременно, то есть выделить их и запустить одним разом, не последовательно друг за другом.

Теперь объединим полученные выборки в таблицу — датафрейм. Для этого мы воспользуемся функцией cbind.data.frame(), которая похожа на cbind() для склеивания векторов в матрицу:

dat <- cbind.data.frame(answer01, answer02, answer03, answer04)

# первые строки dat
head(dat)

Добавим названия столбцам таблицы:

colnames(dat) <- c("январь 2020", "январь 2021", 
                   "январь 2022", "январь 2023")

Посмотрим на таблицу в отдельной вкладке RStudio через функцию View():

View(dat)

Строим базовый график

В рамках этого практикума мы будем строить столбиковую диаграмму для числа ответов. Столбиковая диаграмма в R строится не на основе «сырых» данных, а на основе готовой таблички с частотами. Например, такой:

# выбрали столбец январь 2020 
# по названию
table(dat$`январь 2020`)
## 
##         Да        Нет Нет ответа 
##         81         98         21

Столбиковую диаграмму мы будем строить сразу для всех временных точек (четыре группы по три столбца с ответами «Да», «Нет», «Нет ответа»). Поэтому давайте посчитаем частоты для каждого столбца и объединим по столбцам в матрицу:

counts <- cbind(table(dat$`январь 2020`), 
                table(dat$`январь 2021`),
                table(dat$`январь 2022`), 
                table(dat$`январь 2023`))
counts
##            [,1] [,2] [,3] [,4]
## Да           81  138   88   97
## Нет          98   45   77   86
## Нет ответа   21   17   35   17

Для удобства добавим названия столбцов — они такие же, как в исходном датафрейме dat:

colnames(counts) <- colnames(dat)
counts
##            январь 2020 январь 2021 январь 2022 январь 2023
## Да                  81         138          88          97
## Нет                 98          45          77          86
## Нет ответа          21          17          35          17

Теперь построим самый базовый вариант графика. Нам понадобится функция barplot() с аргументом beside = TRUE, так как мы хотим получить столбиковую диаграмму по группам (группы по три столбца рядом друг с другом):

barplot(counts, 
        beside=TRUE, 
        col=c(), 
        border="white",
        xlab="R хороший?", 
        ylab = "Число ответов", 
        font.lab = 1,
        font.axis = 2,
        legend = rownames(counts))

Улучшаем график

Как это иногда бывает, возникла проблема с масштабом и положением легенды на графике. Давайте это поправим!

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

png("Bar.png", width = 9, height = 4, units = 'in', res = 300)

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

png(...)

# код
# код

dev.off()

Во-вторых, скорректируем положение легенды — добавим аргумент args.legend:

png("Bar.png", width = 9, height = 4, units = 'in', res = 300)
barplot(counts, 
        beside=TRUE, 
        col=c(), 
        border="white",
        xlab="R хороший?", 
        ylab = "Число ответов", 
        font.lab=1,
        font.axis=2,
        legend = rownames(counts),
        args.legend = list(x = 15, 
                           y = 178,
                           cex = 0.6, 
                           horiz = TRUE))
dev.off()
## quartz_off_screen 
##                 2

Куда R сохранил график? В рабочую папку. Узнать, какая папка является рабочей, можно с помощью функции getwd():

getwd()
## [1] "/Users/allat/Desktop"

Не всегда к этой папке удобно подобраться. Конечно, рабочую папку можно изменить или прописать полный путь к файлу (не Bar.png, а, например, C://Users/student/Desktop/Bar.png), но мы поступим проще. Мы просто попросим R открыть папку по тому пути, который нам был показан выше:

utils::browseURL(getwd()) # открывается папка

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

bar <- barplot(counts, 
               beside=TRUE, 
               col=c(), 
               border="white",
               xlab="R хороший?", 
               ylab = "Число ответов", 
               font.lab=1,
               font.axis=2,
               legend = rownames(counts),
               args.legend = list(x = 15, 
                                  y = 178,
                                  cex = 0.6, 
                                  horiz = TRUE))

Посмотрим на объект bar:

bar
##      [,1] [,2] [,3] [,4]
## [1,]  1.5  5.5  9.5 13.5
## [2,]  2.5  6.5 10.5 14.5
## [3,]  3.5  7.5 11.5 15.5

В bar сохранена матрица с координатами середин столбцов по оси X — по ней R отрисовывает график. Чем нам это может помочь? Мы можем забрать отсюда эти координаты столбцов и использовать их для координат подписей по оси X:

# превратим в вектор : 12 элементов
# 1 элемент = высота 1 столбца

xpos <- as.vector(bar)
xpos
##  [1]  1.5  2.5  3.5  5.5  6.5  7.5  9.5 10.5 11.5 13.5 14.5 15.5

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

counts
##            январь 2020 январь 2021 январь 2022 январь 2023
## Да                  81         138          88          97
## Нет                 98          45          77          86
## Нет ответа          21          17          35          17

Собственно, в этой матрице хранятся высоты столбцов, они-то нам и нужны. Преобразуем их в вектор:

ypos <- as.vector(counts)
ypos
##  [1]  81  98  21 138  45  17  88  77  35  97  86  17

Готово! Теперь у нас есть координаты для подписей по оси X и по оси Y. Давайте пока добавим простые подписи — не проценты, а буквы английского алфавита (4 группы по 3 столбца, всего 12 подписей):

labels <- LETTERS[1:12]
labels
##  [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L"
# график как был
barplot(counts, 
        beside=TRUE, 
        col=c(), 
        border="white",
        xlab="R хороший?", 
        ylab = "Число ответов", 
        font.lab = 1,
        font.axis = 2,
        legend = rownames(counts),
        args.legend = list(x = 15, 
                           y = 178,
                           cex = 0.6, 
                           horiz = TRUE))

# добавляем на него текст
# labels: вектор с подписями
# x: вектор с координатами подписей по X
# y: вектор с координатами подписей по Y
# cex: размер шрифта
text(labels, x = xpos, y = ypos, cex = 0.6)

Получилось! Осталось теперь все-таки сделать подписи в виде процентов ответов. Для этого вспомним про функцию apply() — она поможет нам применить функцию для перевода абсолютных частот в проценты:

# function(x){x /sum(x) * 100}: переводим в проценты

# 1: применяем функцию к каждой строке
# 2: применяем функцию к каждому столбцу

# применяем эту функцию к столбцам в counts

count_perc <- apply(counts, 2, function(x){x /sum(x) * 100})
count_perc
##            январь 2020 январь 2021 январь 2022 январь 2023
## Да                40.5        69.0        44.0        48.5
## Нет               49.0        22.5        38.5        43.0
## Нет ответа        10.5         8.5        17.5         8.5

Переведем все в вектор и округлим полученные результаты:

perc <- round(as.vector(round(count_perc)))
perc
##  [1] 40 49 10 69 22  8 44 38 18 48 43  8

Осталось доклеить к процентам символ %, и подписи готовы!

# sep = "", чтобы между числом и %
# не было лишнего пробела
labels <- paste(perc, "%", sep = "")
labels
##  [1] "40%" "49%" "10%" "69%" "22%" "8%"  "44%" "38%" "18%" "48%" "43%" "8%"

Обновим график и выгрузим в другой файл:

png("Bar02.png", width = 9, height = 4, units = 'in', res = 300)
bar <- barplot(counts, 
               beside=TRUE, 
               col=c(), 
               border="white",
               xlab="R хороший?", 
               ylab = "Число ответов", 
               font.lab=1,
               font.axis=2,
               legend = rownames(counts),
               args.legend = list(x = 15, 
                                  y = 178,
                                  cex = 0.6, 
                                  horiz = TRUE))

# сместим подписи вниз на 5 единиц
# чтобы они были прямо на столбцах
text(labels, x = xpos, 
     y = ypos - 5, cex = 0.6, font = 2)
dev.off()
## quartz_off_screen 
##                 2

Все хорошо, но есть проблема: подписи черного цвета, их плохо видно! Добавим в text() вектор с цветами для подписей:

png("Bar03.png", width = 9, height = 4, units = 'in', res = 300)
bar <- barplot(counts, 
               beside=TRUE, 
               col=c(), 
               border="white",
               xlab="R хороший?", 
               ylab = "Число ответов", 
               font.lab=1,
               font.axis=2,
               legend = rownames(counts),
               args.legend = list(x = 15, 
                                  y = 178,
                                  cex = 0.6, 
                                  horiz = TRUE))

# повторяем набор из 3 цветов 4 раза
my.colors <- rep(c("gray90", "gray20", "black"), 4)

text(labels, x = xpos, 
     y = ypos - 5, cex = 0.6, font = 2, 
     col = my.colors)
dev.off()
## quartz_off_screen 
##                 2