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

Работа с файлами

Загрузка данных в R

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

getwd() # wd - от working directory
## [1] "/home/oem/Рабочий стол/rprogramming-4/R-programming-4/lectures/lect3-27-09"

Рабочую папку можно изменить. Например, так:

setwd("C:/AllaT/Рабочий стол/")

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

csv-файлы

Формат csv (comma separated values) - широко распространенный текстовый формат, который используется для представления табличных данных. В качестве разделителя, т.е. символа, который разделяет значения колонок, обычно используется запятая, как и следует из названия.

df <- read.csv("example1.csv")
View(df) # посмотреть на базу данных, V - заглавная

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

df1 <- read.csv("example2.csv")
View(df1)

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

df1 <- read.csv("example2.csv", sep = ";") # sep - от separator
View(df1)

Если в файле есть текст на кириллице, могут возникнуть проблемы при чтении файла или при его отображении. Решения могут быть разными (зависит от системы, ее параметров и самого файла). Вот некоторые из них.

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

Sys.getlocale()
## [1] "LC_CTYPE=ru_RU.UTF-8;LC_NUMERIC=C;LC_TIME=ru_RU.UTF-8;LC_COLLATE=ru_RU.UTF-8;LC_MONETARY=ru_RU.UTF-8;LC_MESSAGES=ru_RU.UTF-8;LC_PAPER=ru_RU.UTF-8;LC_NAME=C;LC_ADDRESS=C;LC_TELEPHONE=C;LC_MEASUREMENT=ru_RU.UTF-8;LC_IDENTIFICATION=C"

Можно добавить русский язык:

#Sys.setlocale("Russian")

А можно просто спеифицировать кодировку самого файла:

#df2 <- read.csv("example3.csv", encoding = "UTF-8")
#df2 <- read.csv("example4.csv", encoding = "WINDOWS-1251")

Будем считать, что с csv-файлами разобрались.

txt-файлы

При работе с txt-файлами необходимо указывать, каким образом столбцы отделены друг от друга (аргумент sep, разделитель, как и в случае в csv-файлами), а также учитывать, что представляет собой первая строка: наблюдение или шапку таблицы (аргумент header). Откроем файл, в котором столбы разделены табуляцией и сравним, как он будет выглядеть при выставлении разных значений параметра header:

table1 <- read.table('example1.txt', sep='\t', header = TRUE)  
View(table1) # header = T - первая строка читается как имя переменной

table2 <- read.table('example1.txt', sep='\t', header = FALSE)
View(table2) # header = F - первая строка читается как наблюдение

Теперь перейдем к другим форматам.

файлы Excel

Чтобы спокойно загружать xls-файлы и xlsx-файлы необходимо установить соответствующие библиотеки xls (xlsx).

Установим библиотеку xlsx. С ее установкой могут возникнуть проблемы: R будет писать что-то про rjava. Это обычно бывает, если на компьютере не установлена Java или установлена такая ее версия, которая конфликтует с R (например, недостаточно новая). Тогда Java можно поставить, скачав отсюда. После этого проблема должна исчезнуть.

install.packages("xlsx")

Теперь обратимся к этой библиотеке - иначе открыть файл мы не сможем:

library(xlsx)

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

ex_data <- read.xlsx("example1.xlsx", 1)

файлы STATA

Для загрузки файлов STATA (файлы с расширением .dta) потребуется библиотека foreign.

install.packages("foreign")
library(foreign)

Теперь загрузим dta-файл.

stata_data <- read.dta("example1.dta")

файлы SPSS

Для загрузки файлов SPSS (файлы с расширением .sav) потребуется библиотека Hmisc.

install.packages("Hmisc")
library(Hmisc)

Загрузим sav-файл.

#sav_data <- spss.get("example1.sav")

Сохранение файлов

Выгружаются данные из R аналогичным образом, но только вместо read в названиях функций используется write. Например, сохраним базу df в csv-формате:

