Изучив основные объекты в R, мы можем перейти к объектам, ради которых многие и начинают изучать R, а именно, к базам данных или датафреймам (точнее, к таблицам, просто в социально-экономических науках эти термины отождествляют, хотя, строго говоря, база данных - это набор связанных между собой таблиц). Но прежде необходимо научиться загружать файлы с данными, чтобы было с чем работать.
Напоминание. Если мы не хотим прописывать слишком длинный путь к файлу, файл с данными можно сохранить сразу в рабочую папку (папку, из которой запускается R). Тогда при попытке открыть файл с заданным названием R будет искать его в этой папке. Узнать, какая папка является рабочей, можно с помощью функции getwd()
:
getwd() # wd - от working directory
## [1] "/Users/allat/Desktop"
Рабочую папку можно изменить. Например, так:
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)
Если в файле есть текст на кириллице, могут возникнуть проблемы при чтении файла или при его отображении. Решения могут быть разными (зависит от системы, ее параметров и самого файла). Самое простое - специфицировать кодировку самого файла:
df2 <- read.csv("example3.csv", encoding = "UTF-8")
df2 <- read.csv("example4.csv", encoding = "WINDOWS-1251")
Обычно текст в кодировке UTF-8 встречается в файлах, созданных в Mac OS или Linux, а WINDOWS-1251 (или CP-1251) - в Windows.
Будем считать, что с csv-файлами разобрались.
txt-файлы
При работе с txt-файлами необходимо указывать, каким образом столбцы отделены друг от друга (аргумент sep
, разделитель, как и в случае в csv-файлами), а также учитывать, что представляет собой первая строка: наблюдение или шапку таблицы (аргумент header
). Откроем файл, в котором столбы разделены табуляцией и сравним, как он будет выглядеть при выставлении разных значений параметра header
:
# header = T - первая строка читается как имя переменной
table1 <- read.table('example1.txt', sep='\t', header = TRUE)
View(table1)
# header = F - первая строка читается как наблюдение
table2 <- read.table('example1.txt', sep='\t', header = FALSE)
View(table2)
Теперь перейдем к другим форматам.
файлы Excel
Чтобы спокойно загружать xlsx-файлы в R можно установить специальные библиотеки: xlsx
или readxl
. С установкой первой библиотеки могут возникнуть проблемы: R будет писать что-то про rjava. Это обычно бывает, если на компьютере не установлена Java или установлена такая ее версия, которая конфликтует с R (например, недостаточно новая). Тогда Java можно поставить, скачав отсюда. После этого проблема должна исчезнуть, по крайней мере, на Windows.
install.packages("xlsx")
Теперь обратимся к этой библиотеке - иначе открыть файл мы не сможем:
library(xlsx)
Наконец, откроем сам файл. Не забудьте указать номер листа после запятой (даже если он всего один), иначе не сработает.
ex_data <- read.xlsx("example1.xlsx", 1)
Иногда даже с переустановленной Java библиотека xlsx
не хочет загружаться. Тогда можно пойти другим путем: в правом верхнем углу RStudio во вкладке Environment выбрать Import Dataset-From Excel, выбрать файл на компьютере через Browse, вписать название переменной, в которую сохраняем таблицу в R (например, df
) в поле Name и нажать Import. Если в файле Excel более одного листа, тогда в поле Sheet нужно указать номер нужного листа.
Код, который исполняется, когда мы выполняем эти действия вручную, выглядит следующим образом:
library(readxl)
df_ex <- read_excel("example1.xlsx")
То есть, можно установить библиотеку readxl
(с ней не будет таких проблем с Java), обратиться к ней и спокойно использовать по аналогии с кодом выше.
файлы 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")
Загрузим более содержательную таблицу. Таблицу, взятую с сайта IBM, содержащую данные об эффективности рекламных кампаний. Файл и codebook к ней можно найти здесь.
dat <- read.csv("http://math-info.hse.ru/f/2018-19/comm-math/marketing.csv")
Какую информацию о таблице мы можем получить?
Можем определить число наблюдений и число переменных в датафрейме. Узнать это можно точно так же, как и размерность матрицы, ведь число строк - это число наблюдений, а число столбцов - это число переменных.
dim(dat)
## [1] 548 7
Или по отдельности:
nrow(dat) # число строк
## [1] 548
ncol(dat) # число столбцов
## [1] 7
Можем узнать гораздо больше - структуру датафрейма: число наблюдений и переменных, типы переменных и примеры значений, которые они принимают. Сделать это можно с помощью уже знакомой функции str()
:
str(dat)
## 'data.frame': 548 obs. of 7 variables:
## $ MarketID : int 1 1 1 1 1 1 1 1 1 1 ...
## $ MarketSize : Factor w/ 4 levels "","Large","Medium",..: 3 3 3 3 3 3 3 3 3 3 ...
## $ LocationID : int 1 1 1 1 2 2 2 2 3 3 ...
## $ AgeOfStore : int 4 4 4 4 5 5 5 5 12 12 ...
## $ Promotion : int 3 3 3 3 2 2 2 2 1 1 ...
## $ Week : int 1 2 3 4 1 2 3 4 1 2 ...
## $ SalesInThousands: num 33.7 35.7 29 39.2 27.8 ...
Также легко посмотреть на первые несколько значений:
head(dat)
## MarketID MarketSize LocationID AgeOfStore Promotion Week
## 1 1 Medium 1 4 3 1
## 2 1 Medium 1 4 3 2
## 3 1 Medium 1 4 3 3
## 4 1 Medium 1 4 3 4
## 5 1 Medium 2 5 2 1
## 6 1 Medium 2 5 2 2
## SalesInThousands
## 1 33.73
## 2 35.67
## 3 29.03
## 4 39.25
## 5 27.81
## 6 34.67
Или последние:
tail(dat)
## MarketID MarketSize LocationID AgeOfStore Promotion Week
## 543 10 Large 919 2 1 3
## 544 10 Large 919 2 1 4
## 545 10 Large 920 14 2 1
## 546 10 Large 920 14 2 2
## 547 10 Large 920 14 2 3
## 548 10 Large 920 14 2 4
## SalesInThousands
## 543 57.20
## 544 64.34
## 545 50.20
## 546 45.75
## 547 44.29
## 548 49.41
Обсуждение вывода описательных статистик для переменных в базе данных оставим на потом, разберем этот вопрос более подробно, когда начнем разведывательный анализ данных. А пока поговорим о чуть более продвинутых вещах.
Результаты, которые выдают нам функции str()
и dim()
, содержат только общую информацию о количестве наблюдений в таблице и не дают никакой информации о пропущенных значениях. Как это исправить? Для начала можно выяснить, сколько в базе неполностью заполненных строк. Функция complete.cases()
выдает логический вектор, где TRUE
означает полностью заполненную строку, а FALSE
- содержащую пропуски (NAs).
head(complete.cases(dat)) # head - первые несколько значений
## [1] TRUE TRUE TRUE TRUE TRUE TRUE
Посчитаем, сколько полностью заполненных наблюдений:
sum(complete.cases(dat))
## [1] 529
Соответственно, остальные (из 548) - недозаполненные (содержащие NAs).
Посмотрим на незаполненные строки:
View(dat[!complete.cases(dat), ]) # ! отрицание
И посчитаем их количество:
nrow(dat[!complete.cases(dat), ])
## [1] 19
Для дальнейшей работы с пропущенными значениями нам понадобятся дополнительные библиотеки. Установим их. Можно устанавливать сразу несколько библиотек – оформить перечень необходимых библиотек в виде вектора, и тогда сразу после установки одной библиотеки начнется загрузка следующей.
install.packages(c("mice", "VIM"))
Обратимся к ним:
library(mice)
library(VIM)
Выведем графики, которые покажут, в каких переменных пропущенных значений больше всего и как выглядит таблица с пропущенными значениями (паттерны пропущенных значений).
На графике слева показано, с какой частотой встречаются пропущенные значения в той или иной переменной. На графике справа показано, в каких комбинациях эти пропущенные значения встречаются. Например, в нашем случае отсутствие ответов в AgeOfStore часто совпадает с отсутствием ответов в SalesInThousands (пропущенные значения отмечены красным цветом).
# aggr - из библиотеки mice
aggr(dat)
Следующий график отвечает за заполненность наблюдений (красным цветом отмечены пропущенные значения, остальное - заполненные значения, чем темнее цвет, тем больше значение). По вертикальной оси - номер строки в базе данных (id наблюдения).
# matrixplot - из библиотеки VIM
matrixplot(dat)
##
## Click in a column to sort by the corresponding variable.
## To regain use of the VIM GUI and the R console, click outside the plot region.
При работе с базами данных необходимо удалить пропущенные значения (или правильно заполнить - кто умеет), потому что иначе мы не сможем полноценно работать с базой (многие функции не работают при наличии NAs, а у некоторых необходимо указывать дополнительный аргумент - учитывать NA или нет).
# удаляем строки, содержащие NA
dat <- na.omit(dat)
Если мы хотим обратиться к конкретной переменной и рассматривать ее как вектор элементов, нужно использовать символ $
.
dat$Promotion # номер рекламной кампании
## [1] 3 3 3 3 2 2 2 2 1 1 1 2 2 2 2 2 2 3 3 3 3 1 1 1 1 2 2 2 2 1 1 1 1 2 2
## [36] 2 2 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3
## [71] 3 3 3 3 3 3 1 1 1 1 3 3 3 3 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 1 1
## [106] 1 1 1 1 1 1 2 2 2 2 3 3 3 2 2 2 2 1 1 1 1 2 2 2 2 3 3 3 3 3 3 3 1 1 1
## [141] 1 2 2 2 2 1 1 1 1 2 2 2 1 1 1 1 2 2 2 3 3 3 1 1 1 1 1 1 1 2 2 2 1 1 1
## [176] 1 2 2 2 2 2 2 2 1 1 1 1 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3
## [211] 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 3 3 3 3 1 1 1 2 2 2 2 3 3 3 3 3 3 3 3 3
## [246] 3 2 2 2 2 2 2 2 2 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 1 1 1 1 2 2 2
## [281] 2 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 3 3
## [316] 3 3 3 3 3 3 3 3 3 3 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 1
## [351] 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3
## [386] 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 1 1 1 1 3 3 3 3 1 1 1 1 2 2 2 2 3 3 3
## [421] 3 1 1 1 1 3 3 3 3 3 3 3 3 2 2 2 2 3 3 3 3 3 3 3 3 1 1 1 1 2 2 2 2 1 1
## [456] 1 1 3 3 3 3 2 2 2 2 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 2
## [491] 2 2 2 2 2 2 2 1 1 1 1 2 2 2 2 1 1 1 1 2 2 2 2 3 3 3 3 1 1 1 1 1 1 1 1
## [526] 2 2 2 2
Attach и detach
Мы можем «закрепить» базу данных с помощью команды attach
, чтобы обращаться к переменным более простым способом:
attach(dat) # заодно покажет, какие переменные есть
head(Promotion) # имя переменной как есть, без $
## [1] 3 3 3 3 2 2
Однако это не всегда удобно, особенно если приходится работать с несколькими базами одновременно (наложение переменных с одинаковыми именами, проблемы с редактированием и прочее).
detach(dat) # возвращаем обратно
Создание и добавление в базу новых переменных Допустим, мы хотим добавить в базу переменную Campaign, которая будет содержать индекс рекламной кампании, но сохраненный как фактор (factor). Для этого нужно через $
задать имя новой переменной и присвоить ей значение:
dat$Campaign <- factor(dat$Promotion)
Часто при работе с данными возникает необходимость выбрать несколько переменных или определенную группу наблюдений и анализировать их отдельно - чтобы не загружать каждый раз огромную базу с ненужными показателями.
Можем выбрать несколько переменных (столбцов) и сохранить их в другую базу:
dat[2:4] # 2 и 4 - порядковые номера столбцов, от 2 до 4
Получится маленькая база из трех переменных. Сохраним её как новую базу dat1
:
dat1 <- dat[2:4]
Если выбираем столбцы не подряд, обязательно их номера нужно оформить в виде вектора:
dat[c(1, 3)] # не просто dat[1, 3]
В противном случае получится совсем не то:
dat[1, 3]
## [1] 1
Это «совсем не то» связано с тем, что, когда мы указываем в квадратных скобках числа через запятую, R воспринимает первое число как номер строки, второе число - как номер столбца (как в матрицах - сначала строка, потом столбец). Можем посмотреть на исходную базу и убедиться в этом. Но зато таким образом мы можем выбирать строки (наблюдения):
dat[, 1:3] # берем все строки, первые 3 столбца
Или столбцы:
dat[1:4, ] # берем первые 4 строки, все столбцы
Если хотим отобрать из базы определенные наблюдения, это тоже можно сделать с помощью квадратных скобок. Например, мы хотим выбрать данные за первую неделю:
week1 <- dat[dat$Week == 1, ]
Тут важно не забыть поставить запятую, чтобы R понимал, что мы накладываем условие на строки, а столбцы берём все, что есть. Можем сочетать условия. Например, выбрать данные за первую неделю по компаниям среднего уровня:
View(dat[dat$Week == 1 & dat$MarketSize == "Medium", ])
Для тех же целей можно использовать встроенную функцию subset()
:
week1 <- subset(dat, Week == 1)
week1_med <- subset(dat, Week == 1 & MarketSize == "Medium")
Конечно, можем отбирать наблюдения и переменные одновременно:
dat_small <- subset(dat, Week == 1 & MarketSize == "Medium", select = c(MarketID, LocationID, AgeOfStore))
В коде выше мы выбрали столбцы MarketID, LocationID, AgeOfStore.
Чтобы удалить переменные, можно действовать двумя способами:
По смыслу это одно и то же. И то, и другое чаще всего осуществляется с помощью функции subset()
.
Допустим, мы хотим выбрать переменные MarketID и SalesInThousands и сохранить их в новую базу:
# указываем имя базы, оставляем MarketID и SalesInThousands
dat1 <- subset(dat, select = c(MarketID, SalesInThousands))
А теперь хотим оставить все, кроме переменных Week и AgeOfStore:
# перед вектором столбцов стоит минус
dat2 <- subset(dat, select = -c(Week, AgeOfStore))