🏷 Ярлыки и другие атрибуты

Все объекты обладают атрибутами. Обязательный атрибут - это класс объекта.

class(full.ess$impfree)
[1] "numeric"

Атрибуты включаются в структуру объекта.

str(full.ess$impfree)
 num [1:44387] 3 2 2 2 1 4 2 1 3 3 ...
 - attr(*, "value.labels")= Named num [1:6] 6 5 4 3 2 1
  ..- attr(*, "names")= chr [1:6] "Not like me at all" "Not like me" "A little like me" "Somewhat like me" ...

Извлечение атрибутов

Задать новые или узнать существующие значения атрибутов через функцию attr():

attr(full.ess$impfree, "value.labels")
Not like me at all        Not like me   A little like me   Somewhat like me            Like me 
                 6                  5                  4                  3                  2 
 Very much like me 
                 1 

или

attr(full.ess, "variable.labels")[509]
                                      impfree 
"Important to make own decisions and be free" 

Напрямую

есть несколько функций для извлечения наиболее популярных атрибутов class, levels (для факторов), length и т.д.

 class(full.ess$impfree)  # класс объекта
[1] "numeric"
 length(full.ess$impfree) # длина объекта (для переменной - количество наблюдений)
[1] 44387
 levels(full.ess$cntry)   # потенциальные значения категориальной переменной
 [1] "AT" "BE" "CH" "CZ" "DE" "EE" "ES" "FI" "FR" "GB" "HU" "IE" "IL" "IS" "IT" "LT" "NL" "NO" "PL" "PT"
[21] "RU" "SE" "SI"
 colnames(full.ess)[1:10]    # имена частей объекта (для data.frame - имена переменных/столбцов)
 [1] "name"     "essround" "edition"  "proddate" "idno"     "cntry"    "nwspol"   "netusoft" "netustm" 
[10] "ppltrst" 

Создать собственный атрибут

Можно создать собственный атрибут и присвоить ему значение.