write.csv(df, "new_file.csv")

Работа с базами данных

Описание базы данных

Загрузим более содержательную базу данных. Базу данных, которая использовалась в исследовании Druckman, Levendusky, McLain No Need to Watch: How the Effects of Partisan Media Can Spread via Inter-Personal Discussions (2017). Файл и codebook к базе данных можно найти здесь.

dat <- read.csv("druckman_levendusky_mclain.csv")

Какую информацию о базе данных мы можем получить?

Можем определить число наблюдений и число переменных в базе. Узнать это можно точно так же, как и размерность матрицы, ведь число строк - это число наблюдений, а число столбцов - это число переменных.

dim(dat)
## [1] 575  31

Можем узнать гораздо больше - структуру базы данных: число наблюдений и переменных, типы переменных и примеры значений, которые они принимают. Сделать это можно с помощью уже знакомой функции str():

str(dat)
## 'data.frame':    575 obs. of  31 variables:
##  $ X              : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Group_Number   : int  101 100 102 101 102 103 101 102 102 102 ...
##  $ Type_of_Session: Factor w/ 5 levels "Delbieration + Choice",..: 5 5 5 5 5 5 5 5 5 5 ...
##  $ Condition      : Factor w/ 16 levels "1","10A","10B",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ Numb_Delib     : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ IndepDelib     : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ Pid            : int  1 1 1 1 1 2 2 2 2 2 ...
##  $ PolInterest    : num  3 3 2 3 1 1 3 2 2 2 ...
##  $ Educ           : int  3 2 4 3 4 2 3 3 4 4 ...
##  $ Income         : int  2 2 4 3 4 2 2 4 4 4 ...
##  $ Race           : int  0 0 0 0 0 5 0 0 0 0 ...
##  $ Age            : int  1 0 1 1 1 0 1 1 1 1 ...
##  $ Female         : int  1 1 1 1 0 1 1 1 0 0 ...
##  $ Veto           : int  1 0 1 1 1 0 1 1 1 1 ...
##  $ OilExport      : int  1 0 0 1 1 1 1 1 1 1 ...
##  $ Renewable      : int  1 0 0 1 1 1 1 1 1 0 ...
##  $ HouseMajority  : int  1 0 0 1 1 1 1 1 1 1 ...
##  $ LawCons        : int  1 0 1 1 1 1 1 1 1 1 ...
##  $ SecState       : int  0 0 1 0 1 0 1 1 1 1 ...
##  $ GulfBan        : int  0 1 0 1 0 0 1 1 1 0 ...
##  $ ConsParty      : int  1 0 1 1 1 1 1 1 1 1 ...
##  $ MEOil          : int  1 0 1 1 0 0 1 1 0 0 ...
##  $ VideoWatch1    : Factor w/ 7 levels "","A","B","C",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ VideoWatch2    : Factor w/ 7 levels "","A","B","C",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ VideoWatch3    : Factor w/ 7 levels "","A","B","C",..: 1 1 1 1 1 1 1 1 1 1 ...
##  $ SupportCoastal : int  5 5 5 6 3 5 6 5 4 6 ...
##  $ SupportFederal : int  4 2 4 4 3 5 6 4 4 7 ...
##  $ SupportKeystone: int  5 4 4 4 5 6 6 4 4 6 ...
##  $ KnowOther      : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ TrustOther     : int  NA NA NA NA NA NA NA NA NA NA ...
##  $ ProtectEnviro  : int  NA NA 2 4 1 4 1 3 4 3 ...

Также легко посмотреть на первые несколько значений:

