Нужные данные часто содержатся на сайтах в неудобной форме. Это только студенты по наивности думают, что .csv
или .dat
файлы как булки на деревьях растут.
Загружаем две основных библиотеки для парсинга:
library(XML) # parsing XML files
library(RCurl) # filling forms
## Loading required package: bitops
# on Ubuntu you need to install `libcurl4-gnutls-dev` before installing
# RCurl
В википедии есть данные по тому, сколько медалей получила каждая страна в Олимпийских играх. Для начала нужно поглядеть на данные за 2010 год.
Мы их попытаемся вытащить автоматом. Потом, например, можно узнать как связано количество серебряных и золотых медалей.
Находим все таблицы и смотрим, что там нашлось:
page <- "http://en.wikipedia.org/wiki/2010_Winter_Olympics_medal_table"
tables <- readHTMLTable(page)
str(tables)
## List of 7
## $ toc : NULL
## $ NULL:'data.frame': 2 obs. of 1 variable:
## ..$ 2010 Winter Olympics: Factor w/ 2 levels "Bid process\nVenues\nMascots\nConcerns and controversies\nTorch relay (route)\nOpening ceremony (flag bearers)\nMedal table (me"| __truncated__,..: 1 2
## $ NULL:'data.frame': 27 obs. of 6 variables:
## ..$ Rank : Factor w/ 27 levels "1","10","11",..: 1 11 17 18 19 20 21 27 22 2 ...
## ..$ Nation: Factor w/ 26 levels "0","1","5","86",..: 8 15 26 21 24 25 9 3 6 20 ...
## ..$ Gold : Factor w/ 11 levels "0","1","10","14",..: 4 3 11 11 9 9 8 5 7 7 ...
## ..$ Silver: Factor w/ 12 levels "0","1","13","15",..: 10 3 4 11 9 1 5 7 9 2 ...
## ..$ Bronze: Factor w/ 11 levels "0","1","11","13",..: 9 11 4 10 5 7 8 3 10 7 ...
## ..$ Total : Factor w/ 15 levels "1","11","14",..: 8 10 11 7 3 15 2 NA 5 14 ...
## $ NULL: NULL
## $ NULL: NULL
## $ NULL:'data.frame': 7 obs. of 2 variables:
## ..$ V1: Factor w/ 5 levels "","Sports\nMedal tables\nNOCs\nMedalists\nSymbols",..: 4 1 2 1 3 1 5
## ..$ V2: Factor w/ 2 levels "1896\n1900\n1904\n1908\n1912\n1920\n1924\n1928\n1932\n1936\n1948\n1952\n1956\n1960\n1964\n1968\n1972\n1976\n1980\n1984\n1988\n1"| __truncated__,..: NA NA NA NA 1 NA 2
## $ NULL:'data.frame': 6 obs. of 2 variables:
## ..$ V1: Factor w/ 4 levels "","Sports\nMedal tables\nNOCs\nMedalists\nSymbols",..: 1 2 1 3 1 4
## ..$ V2: Factor w/ 2 levels "1896\n1900\n1904\n1908\n1912\n1920\n1924\n1928\n1932\n1936\n1948\n1952\n1956\n1960\n1964\n1968\n1972\n1976\n1980\n1984\n1988\n1"| __truncated__,..: NA NA NA 1 NA 2
Судя по описанию, понимаем, что наша таблица — третья из полученных:
df <- tables[[3]]
str(df)
## 'data.frame': 27 obs. of 6 variables:
## $ Rank : Factor w/ 27 levels "1","10","11",..: 1 11 17 18 19 20 21 27 22 2 ...
## $ Nation: Factor w/ 26 levels "0","1","5","86",..: 8 15 26 21 24 25 9 3 6 20 ...
## $ Gold : Factor w/ 11 levels "0","1","10","14",..: 4 3 11 11 9 9 8 5 7 7 ...
## $ Silver: Factor w/ 12 levels "0","1","13","15",..: 10 3 4 11 9 1 5 7 9 2 ...
## $ Bronze: Factor w/ 11 levels "0","1","11","13",..: 9 11 4 10 5 7 8 3 10 7 ...
## $ Total : Factor w/ 15 levels "1","11","14",..: 8 10 11 7 3 15 2 NA 5 14 ...
Часто бывает, что в таблице были объединенные ячейки и тогда данные в некоторых строках оказываются смещены. У нас меньше 30 наблюдений и можно почистить руками, но мы потренируемся, чтобы быть готовыми к таблице с миллионом строк.
df[5:9, ]
## Rank Nation Gold Silver Bronze Total
## 5 5 South Korea (KOR) 6 6 2 14
## 6 6 Switzerland (SUI) 6 0 3 9
## 7 7 China (CHN) 5 2 4 11
## 8 Sweden (SWE) 5 2 4 11 <NA>
## 9 9 Austria (AUT) 4 6 6 16
Узнаем, какие строчки сдвинуты:
to.correct <- is.na(df$Total)
to.correct
## [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE
## [12] FALSE FALSE FALSE FALSE FALSE FALSE TRUE FALSE FALSE FALSE TRUE
## [23] FALSE FALSE FALSE TRUE TRUE
R пытается автоматом присвоить переменным правильный тип (целое число, дробное число, факторная переменная). И для избежания ошибок создаёт разные запреты: например, если у факторной переменной было заявлено два значения “да” и “нет”, то присвоить ей что-то иное не получится.
Поскольку у нас возникла путаница, то мы сначала переведем все переменные в текстовые, чтобы R не ругался на якобы ошибочные действия:
df$Rank <- as.character(df$Rank)
df$Nation <- as.character(df$Nation)
df$Gold <- as.character(df$Gold)
df$Silver <- as.character(df$Silver)
df$Bronze <- as.character(df$Bronze)
df$Total <- as.character(df$Total)
Сдвигаем нужные строки:
df[to.correct, 2:6] <- df[to.correct, 1:5]
Указываем правильные типы данных:
df$Nation <- as.character(df$Nation)
df$Gold <- as.numeric(df$Gold)
df$Silver <- as.numeric(df$Silver)
df$Bronze <- as.numeric(df$Bronze)
df$Total <- as.numeric(df$Total)
Смотрим на почти идеальную таблицку. Останется только присвоить правильный ранг там, где сейчас в графе ранг стоит название страны:
df[5:9, ]
## Rank Nation Gold Silver Bronze Total
## 5 5 South Korea (KOR) 6 6 2 14
## 6 6 Switzerland (SUI) 6 0 3 9
## 7 7 China (CHN) 5 2 4 11
## 8 Sweden (SWE) Sweden (SWE) 5 2 4 11
## 9 9 Austria (AUT) 4 6 6 16
summary(df)
## Rank Nation Gold Silver
## Length:27 Length:27 Min. : 0.00 Min. : 0.00
## Class :character Class :character 1st Qu.: 0.50 1st Qu.: 1.00
## Mode :character Mode :character Median : 2.00 Median : 2.00
## Mean : 6.37 Mean : 6.44
## 3rd Qu.: 5.50 3rd Qu.: 5.50
## Max. :86.00 Max. :87.00
## Bronze Total
## Min. : 0.0 Min. : 1.0
## 1st Qu.: 1.0 1st Qu.: 3.0
## Median : 3.0 Median : 6.0
## Mean : 6.3 Mean : 19.1
## 3rd Qu.: 5.5 3rd Qu.: 14.5
## Max. :85.0 Max. :258.0
Кстати, Гугл-докс тоже умеет это делать! Создаёте пустую таблицу на docs.google.com. В верхней левой ячейке набираете:
=ImportHtml("http://en.wikipedia.org/wiki/2010_Winter_Olympics_medal_table","table",3)
И наслаждаетесь тем же результатом!
При копании в текстовых данных полезны бывают фукнции
Текстовые данные могут быть:
Есть два стратегии искать данные в XML файле. Одна удобна для небольших деревьев, другая требует больше усилий, но позволяет работать с огромнейшими XML файлами.
…
Порой мы насохраняли много однотипных файлов и нужно их объединить автоматически в один.
Получаем список файлов с расшерением .csv
в текущей папке:
file.list <- dir("*.csv")
print(file.list)
## character(0)
А дальше их можно читать в цикле:
all.data <- NULL
for (f in file.list) {
data <- read.csv(f) # читаем очередной файл
cat(paste("Файл ", f, "содержит", nrow(data), "строк."))
all.data <- rbind(all.data, data) # дописываем его в большую табличку
}
И на выходе мы получаем большой массив данных
str(all.data)
## NULL
Можно парсить данные с помощью другого языка программирования, и только потом статистическую обработку делать в R. Например, подойдёт Питон с библиотеками beautiful soup
или lxml
.
Можно поискать готовые программы для парсинга. Их много платных. Есть бесплатные. Зачастую (личное мнение) чтобы разобраться с программой уйдёт времени почти столько же, сколько на программирование, а пользы явно меньше. Всё-таки при парсинге приходится решать кучу мелких технических вопросов.