attr(full.ess$impfree, "my.super.useful.comment") <- "Это порядковая переменная, не принимайте допущение о нормальности!"
str(full.ess$impfree)
 num [1:44387] 3 2 2 2 1 4 2 1 3 3 ...
 - attr(*, "value.labels")= Named num [1:6] 6 5 4 3 2 1
  ..- attr(*, "names")= chr [1:6] "Not like me at all" "Not like me" "A little like me" "Somewhat like me" ...
 - attr(*, "my.super.useful.comment")= chr "Это порядковая переменная, не принимайте допущение о нормальности!"
  • ❗️Атрибуты могут теряться при создании подмассивов и перекодировании (возможное решение - использовать спец.функции или sjlabelled::copy_labels или (полностью) переходить на tidyverse.
  • ❗️Работать с ярлыками немного сложнее, чем с данными без ярлыков. Если вы предпочтете ярлыки, обратите внимание на пакеты tidyr и dplyr, а также sjmisc и sjlabelled.

Пакет sjmisc

Один из возможных вариантов работы с ярлыками значений.

# Шаг 1 - считайте данные пакетом haven
library("haven")
full.ess <- read_sav("data/ESS8e02.sav")

# Шаг 2 - используйте пакет sjmisc для построения таблиц
library("sjmisc")
frq(full.ess$netusoft)

Internet use, how often (x) <numeric>
# total N=44387  valid N=44338  mean=3.86  sd=1.59

Value |              Label |     N | Raw % | Valid % | Cum. %
-------------------------------------------------------------
    1 |              Never |  8133 | 18.32 |   18.34 |  18.34
    2 |  Only occasionally |  2630 |  5.93 |    5.93 |  24.27
    3 | A few times a week |  2993 |  6.74 |    6.75 |  31.03
    4 |          Most days |  4235 |  9.54 |    9.55 |  40.58
    5 |          Every day | 26347 | 59.36 |   59.42 | 100.00
    7 |            Refusal |     0 |  0.00 |    0.00 | 100.00
    8 |         Don't know |     0 |  0.00 |    0.00 | 100.00
    9 |          No answer |     0 |  0.00 |    0.00 | 100.00
 <NA> |               <NA> |    49 |  0.11 |    <NA> |   <NA>
flat_table(full.ess, "netusoft", "gndr", margin = "col")
                   gndr  Male Female No answer
netusoft                                      
Never                   16.48  20.02       NaN
Only occasionally        5.66   6.18       NaN
A few times a week       6.69   6.81       NaN
Most days                9.96   9.18       NaN
Every day               61.20  57.82       NaN
Refusal                  0.00   0.00       NaN
Don't know               0.00   0.00       NaN
No answer                0.00   0.00       NaN

as_label()

Функция as_label из пакета sjlabelled конвертирует ярлыки в значения и возвращает факторную переменную.

library("sjlabelled")

table(full.ess$domicil, useNA = "always")

    1     2     3     4     5  <NA> 
 9937  4433 13958 13274  2735    50 
table(as_label(full.ess$domicil), useNA = "always")

                      A big city Suburbs or outskirts of big city               Town or small city 
                            9937                             4433                            13958 
                 Country village      Farm or home in countryside                          Refusal 
                           13274                             2735                                0 
                      Don't know                        No answer                             <NA> 
                               0                                0                               50 

🔄 Чтение и сохранение данных

Несколько функций, которые позволяют считывать данные из внешних файлов и “возвращать” плоские таблицы data.frame.

⬅️ Чтение данных

Функция read.table()

Читает таблицы в текстовых файлах с каким-либо разделителем.

В аргументах нужно указывать структуру. Ключевые аргументы:

  • file имя файла данных,
  • header - содержит ли первая строка названия переменных,
  • sep - какой символ используется для разделения колонок,
  • dec - какой символ разделяет доли от целых (точка в англоязычных регионах, НО запятая в России и некоторых других странах).

Читаем текстовый файл данных (здесь - строки в коде) и сохраняем получившийся data.frame под названием animal.data:

animal.data<- read.table(
  text="animal, weight, height
      'Cat',    3.5,    36
      'Dog',    5.1,    53
      'Dog',    4.1,    40",
  header=TRUE, 
  sep=",",
  dec="."
)

animal.data

Число пробелов здесь не играет роли.

Пакет readxl

Функция read_excel() читает файлы xls, xlsx.

library("readxl")
test.data<- read_excel(
                    path="data/test2016.xlsx", 
                    sheet=1,  
                    range="B1:D11",
                    col_names=TRUE)
test.data

Пакет foreign

Функция read.spss() читает файлы SPSS, угадывает типы переменных и преобразует их в числовые или переменные-факторы.

library(foreign)
full.ess1 <- read.spss("data/ESS8e02.sav", to.data.frame = TRUE)
head(full.ess, 10) # Выводит в консоль первые 10 строк
rm(full.ess1)

Пакет haven

Функция read_spss() читает файлы SPSS и преобразует их в тибблы с ярлыками и цифровыми значениями. Существенно быстрее аналогичной функции из пакета foreign. Тибблы - это обогащенные таблицы данных, для работы с ними необходимо использовать адаптированные под них пакеты и функции.

Встроен в RStudio, доступен через правый клик по файлу во вкладке Files

head(full.ess, 10)
# A tibble: 10 × 536
   name    essround edition proddate  idno cntry    nwspol netusoft netustm ppltrst  pplfair   pplhlp polintr
   <chr>      <dbl> <chr>   <chr>    <dbl> <chr+lb> <dbl+> <dbl+lb> <dbl+l> <dbl+l> <dbl+lb> <dbl+lb> <dbl+l>
 1 ESS8e02        8 2.0     30.05.2…     1 AT [Aus…    120 4 [Most…     180   8 [8]  8 [8]    3 [3]   1 [Ver…
 2 ESS8e02        8 2.0     30.05.2…     2 AT [Aus…    120 5 [Ever…     120   6 [6]  6 [6]    5 [5]   1 [Ver…
 3 ESS8e02        8 2.0     30.05.2…     4 AT [Aus…     30 2 [Only…      NA   5 [5]  6 [6]    4 [4]   3 [Har…
 4 ESS8e02        8 2.0     30.05.2…     6 AT [Aus…     30 5 [Ever…     120   6 [6]  5 [5]    6 [6]   2 [Qui…
 5 ESS8e02        8 2.0     30.05.2…    10 AT [Aus…     30 5 [Ever…     180   5 [5]  5 [5]    7 [7]   3 [Har…
 6 ESS8e02        8 2.0     30.05.2…    11 AT [Aus…     60 5 [Ever…     120   3 [3]  5 [5]    4 [4]   2 [Qui…
 7 ESS8e02        8 2.0     30.05.2…    12 AT [Aus…     15 2 [Only…      NA   7 [7]  7 [7]    6 [6]   3 [Har…
 8 ESS8e02        8 2.0     30.05.2…    13 AT [Aus…     45 4 [Most…      30   7 [7]  7 [7]    7 [7]   3 [Har…
 9 ESS8e02        8 2.0     30.05.2…    14 AT [Aus…     10 5 [Ever…     120   9 [9] 10 [Mos… 10 [Peo… 4 [Not…
10 ESS8e02        8 2.0     30.05.2…    15 AT [Aus…     60 4 [Most…     120   5 [5]  3 [3]    4 [4]   2 [Qui…
# … with 523 more variables: psppsgva <dbl+lbl>, actrolga <dbl+lbl>, psppipla <dbl+lbl>,
#   cptppola <dbl+lbl>, trstprl <dbl+lbl>, trstlgl <dbl+lbl>, trstplc <dbl+lbl>, trstplt <dbl+lbl>,
#   trstprt <dbl+lbl>, trstep <dbl+lbl>, trstun <dbl+lbl>, vote <dbl+lbl>, prtvtbat <dbl+lbl>,
#   prtvtcbe <dbl+lbl>, prtvtfch <dbl+lbl>, prtvtdcz <dbl+lbl>, prtvede1 <dbl+lbl>, prtvede2 <dbl+lbl>,
#   prtvtfee <dbl+lbl>, prtvtdes <dbl+lbl>, prtvtdfi <dbl+lbl>, prtvtcfr <dbl+lbl>, prtvtbgb <dbl+lbl>,
#   prtvtehu <dbl+lbl>, prtvtbie <dbl+lbl>, prtvtcil <dbl+lbl>, prtvtbis <dbl+lbl>, prtvtbit <dbl+lbl>,
#   prtvblt1 <dbl+lbl>, prtvblt2 <dbl+lbl>, prtvblt3 <dbl+lbl>, prtvtfnl <dbl+lbl>, prtvtbno <dbl+lbl>, …

↘️ Сохранение данных

Аналогично, в тех же пакетах, что и считываем.

.Rdata

Проще и быстрее всего сохранять объекты в родном формате R.

save(dt, file="myfile.Rdata")

Обратите внимание, что в отличие от работы с внешними данными, при открытии файлов R не нужно сохранять их в какой-либо объект, они автоматически появляются в памяти под тем именем, под которым были сохранены.

load("myfile.Rdata")

.csv, .txt

write.table(data.input, file="data.txt")

Содержаение data.txt:

    "i.am.numeric" "i.am.factor" "i.am.char"
"1"   1             "yes"         "chars"
"2"   2             "no"          "chars"

Добавим немного аргументов:

write.table(
  data.input,       # объект данных для экспорта
  file = "data.txt",# название файла
  quote = FALSE,    # брать ли в кавычки текстовые значения?
  sep="\t",         # разделитель между значениями - знак табуляции
  na="999",         # чем заменить пропущенные значения
  dec=",",          # запятая или точка между для долей?
  col.names = TRUE, # добавить названия переменных?
  row.names = FALSE # добавить названия строк?
  )

Содержаение data.txt:

i.am.numeric    i.am.factor i.am.char
1               yes         chars
2             no            chars
3               idk         chars

write.csv и write.csv2 - та же команда с полезными умолчаниями.

SPSS, SAS, Stata

Как решаются сложности с ярлыками для SPSS? Через тибблы.

foreign::write.foreign()
write.foreign(
  df = data.input,           # объект данных для экспорта
  datafile = "sample.sav",   # название файла данных
  codefile = "codefile.txt", # название файла-синтакса SPSS, который добавит к данным ярлыки
  package = "SPSS"           # формат экспортируемого файла
  )

Можно скачать и посмотреть, что получилось: codefile.txt, sample.sav.

haven::write_sav()
library("haven")

#   нужно прописать все ярлыки значений
dt1 <- data.frame(                # создаем data.frame
  labelled_spss(                  # создаем специальный объект с ярлыками
         data.input$i.am.numeric, # значения переменной
           c(Good = 1, Bad = 8),  # кодировка для сохранения в SPSS
           na_values = c(9, 10),  # какие значения закодировать как как пропущенные?
           na_range =  c(88,99)   # какие значения закодировать как как пропущенные?
  ),
  
   labelled_spss(
           as.character(data.input$i.am.factor),
           c(Happy = "yes", Unhappy = "no"),
           na_values = "idk")
  )

# и ярлыки переменных, приписав атрибут под названием "label"
attr(
  x = dt1[,1],       # переменная
  which = "label"    # название атрибута
  ) <- "Single var"  # присвоение значения атрибуту label

# и только затем сохранить во внешний файл
write_sav(dt1, "sample2.sav")

Можно скачать и посмотреть, что получилось: sample2.sav.

✂️ Фильтрация данных и доступ к частям объектов

Индексирование data.frame

Любой тип данных можно индексировать.

плоская.таблица[строки, колонки]

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

Например по номеру строки или переменной:

full.ess[1, 2]

Или по имени переменной

full.ess[1, "idno"]

Если нужны все значения одной переменной, значения для строк пропускаем, но при этом оставляем запятую, чтобы обозначить, что речь идет о колонках, а не о строках:

full.ess[,  "idno"]

или более коротко с использованием знака $ для обозначения колонки (переменной)

full.ess$idno

На практике используются оба способа.

Создание подмассивов

1

Полезный оператор : для создания последовательностей чисел.

1:5
[1] 1 2 3 4 5

Следующий код создает подмассив данных data.from.spss с пятью первыми строками и двумя переменными idno и cntry:

full.ess[1:5,  c("idno", "cntry")]

2

Для фильтрации массивов по значениям переменных необходимо сперва создать логическую переменную-фильтр. Это осуществляется через операцию сравнения и оператор сравнения == (не путайте с оператором присвоения =). Например:

2 + 2 == 5
[1] FALSE

Сравнение для вектора:

c(1,2,1) == 2
[1] FALSE  TRUE FALSE

3

Создадим логическую переменную-фильтр на основе данных, отберем респондентов из Бельгии. Для этого нужна логическая переменная, принимающая значение TRUE, когда респондент из Бельгии:

Belgium.filter <- full.ess[ ,"cntry"] == "Belgium"

full.ess[Belgium.filter, ]

или короче:

full.ess[full.ess$cntry=="Belgium", ]

4 subset()

Ровно ту же операцию можно сделать используя функцию subset

 subset(
   x = full.ess,              #данные
   subset= cntry == "Belgium" #критерий отбора строк
   ) 

Индексирование списков

Списки (объекты класса list) индексируются с использованием двойных квадратных скобок:

  • one.list[[1]] - для получения первого элемента списка

и аналогично, если элементы списка имеют имена:

  • one.list[["my.named.element"]]

или аналогично

  • one.list$my.named.element

Например, если второй элемент списка - это таблица данных, то можно делать подмассив обращаясь сразу к первым десяти строкам:

  • one.list [[ 2 ]] [ 1:10, ]

Аналогично, если первый элемент списка - это другой список, можно обращаться и делать его подмассив напрямую:

  • one.list[[ 1 ]] [[ 5 ]] - для получения пятого элемента в первом элементе списка

Имена переменных

1

Функция colnames() возвращает вектор имен колонок/переменных (сам по себе он является обыкновенным текстовым вектором):

colnames(full.ess)[1:10]
 [1] "name"     "essround" "edition"  "proddate" "idno"     "cntry"    "nwspol"   "netusoft" "netustm" 
[10] "ppltrst" 

Если они нас не устраивают, можно их поменять на более понятные через оператор присвоения:

colnames(full.ess)[1:3] <- c("ID number", "ESS round", "Версия массива")

head(full.ess) # Выводит в консоль ("печатает") первые строки массива данных

2

❗️ Имена могут быть кириллическими и содержать пробелы, но во избежание проблем и для удобства обращения лучше делать их короткими и латинскими.

  • При обращении к имени с пробелами необходимо использовать символы обратного апострофа `, например full.ess$`ID number` или через индексирование full.ess[, "ID number"]
  • Если пробелов нет, можно обращаться напрямую к переменной full.ess$cntry

👻 Пропущенные значения

Типы

Основной формат пропущенных значений – NA – “Not Available”. Необходимо приводить к этому формату все другие пропущенные значения.

log(-1) # логарифм отрицательного значения
[1] NaN
100/0 # Разделите число на ноль
[1] Inf
  • Некоторые функции возвращают NaN (Not A Number), а некоторые другие функции не знают что с ними делать.
  • В цифровых переменных может встречаться или появляться в результате неверного применения функций NaN= Not a number.
  • Обратите внимание, деление на ноль приводит к значению Inf (бесконечность), которое не является пропущенным значением .

Определение и поиск is.na()

Эта функция возвращает логические значения той же размерности, что и ее аргумент. TRUE означает, что значение пропущено.

var.with.missings <- c(5, NA, NaN, Inf, NULL, "", " ") # создадим вектор с различными видами пропущенных значений

is.na(var.with.missings) # посмотрим, какие из этих значений распознаются как пропущенные
[1] FALSE  TRUE FALSE FALSE FALSE FALSE

Посчитать количество пропущенных значений

Логические переменные автоматически конвертируются в цифровые (0 и 1) в большинстве базовых функций. Можно посчитать, сколько пропущенных значений в переменной impfree, используя is.na() и sum().

miss.rev <- is.na(full.ess$impfree) # сколько пропущенных значений в переменной?
table(miss.rev)
miss.rev
FALSE  TRUE 
43591   796 

Оператор !, инвертирует логические значения на обратные. Применение арифметических операций к логическим переменным автоматически конвертирует их в цифровую переменную со значениями 0 и 1. Поэтому можно быстро узнать число непропущенных значений таким образом:

sum(!is.na(full.ess$impfree))  # сколько НЕпропущенных значений в переменной?
[1] 43591

Удаление и замена

LISTWISE удаление

na.omit() исключает все пропущенные из вектора и производит listwise удаление, если применяется к массиву данных. Очень осторожно с этим! Например:

full.ess.nomissings <- na.omit(full.ess)

nrow(full.ess) # Посмотрим на количество наблюдений (строк/респондентов)
[1] 44387
nrow(full.ess.nomissings) # И сколько из них осталось после удаления
[1] 0

Перекодирование

Как заменить закодированные цифрами пропущенные значения на те, что понимает R. Например, неиспользование интернета мы хотим закодировать как пропущенный ответ. Сначала посмотрим на распределение:

table(full.ess$netusoft, useNA="always")

    1     2     3     4     5  <NA> 
 8133  2630  2993  4235 26347    49 

Затем перекодируем и снова посмотрим:

full.ess$netusoft[full.ess$netusoft == 1] <- NA

table(full.ess$netusoft, useNA="always")

    2     3     4     5  <NA> 
 2630  2993  4235 26347  8182 

Разберем строку full.ess$netusoft[full.ess$netusoft == 1] <- NA.
1. full.ess – это массив данных, в котором мы обращаемся netusoft.
2. Из этой переменной мы отбираем только значения, соответствующие условию full.ess$netusoft == 1, то есть по той же самой переменной тех респондентов, у которых значение равно 1.
3. Всем этим респондентам в переменной netusoft будет приписано значение NA

Пропущенные значения в факторах и текстовых переменных

В текстовых: пустые значения не считаются пропущенными!

b <- c("One", "Two", "", NA)
is.na(b)
[1] FALSE FALSE FALSE  TRUE

Их можно заменить на NA:

b[b==""]<-NA # отфильтруем значения b равные пустой текстовой строке и присвоим им значение NA
is.na(b) # посмотрим, что из этого вышло
[1] FALSE FALSE  TRUE  TRUE

В факторах: иногда потенциальных категорий может быть больше, чем эмпирически наблюдаемых значений, например:

voted <- factor(c("Нет", "Нет", "Нет", "Нет"), levels=c("Да", "Нет", "Не скажу"))
voted
[1] Нет Нет Нет Нет
Levels: Да Нет Не скажу

Можно автомтически удалить отсутствующие в переменной уровни функцией droplevels()

droplevels(voted)
[1] Нет Нет Нет Нет
Levels: Нет

Разные аргументы в разных функциях

  • Например, в table() используется аргумент useNA, который может принимать значения always (всегда добавлять колонку с NA), ifany (добавлять, если пропущенные есть) и no (не добавлять).
  • а в aggregate() - аргумент na.action, который принимает такие значения как na.omit (удалять) и na.pass (передавать в агрегирующую функцию).
  • чаще чем другие аргументы используется na.rm, принимающий значения TRUE (удаляет строки с пропущенными значениями) или FALSE (оставляет эти строки).

Другие способы работы с пропущенными значениями

  • Замена пропущенных средним значением – не рекомендуется никогда, так как может приводить к искажению результатов, в частности, в факторном анализе.
  • Попарное (pairwise) удаление – редко рекомендуется, может искажать результаты, в частности в структурных моделях.
  • Множественная импутация – подстановка значений, предсказанных другими переменными в массиве. Время- и трудозатратна, но, при соблюдении ряда условий, предпочтительна в большинстве случаев.
  • FIML (full information maximum likelihood) метод, применяемых в многомерном анализе, таком как факторный, который позволяет использовать всю имеющуюся информацию без удаления респондентов и без предсказания пропущенных значений.

Подробнее

Кабаков Р. (2015) - Глава 15.

🛁 Tidyverse

Курс “Introduction to the Tidyverse” на datacamp.com - https://www.datacamp.com/courses/introduction-to-the-tidyverse

Tidyverse - это набор пакетов для очистки и преобразования данных. Большая часть функций интуитивно понятна.

%>% pipes

“Трубы” - передают объект, стоящий слева в качестве первого аргумента функции справа.

library(magrittr) # пакет, обеспечивающий работу команды %>%

c(2,5, NA) %>% sum(., na.rm=TRUE) # там, где должен стоять аргумент, передаваемый через "трубу", ставят точку
[1] 7
# если аргумент первый, то его можно просто не упоминать
c(2,5) %>% sum() 
[1] 7
# если не первый, то нужно использовать вместо него точку
c(2,5) %>% sum(10, .) 
[1] 17
# если аргумент первый и он всего один, можно даже не ставить скобки
c(2,5) %>% sum
[1] 7

Трубы могут быть сколь угодно длинными (но не рекомендуется более 10 строк, т.к. поиск ошибок становится трудным)

c(2,5) %>% sum %>% sqrt 
[1] 2.645751

Результат работы трубы сохраняется как обычно - в объект в начале этой трубы.

pipe.result <- c(2,5) %>% sum %>% log 

pipe.result
[1] 1.94591

Полный пример “трубы”

library(dplyr)
library(magrittr)
library(sjmisc)


Austria <- # результат будет сохранен в объект Austria
  full.ess %>%   # берем объект full.ess и отправляем его первым аргументом в следующую функцию
  dplyr::select("impfree", "cntry", "yrbrn", "gndr") %>%  # отбираем переменные
  filter(cntry=="AT") %>%                         # фильтруем респондентов
      mutate(age = 2014 - yrbrn,                  # перекодируем
             female = as.numeric(gndr==2 & !is.na(gndr)),   
             impfree.reversed =  
                rec(impfree, rec=c(" 1=6 [very much]; 
                                     2=5 [like me]; 
                                     3=4 [somewhat]; 
                                     4=3 [a little]; 
                                     5=2 [not like me]; 
                                     6=1 [not at all]"), 
               append=TRUE, # добавить к существующущему массиву данных?
               as.num = TRUE, # интервальная?
               var.label="Ценность свободы"))


Austria
# A tibble: 2,010 × 7
                 impfree cntry            yrbrn       gndr   age female impfree.reversed
               <dbl+lbl> <chr+lbl>    <dbl+lbl>  <dbl+lbl> <dbl>  <dbl>            <dbl>
 1 3 [Somewhat like me]  AT [Austria]      1982 2 [Female]    32      1                4
 2 2 [Like me]           AT [Austria]      1964 1 [Male]      50      0                5
 3 2 [Like me]           AT [Austria]      1948 2 [Female]    66      1                5
 4 2 [Like me]           AT [Austria]      1962 1 [Male]      52      0                5
 5 1 [Very much like me] AT [Austria]      1996 2 [Female]    18      1                6
 6 4 [A little like me]  AT [Austria]      1951 2 [Female]    63      1                3
 7 2 [Like me]           AT [Austria]      1964 2 [Female]    50      1                5
 8 1 [Very much like me] AT [Austria]      1972 2 [Female]    42      1                6
 9 3 [Somewhat like me]  AT [Austria]      1994 2 [Female]    20      1                4
10 3 [Somewhat like me]  AT [Austria]      1975 2 [Female]    39      1                4
# … with 2,000 more rows

Таблицы и частоты

library(sjmisc)

# частоты
full.ess$impfree %>% 
  table %>% 
  prop.table
.
          1           2           3           4           5           6 
0.302608337 0.383267188 0.191828588 0.080980019 0.034020784 0.007295084 
  # или

frq(full.ess$impfree)

Important to make own decisions and be free (x) <numeric>
# total N=44387  valid N=43591  mean=2.18  sd=1.10

Value |              Label |     N | Raw % | Valid % | Cum. %
-------------------------------------------------------------
    1 |  Very much like me | 13191 | 29.72 |   30.26 |  30.26
    2 |            Like me | 16707 | 37.64 |   38.33 |  68.59
    3 |   Somewhat like me |  8362 | 18.84 |   19.18 |  87.77
    4 |   A little like me |  3530 |  7.95 |    8.10 |  95.87
    5 |        Not like me |  1483 |  3.34 |    3.40 |  99.27
    6 | Not like me at all |   318 |  0.72 |    0.73 | 100.00
    7 |            Refusal |     0 |  0.00 |    0.00 | 100.00
    8 |         Don't know |     0 |  0.00 |    0.00 | 100.00
    9 |          No answer |     0 |  0.00 |    0.00 | 100.00
 <NA> |               <NA> |   796 |  1.79 |    <NA> |   <NA>
# кросс-табуляции
full.ess %>% 
  dplyr::select(impfree, gndr) %>% 
  table %>% 
  prop.table
       gndr
impfree           1           2
      1 0.140336790 0.162292374
      2 0.189845829 0.193378912
      3 0.089542993 0.102298798
      4 0.036064972 0.044920620
      5 0.015440029 0.018583096
      6 0.003166009 0.004129577
  # или

flat_table(full.ess, impfree, gndr,  margin="col")
                   gndr  Male Female No answer
impfree                                       
Very much like me       29.58  30.88       NaN
Like me                 40.02  36.79       NaN
Somewhat like me        18.88  19.46       NaN
A little like me         7.60   8.55       NaN
Not like me              3.25   3.54       NaN
Not like me at all       0.67   0.79       NaN
Refusal                  0.00   0.00       NaN
Don't know               0.00   0.00       NaN
No answer                0.00   0.00       NaN

По группам

library(dplyr)
# средние по группам
full.ess %>%
  group_by(gndr) %>%
  summarise(
    Mean=mean(impfree, na.rm=T), # имя колонки = функция
    Sugma=sd(impfree, na.rm=T),
    N=n(),
    SE=sd(impfree, na.rm=T)/sqrt(n())
    )

🐵 Среда и хороший тон

Когда вы пишете код, его адресатами являются несколько агентов:

  • компьютер, т.к. код должен выполняться и что-то обсчитывать;
  • другие люди, которые будут читать этот код;
  • вы в будущем, которые тоже будете читать этот код.

Поэтому код должен быть понятен, хорошо отформатирован и подробно откомментирован.

Названия объектов

Объекты (и переменные) нужно называть понятными именами.

🚫 V123, a, model3, table 👍 model.interactions, simple.regression, regression.coef

Имена объектов не должны совпадать с названиями функций в подгруженных пакетах (например, ни с одной из функций в базовом R, таких как table, data, sum и т.д.)

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

  • не использовать знак _, а использовать точки вместо пробелов, model.with.interactions;

или

  • не использовать знак _, а использовать заглавные буквы вместо пробелов modelWithInteractions.

Избегайте общих слов, указывайте в названии, что специфического содержит объект

🚫 data, survey, model, table 👍 ESS.5, ESS.5.Russia, regression.tables.total

Комментарии

  • каждый этап отделяйте секциями (они отражаются в панелях RStudio, если добавить четыре #### или четыре —- в конце.
  • сложные выражения поясняйте человеческим языком.

Форматирование

Код должен легко читаться

  • смысловые блоки можно отделять несколькими пустыми строками;
  • не пишите все в одну строку, переносы строки - наши друзья!
  • скобки закрывать на новой строке и с отступом, соответствующим открытой скобке;
ESS7.trim <- read.spss(
  file = "ESS_trimmed.sav",
  use.value.labels = FALSE,
  use.missings = TRUE,
  to.data.frame = TRUE
  )
  • можно автоматизировать форматирование с помощью пакета formatR.

  • будьте последовательны, используя одну из (любую) конвенций, но последовательно. Например, для приписывания значений либо <- либо = по всему коду.

Структура кода

  1. Пакеты, которые нужно запустить (не включайте команды установки или специально помечайте их)
  2. Блок команд для считывания данных.
  3. Чистка, модификация и фильтрация данных.
  4. Модель, например, регрессионная модель в функции lm()
  5. Коммуникация результатов
  • графики
  • таблицы с коэффициентами
  • и т.д.
  1. Сохранение результатов в одном из выбранных форматов (например .pdf или .xlsx).

Чистота памяти

Удаляйте все ненужные объекты, особенно большие!

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

rm(full.ess)

Новые слова

sjmisc readxl foreign haven

read.spss(), read_sav(), read.table(),

subset(), as_label(), NA, NaN, NULL(), is.na(), na.omit()

Дома

  1. Установите R и Rstudio на свой компьютер:

R: https://cloud.r-project.org/

RStudio: https://www.rstudio.com/products/rstudio/download/#download

#




Максим Руднев, 2018-2021 на основе RMarkdown.