head(dat)
##   X Group_Number Type_of_Session Condition Numb_Delib IndepDelib Pid
## 1 1          101 No Deliberation         1         NA         NA   1
## 2 2          100 No Deliberation         1         NA         NA   1
## 3 3          102 No Deliberation         1         NA         NA   1
## 4 4          101 No Deliberation         1         NA         NA   1
## 5 5          102 No Deliberation         1         NA         NA   1
## 6 6          103 No Deliberation         1         NA         NA   2
##   PolInterest Educ Income Race Age Female Veto OilExport Renewable
## 1           3    3      2    0   1      1    1         1         1
## 2           3    2      2    0   0      1    0         0         0
## 3           2    4      4    0   1      1    1         0         0
## 4           3    3      3    0   1      1    1         1         1
## 5           1    4      4    0   1      0    1         1         1
## 6           1    2      2    5   0      1    0         1         1
##   HouseMajority LawCons SecState GulfBan ConsParty MEOil VideoWatch1
## 1             1       1        0       0         1     1            
## 2             0       0        0       1         0     0            
## 3             0       1        1       0         1     1            
## 4             1       1        0       1         1     1            
## 5             1       1        1       0         1     0            
## 6             1       1        0       0         1     0            
##   VideoWatch2 VideoWatch3 SupportCoastal SupportFederal SupportKeystone
## 1                                      5              4               5
## 2                                      5              2               4
## 3                                      5              4               4
## 4                                      6              4               4
## 5                                      3              3               5
## 6                                      5              5               6
##   KnowOther TrustOther ProtectEnviro
## 1        NA         NA            NA
## 2        NA         NA            NA
## 3        NA         NA             2
## 4        NA         NA             4
## 5        NA         NA             1
## 6        NA         NA             4

Или последние:

tail(dat)
##       X Group_Number                Type_of_Session Condition Numb_Delib
## 570 570           99 Deliberation + Forced Exposure        9B          6
## 571 571           14 Deliberation + Forced Exposure        9B          4
## 572 572           99 Deliberation + Forced Exposure        9B          6
## 573 573           90 Forced Exposure + Deliberation        9B          4
## 574 574           33 Deliberation + Forced Exposure        9B          4
## 575 575           34 Deliberation + Forced Exposure        9B          4
##     IndepDelib Pid PolInterest Educ Income Race Age Female Veto OilExport
## 570          1   6           2    2      2    0   1      1    1         1
## 571          1   6           2    4      3    0   1      0    1         1
## 572          1   7           2    1      2    0   1      1    0         0
## 573          0   7           4    2      3    0   0      0    1         1
## 574          0   7           2    4      3    0   1      1    1         1
## 575          0   7           4    4      4    0   1      0    1         1
##     Renewable HouseMajority LawCons SecState GulfBan ConsParty MEOil
## 570         0             1       1        1       0         1     0
## 571         1             1       1        1       1         0     1
## 572         0             0       1        0       0         1     0
## 573         1             1       1        1       1         1     1
## 574         0             0       1        0       0         1     0
## 575         1             1       1        1       0         1     1
##     VideoWatch1 VideoWatch2 VideoWatch3 SupportCoastal SupportFederal
## 570                                                  6              6
## 571                                                  4              3
## 572                                                  6              7
## 573                                                  6              6
## 574                                                  4              6
## 575                                                  6              7
##     SupportKeystone KnowOther TrustOther ProtectEnviro
## 570               3         4          1             1
## 571               4         3          4             6
## 572               7         2          5             2
## 573               6         4          4             4
## 574               7         4          4             4
## 575               6         2          2             4

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

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

Результаты, которые выдают нам функции str() и dim(), содержат только общую информацию о количестве наблюдений в базе данных и не дают никакой информации о пропущенных значениях. Как это исправить? Для начала можно выяснить, сколько в базе неполностью заполненных строк. Функция complete.cases() выдает логический вектор, где TRUE означает полностью заполненную строку, а FALSE - содержащую пропуски (NAs).

head(complete.cases(dat)) # head - первые несколько значений
## [1] FALSE FALSE FALSE FALSE FALSE FALSE

Посчитаем, сколько полностью заполненных наблюдений:

sum(complete.cases(dat))
## [1] 381

Соответственно, остальные (из 575) - недозаполненные (содержащие NAs).

Посмотрим на незаполненные строки:

View(dat[!complete.cases(dat), ]) # ! отрицание

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

install.packages(c("mice", "VIM"))

Обратимся к ним:

library(mice)
library(VIM)

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

На графике слева показано, с какой частотой встречаются пропущенные значения в той или иной переменной. На графике справа показано, в каких комбинациях эти пропущенные значения встречаются. Например, в нашем случае отсутствие ответов в TrustOther часто совпадает с отсутствием ответов в KnowOther (пропущенные значения отмечены красным цветом).

aggr(dat)

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

matrixplot(dat) 

Удаление пропущенных значений

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

dat <- na.omit(dat)

Выбор переменных

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

dat$Educ # образование
##   [1] 4 3 3 3 3 2 3 2 3 4 2 3 4 3 3 2 2 2 3 3 3 3 2 1 4 4 4 3 3 1 4 4 3 4 4
##  [36] 4 2 4 2 4 2 4 3 4 2 4 2 4 3 4 2 2 1 3 3 4 2 3 3 3 3 3 3 3 3 4 4 4 4 3
##  [71] 3 3 3 3 3 4 2 3 2 3 2 2 1 3 3 4 4 4 3 4 4 3 4 2 2 1 2 2 4 2 4 4 3 3 4
## [106] 2 3 3 4 3 3 4 2 4 3 3 4 3 1 3 2 3 2 1 4 4 4 3 2 2 2 2 3 4 4 2 4 4 2 1
## [141] 4 4 1 2 2 2 2 1 2 2 3 4 3 4 3 4 3 2 3 4 3 2 2 3 2 4 2 4 1 3 2 3 3 2 4
## [176] 3 2 3 2 3 1 3 3 3 2 4 2 3 2 2 1 2 2 4 4 3 4 3 3 4 4 4 2 2 3 2 4 4 4 3
## [211] 4 3 3 2 3 2 1 1 2 3 2 2 4 4 2 4 2 2 2 3 3 2 1 2 3 3 1 3 3 2 4 2 4 3 3
## [246] 4 3 1 3 2 4 4 3 3 3 4 3 1 3 2 2 3 3 2 1 4 2 4 4 3 3 2 2 2 2 2 2 2 3 3
## [281] 4 2 2 1 4 3 4 3 4 1 3 4 1 3 3 3 4 3 2 2 2 3 2 1 4 4 2 1 2 3 4 1 4 2 3
## [316] 3 4 3 4 2 2 4 3 2 4 1 1 2 1 4 2 3 4 2 2 3 3 2 2 1 3 3 3 2 2 3 2 4 4 3
## [351] 2 3 4 2 3 3 4 4 3 4 4 2 2 2 3 4 3 2 2 1 3 4 2 3 3 2 4 1 2 4 4

Attach и detach

Мы можем “закрепить” базу данных с помощью команды attach, чтобы обращаться к переменным более простым способом:

attach(dat) # заодно покажет, какие переменные есть
head(Educ) # имя переменной как есть, без $
## [1] 4 3 3 3 3 2
Educ
##   [1] 4 3 3 3 3 2 3 2 3 4 2 3 4 3 3 2 2 2 3 3 3 3 2 1 4 4 4 3 3 1 4 4 3 4 4
##  [36] 4 2 4 2 4 2 4 3 4 2 4 2 4 3 4 2 2 1 3 3 4 2 3 3 3 3 3 3 3 3 4 4 4 4 3
##  [71] 3 3 3 3 3 4 2 3 2 3 2 2 1 3 3 4 4 4 3 4 4 3 4 2 2 1 2 2 4 2 4 4 3 3 4
## [106] 2 3 3 4 3 3 4 2 4 3 3 4 3 1 3 2 3 2 1 4 4 4 3 2 2 2 2 3 4 4 2 4 4 2 1
## [141] 4 4 1 2 2 2 2 1 2 2 3 4 3 4 3 4 3 2 3 4 3 2 2 3 2 4 2 4 1 3 2 3 3 2 4
## [176] 3 2 3 2 3 1 3 3 3 2 4 2 3 2 2 1 2 2 4 4 3 4 3 3 4 4 4 2 2 3 2 4 4 4 3
## [211] 4 3 3 2 3 2 1 1 2 3 2 2 4 4 2 4 2 2 2 3 3 2 1 2 3 3 1 3 3 2 4 2 4 3 3
## [246] 4 3 1 3 2 4 4 3 3 3 4 3 1 3 2 2 3 3 2 1 4 2 4 4 3 3 2 2 2 2 2 2 2 3 3
## [281] 4 2 2 1 4 3 4 3 4 1 3 4 1 3 3 3 4 3 2 2 2 3 2 1 4 4 2 1 2 3 4 1 4 2 3
## [316] 3 4 3 4 2 2 4 3 2 4 1 1 2 1 4 2 3 4 2 2 3 3 2 2 1 3 3 3 2 2 3 2 4 4 3
## [351] 2 3 4 2 3 3 4 4 3 4 4 2 2 2 3 4 3 2 2 1 3 4 2 3 3 2 4 1 2 4 4

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

detach(dat) # возвращаем обратно

Создание и добавление в базу новых переменных

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

dat$Session <- factor(dat$Type_of_Session)
View(dat)

Фильтрация наблюдений

Часто при работе с данными возникает необходимость выбрать несколько переменных или определенную группу наблюдений и анализировать их отдельно - чтобы не загружать каждый раз огромную базу с ненужными показателями.

Можем выбрать несколько переменных (столбцов) и сохранить их в другую базу:

dat[2:4] # 2 и 4 - порядковые номера столбцов, от 2 до 4

Получится маленькая база из трех переменных. И сохраним как новую базу dat1:

dat1 <- dat[2:4] 
View(dat1)

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

dat[c(1, 3)] # не просто dat[1, 3]

В противном случае получится совсем не то:

dat[1, 3] 
## [1] Delbieration + Choice
## 5 Levels: Delbieration + Choice ... No Deliberation

Это “совсем не то” связано с тем, что, когда мы указываем в квадратных скобках числа через запятую, R воспринимает первое число как номер строки, второе число - как номер столбца (как в матрицах - сначала строка, потом столбец). Можем посмотреть на исходную базу и убедиться в этом:

View(dat)

Но таким образом мы можем выбирать строки (наблюдения):

dat[, 1:3] # берем все наблюдения (строки), первые 3 переменные (столбцы)

Или столбцы:

dat[1:4, ] # берем первые 4 наблюдения (строки), все переменные (столбцы)

Фильтрация по условиям

Если хотим отобрать из базы определенные наблюдения, это тоже можно сделать с помощью subset() (“фильтры”). Например, хотим выбрать респондентов с определенным уровнем образования:

sub_educ <- subset(dat, Educ >= 3)
View(sub_educ)

Для указания нескольких условий опять потребуются логические операторы:

y <- subset(dat, Educ >= 3 & Female == 0) # несколько условий
View(y)

Конечно, можем отбирать наблюдения и переменные одновременно:

z <- subset(dat, Educ >= 3 & Female == 0, select = c(Group_Number, Educ, Female)) 
View(z)

Удаление переменных

Чтобы удалить переменные, можно действовать двумя способами:

  1. удалить их из базы

  2. оставить все остальные переменные в базе

По смыслу это одно и то же. И то, и другое чаще всего осуществляется с помощью функции subset().

Допустим, мы хотим выбрать переменные Educ и Female и сохранить их в новую базу:

dat <- subset(dat, select = c(Educ, Female)) # указываем имя базы, оставляем Educ и Female
View(dat)

А теперь хотим оставить все, кроме переменной Educ:

dat1 <- subset(dat, select = -Educ) # перед ней стоит минус
View(dat1)