Журнал LinuxFormat - перейти на главную

LXF102:R

Материал из Linuxformat
Перейти к: навигация, поиск
R Свободный инструментарий для статистической обработки данных

Содержание

Данные и графики

ЧАСТЬ 2 Анализ «хороших» данных – это просто. А вот чтобы сделать ваши данные «хорошими», а затем и представить их – придётся попотеть. К счастью, Алексей Шипунов и Евгений Балдин знают, как облагородить невзрачные столбцы чисел.

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

R и работа с данными

Данные можно представить в текстовом или в двоичном виде. Не вдаваясь в детали, примем, что текстовые данные – это те, которые можно прочитать и отредактировать в текстовом редакторе (Emacs/Vi и прочее). Чтобы отредактировать двоичные данные, как правило, нужна программа, которая эти данные произвела.

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

Первое, что надо сделать перед чтением данных – это убедиться, что текущая директория в R и та директория, где находятся данные, есть одно и то же. Для этого в запущенной сессии R надо ввести команду:

> getwd()
[1] “/home/username/”

Пусть это вовсе не та директория, в которой лежат данные. Тогда поменять ее можно командой:

> setwd(“./workdir”)
> getwd()
[1] “/home/username/workdir”

Как обычно, развернутую справку можно получить с помощью вызова help(getwd).

Далее следует проверить, есть ли в текущей директории нужный файл:

> dir()
[1] “mydata.txt”

Вот теперь можно и загрузить данные. За чтение табличных текстовых данных отвечает команда read.table():

> read.table(“mydata.txt”, sep=”;”, head=TRUE)
абв
1123
2456
3789

Все очень просто, за исключением того, что перед чтением нужно знать, в каком формате хранятся данные – а именно, что у столбцов есть имена (head=TRUE) и что разделителем является точка с запятой (sep=”;”). Функция read.table() очень хороша, но не настолько умна, чтобы определять формат данных на лету. Чтобы просмотреть содержимое файла, не выходя из R, можно воспользоваться функцией file.show():

> file.show(“mydata.txt”)
а;б;в
1;2;3
4;5;6
7;8;9

Многие команды R, включая и read.table(), имеют для аргументов значения по умолчанию. Например, значение sep по умолчанию равно “”. В данном случае это означает, что разделителем является любое количество пробелов или знаков табуляции, поэтому если данные вместо точек с запятыми разделены пробельными символами, то аргумент sep можно не указывать. Естественно, бывает безумное множество различных частных случаев, и сколько бы усилий ни было приложено, все не описать. Отметим, однако, еще несколько важных моментов:

  • Файлы можно загружать и из других директорий, при этом можно использовать относительную адресацию:
> read.table(“../workdir/mydata.txt”)
  • Русский текст в файлах читается без проблем, если он набранв кодировке, совпадающей с текущей локалью. Пусть локаль – ru_RU.KOI8-R, а сам файл закодирован в UTF-8; тогда при его чтении следует воспользоваться функцией file():
> read.table(
+ file(“mydata-unicode.txt”, encoding=”UTF-8”),
+                       sep=”;”, head=TRUE)
 абв
1123
2456
3789

Иногда нужно, чтобы R прочитал, кроме имен столбцов, еще и имена строк. В этом случае в первой строке должно быть на одну колонку меньше, чем в теле таблицы (в данном примере – три вместо четырех):

> file.show(“mydata2.txt”)
абв
раз 1 2 3
два 4 5 6
три 7 8 9
> read.table(“mydata2.txt”, head=TRUE)
  абв
раз 1 2 3
два 4 5 6
три 7 8 9
  • По отечественным правилам, в качестве десятичного разделителянужно использовать запятую, а не точку. Если кто-то при подготовке исходных данных этим правилам последовал, то необходимо переопределить аргумент dec:
 > read.table(“mydata3.txt”, dec=”,”, h=T)
    а б в
 раз 1.1 2.2 3.3
 два 4.4 5.0 6.0
 три 7.0 8.0 9.0

Обратите внимание на сокращенное обозначение аргумента и его значения (h=T). Сокращать можно и нужно, но с осторожностью, поэтому в тексте мы всегда будем использовать TRUE/FALSE.

В целом, с текстовыми таблицами больших проблем не возникает. Разные экзотические текстовые форматы, как правило, можно преобразовать к «типичным» если не с помощью R, то с помощью каких-нибудь многочисленнейших текстовых утилит (вплоть до «тяжеловесов» типа языка Perl). А вот с «посторонними» двоичными форматами дело обстоит гораздо хуже. Здесь, прежде всего, возникают проблемы, связанные с полностью закрытыми форматами, например, такими, как формат популярной в определенных кругах электронной таблицы Microsoft Excel. Вообще говоря, ответ на вопрос: «Как прочитать двоичный формат в R?» часто сводится к совету по образцу известного анекдота: «выключим газ, выльем воду и вернемся к условию предыдущей задачи». То есть надо найти способ, как преобразовать двоичные данные в обычные текстовые таблицы. Проблем на этом пути возникает обычно не слишком много, но уж больно они разнообразные.

Второй путь – это найти способ прочитать данные в R без преобразования. Специально для этих целей в R есть пакет foreign, который может читать двоичные данные, выводимые пакетами Minitab, S, SAS, SPSS, Stata, Systat, а также формат DBF. Чтобы узнать подробнее об определенных в этом пакете командах, надо загрузить пакет и вызвать общую справку:

 > library(foreign)
 > help(package=foreign)

Что же касается пресловутого формата Excel, то здесь дело обстоит хуже. Существует не меньше пяти разных способов загрузить эти файлы в R, но все они имеют ограничения. К тому же новый формат MS Excel 2007 пока вообще не поддерживается. Из всех способов наиболее привлекательным представляется обмен с R через буфер. Если открыть в OpenOffice Calc XLS-файл, то можно скопировать в буфер обмена любое количество ячеек, а потом загрузить их в R:

 > read.table(“clipboard”)

Это очень просто, и главное, работает с любой Excel-подобной программой.

Тут следует отметить еще одну вещь: ни в коем случае не рекомендуется производить какой-либо статистический анализ в программах электронных таблиц. Не говоря уже о том, что Интернет просто забит статьями об ошибках в этих программах и/или в их статистических модулях, это еще и крайне неверно идеологически. Иначе говоря: Use R!

Добавим еще несколько деталей:

  • R может загружать изображения. Для этого есть сразу несколько пакетов. Наиболее продвинутый из них – pixmap. R также может загружать карты в формате ArcInfo и др. (пакеты maps, maptools) и много чего еще.
  • У R есть собственный двоичный формат. Он быстро записывается и быстро загружается, но его нельзя использовать с другими программами:
 > x <- “яблоко”
 > save(x, file=”x.rd”)
> rm(x)
>x
Ошибка: объект “x” не найден
> dir()
[1] “x.rd”
> load(“x.rd”)
>x
[1] “яблоко”

Для сохранения и загрузки двоичных файлов служат команды save() и load(), для создания объекта – <-, а для удаления – rm().

  • Для R написано множество интерфейсов к базам данных, в частности, для MySQL, PostgresSQL и SQLite (последний может вызываться прямо из R, см. пакеты RSQLite и sqldf).
  • Наконец, R сам может записывать таблицы и другие результаты обработки данных, и, разумеется, графики. Об этом мы и поговорим ниже.

Графики

Несмотря на то, что «настоящие» статистики часто относятся к графикам почти с презрением, для «широких масс» одним из основных достоинств R служит именно удивительное разнообразие типов графиков, которые он умеет строить. R в этом смысле – один из рекордсменов. В базовом наборе есть несколько десятков типов графиков, еще больше в рекомендуемом пакете lattice, и, естественно, намного больше в пакетах с CRAN. По оценочным прикидкам получается, что разнообразных типов графиков в R никак не меньше тысячи. При этом они еще и достаточно хорошо настраиваются, то есть пользователь при желании может легко разнообразить эту исходную тысячу на свой вкус.

Два типа графических команд

Для правильного отображения кириллицы в X-окне (если это действительно необходимо) следует правильно указать шрифты, например, так:

> X11(fonts = c(
+ “-rfx-helvetica-%s-%s-*-*-%d-*-*-*-*-*-koi8-r”,
+ “-adobe-symbol-medium-r-*-*-%d-*-*-*-*-*-*-*”))

Опция fonts команды X11 принимает вектор из двух элементов, формируемый командой c(). Первый соответствует шрифту, используемому для обычных текстовых меток, а второй предназначен для отображения стандартных спецсимволов, например, греческого алфавита. С помощью программы xfontsel можно подобрать себе шрифт по вкусу. Подробности в ищите разделе Fonts в документации, выдаваемой по help(X11).

Рассмотрим пример:

> plot(1:20, main=”Заголовок”)
legend(“topleft”, pch=1, legend=”Мои точки”)

Тут много такого, о чем пока речи не шло. Но самое главное – то, что первая команда рисует график «с нуля», тогда как вторая только добавляет к уже нарисованному графику детали. Это и есть два типа графических команд, используемых в базовом графическом наборе R. Теперь немного подробнее: plot() – основная графическая команда, причем команда «умная» (правильнее сказать «generic», или общая). Это значит, что она распознает тип объекта, который подлежит рисованию, и строит график в соответствии с ним. Например, в приведенном примере 1:20 – это последовательность чисел от 1 до 20, то есть вектор, а для одиночного вектора предусмотрен график, где по оси абсцисс расположены индексы (номера каждого элемента вектора по порядку), а по оси ординат – сами элементы. Если в аргументе команды будет что-то другое, то будет построен иной график, более подходящий для этого объекта. Вот пример:

> plot(cars)
> title(main=”Автомобили 20-х годов”)

Здесь тоже использованы команды обоих типов, но оформлены они немного иначе. Не беда, что мы забыли дать заголовок в команде plot(), так как его всегда можно добавить потом, командой title(). «cars» – это встроенная в R таблица данных (подробности – в выводе команды ?cars), которая использована здесь по прямому назначению, то есть для демонстрации возможностей программы. Для нас сейчас важно, что это не вектор, а таблица из двух колонок: speed и distance (скорость и тормозной путь). Функция plot() автоматически нарисовала так называемый scatterplot, когда по оси X откладывается значение одной переменной (колонки), а по оси Y – другой, и еще присвоила осям имена этих колонок. Любопытным советуем проверить, что нарисует plot(), если ему «подсунуть» таблицу с тремя колонками, скажем, встроенную таблицу «trees». Кстати говоря, узнать, какие еще есть встроенные таблицы, можно с помощью команды data() (именно так, без аргументов).

Графические устройства

Встретив команду plot(), R открывает так называемое экранное графическое устройство (в случае X Window это стандартное окно X11) и начинает вывод на него. Если следующая команда того же типа, то есть не добавляющая, то R «сотрет» старое изображение и начнет выводить новое в этом же окне. Если ввести команду:

> dev.off()

то R закроет графическое окно, что, впрочем, можно сделать, просто щелкнув на кнопке в рамке оконного менеджера. Экранных устройств в R предусмотрено несколько – в каждой операционной системе свое (а в Mac OS X даже два). Но все это не так важно, пока вы не захотите строить графики и сохранять их в виде графических файлов. В этом случае придется познакомиться с другими графическими устройствами. Их несколько (количество опять-таки зависит от операционной системы), а пакеты предоставляют еще около десятка. Работают они примерно так:

> png(file=”1-20.png”, bg=”transparent”)
> plot(1:20)
> dev.off()

Команда png() открывает одноименное графическое устройство, причем задается параметр, включающий прозрачность базового фона (удобно, например, для Web). Такого параметра у экранных устройств нет. Как только вводится команда dev.off(), устройство закрывается, и на диске появляется файл 1-20.png. png() – одно из самых распространенных устройств при записи файлов. Недостатком его является, разумеется, растровая природа формата. Аналогичным по своей функциональности является и устройство jpeg(), которое производит JPEG-файлы.

R поддерживает и векторные форматы, например, PDF. Здесь, однако, могут возникнуть специфические для русскоязычного пользователя трудности со шрифтами. Остановимся на них чуть подробнее. Вот как надо «правильно» создавать PDF-файл, содержащий русский текст:

> pdf(“1-20.pdf”, family=”NimbusSan”, encoding=”KOI8-R.enc”)
> plot(1:20, main=”Заголовок”)
> dev.off()
> embedFonts(“1-20.pdf”)

Как видно, требуется указать, какой шрифт мы будем использовать, а также кодировку, с которой мы работаем. Помимо KOI8-R, из восьмибитных кириллических кодировок доступны CP-1251 и KOI8-U. Затем нужно закрыть графическое устройство и встроить в полученный файл шрифты с помощью команды embedFonts(). Следует отметить, что шрифт NimbusSan и возможность встраивания шрифтов командой обеспечивается взаимодействием R со сторонней программой Ghostscript, в поставку которой входят шрифты, содержащие русские буквы. Кроме PDF, R «знает» и другие векторные форматы, например, PostScript, xfig и picTeX. Есть отдельный пакет RSvgDevice, который поддерживает популярный векторный формат SVG. График в этом формате можно, например, открыть и видоизменить в свободном векторном редакторе Inkscape.

Графические опции

Как уже говорилось, графика в R настраивается в очень широких пределах. Один из способов настройки – это видоизменение графических опций, встроенных в R. Вот, к примеру, распространенная задача: нарисовать две гистограммы одну под другой на одном рисунке. Чтобы это сделать, надо изменить исходные опции – а именно, разделить пространство рисунка на две части, примерно так:

> # Создается eps-файл размером 6 на 6 дюймов
> postscript(“2hist.eps”,width=6.0,height=6.0,
+ horizontal=FALSE,onefile=FALSE,paper=”special”)
> # Изменяется одно из значений по умолчанию
> old.par <- par(mfrow=c(2,1))
> hist(cars$speed)
> hist(cars$dist)
> # Восстанавливаем старое значение по умолчанию
> par(old.par)
> dev.off()

Ключевая команда здесь par() – изменяется один из ее параметров, mfrow, который регулирует, сколько изображений и как будет размещено на «листе». Значение mfrow по умолчанию – c(1,1), то есть один график по вертикали и один по горизонтали. Чтобы не печатать каждый раз команду par() без аргументов (для выяснения «умалчиваемых» значений каждого из 71 параметра), мы «запомнили» старое значение в объекте old.par, а в конце вернули состояние к сохраненному. То, что команда hist() строит гистограмму, очевидно из названия.

Идеологически верная графика

При всем своем разнообразии, графическая система в R построена на основе строгих правил. Выбор типа графика, основных цветов и символов для изображения точек, расположение подписей и т.д. были тщательно продуманы создателями. Одним из ключевых для R исследований является книга Уильяма Кливленда «Элементы графической обработки данных». Многие его идеи были осуществлены именно в S-PLUS, а затем и в R. Например, Кливленд нашел, что традиционные «столбчатые» графики очень плохо воспринимаются, особенно когда речь идет о близких значениях данных, и предложил им на замену «точечные диаграммы». Вот так они реализованы в R:

> dotchart(Titanic[,,”Adult”,”No”],
+                 main=’Погибшие на “Титанике”’)

Встроенная таблица данных Titanic – это четырехмерная матрица, которая отражает статистику по возрастным группам, типу билета и полу.

Особенно активно Кливленд (и не только он) возражал против использования трехмерных графиков и так называемых «пирогов». Поначалу «пирожных» графиков в R вовсе не было, причем по принципиальным соображениям. Сегодня они есть, но если вы откроете страницу помощи, то узнаете, что «Pie charts are a very bad way of displaying information». Трехмерных графиков в R и сейчас немного (правда, есть особый пакет rgl, позволяющий строить такие графики через OpenGL), а если вы хотите узнать, как меняется поведение двух переменных по отношению к третьей, предлагаются так называемые «Trellis graphs» или графики-решетки:


> coplot(log(Volume) ~ log(Girth) | Height, data = trees)

При выполнении этой команды на рисунке отображается, как зависит объем древесины от объема кроны (в логарифмической шкале) у деревьев различной высоты. Действительно, такое представление гораздо эффективнее трехмерного. Странно, что распространенные пакеты статобработки почти не используют графики-решетки, хотя их наличие неоднократно называлось одной из главных причин коммерческого успеха S-PLUS.

Интерактивность

Интерактивная графика позволяет выяснить, где именно на графике расположены нужные вам точки, и поместить объект (скажем, подпись) в нужное место, а также проследить «судьбу» одних и тех же точек на разных графиках. Кроме того, если данные многомерные, то можно вращать облако точек в плоскости разных переменных, чтобы выяснить структуру данных.

Еще несколько лет назад пришлось бы написать, что здесь вместо R следует воспользоваться другими аналитическими инструментами, но R развивается так быстро, что все эти методы теперь доступны, причем в нескольких вариантах. Например, так можно добавлять подписи в указанную мышью область графика:

> plot(1:20)
> text(locator(), “Моя любимая точка”, pos=4)

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

Интерактивная графика других типов реализована командой identify(), а также пакетами rggobi, TeachingDemos и iplot.

Как сохранять результаты

Начинающие работу с R обычно просто копируют результаты работы (скажем, данные тестов) из консоли R в текстовый файл. И действительно, на первых порах этого может оказаться достаточно. Однако рано или поздно возникает необходимость сохранять созданные объемные объекты (например, таблицы данных). Можно использовать уже упомянутый в начале статьи внутренний двоичный формат, но это не всегда удобно. Лучше всего сохранять таблицы данных в виде текстовых таблиц, которые потом можно будет открывать другими приложениями или текстовыми редакторами. Для этого служит команда write.table():

> write.table(file=”trees.csv”, trees,
+            row.names=F, sep=”;”, quote=F)

В текущую рабочую директорию будет записан файл trees.csv, образованный из встроенной в R таблицы данных trees. А что, если надо записать во внешний файл результаты выполнения команд? В этом случае используется команда sink():

> sink(“1.txt”, split=T)
> 2+2
[1] 4
> sink()

При этом во внешний файл запишется строчка “[1] 4”, то есть результат выполнения команды. Сама команда записана не будет, а если хочется, чтобы она была записана, то придется ввести что-то вроде:

> print(“2+2”)
[1] “2+2”
> 2+2
[1] 4

то есть повторять каждую команду два раза. Для сохранения истории команд служит команда savehistory(), а для сохранения всех созданных объектов – save.image(). Последняя может оказаться также полезной для сохранения промежуточных результатов, если вы не уверены в стабильности работы компьютера.

Мастера отчетов

Таблицы, созданные в R, можно сохранять и в более «приличном» виде: например, в форматах LaTeX или HTML, при помощи пакета xtable. Естественно, хочется пойти дальше, и сохранять в каком-нибудь из этих форматов вообще всю R-сессию. Для HTML такое возможно, если использовать пакет R2HTML с CRAN:

> library(R2HTML)
> dir.create(“example”)
> HTMLStart(“example”)
HTML> 2+2
HTML> plot(1:20)
HTML> HTMLplot()
HTML> HTMLStop()

В рабочей директории будет создана поддиректория example, и туда будут записаны HTML-файлы, содержащие полный отчет о текущей сессии, в том числе и созданный график.

Можно пойти и еще дальше. Что если создать файл, который будет содержать код R, перемешанный с текстовыми комментариями, и потом «скормить» этот файл R так, чтобы фрагменты кода заменились на результат их исполнения? Идея эта называется «literate programming» (грамотное программирование) и принадлежит Дональду Кнуту, создателю TeX. В случае R такая система используется для автоматической генерации отчетов – функции, которая фактически отсутствует в остальных статистических пакетах и делает R поистине незаменимым. Для создания подобного отчета, для начала надо набрать простой файл c LaTeX-подобной структурой и назвать его, например, test-Sweave.Rnw:

\documentclass[a4paper,12pt]{scrartcl}
% Стандартная шапка для LaTeX-документа
\usepackage[T2A]{fontenc}
% В зависимости от используемой локали вместо koi8-r нужно
%поставить cp1251 или utf8
\usepackage[koi8-r]{inputenc}
\usepackage[english,russian]{babel}
\usepackage{indentfirst}
\title{Тест Sweave}
\author{А.В.\,Тор}
\begin{document} % Тело документа
\maketitle
\textsf{R} как калькулятор:
<<echo=TRUE,print=TRUE>>=
1+1
1 + pi
sin(pi/2)
@
Картинка:
<<fig=TRUE>>=
plot(1:20)
@
\end{document}
     Затем этот файл необходимо обработать в R:
 > Sweave(“test-Sweave.Rnw”)
 Writing to file test-Sweave.tex
 Processing code chunks ...
  1 : echo print term verbatim
  2 : echo term verbatim eps pdf
 You can now run LaTeX on ‘test-Sweave.tex’

При этом создается готовый LaTeX-файл test-Sweave.tex. И, наконец, при помощи latex/dvips или pdflatex получить результирующий файл:

 => latex test-Sweave.tex
 => dvips test-Sweave.dvi
 => gv test-Sweave.ps
 # или
 => pdflatex test-Sweave.tex
 => acroread test-Sweave.pdf

Такой отчет можно расширять, шлифовать, изменять исходные данные, и при этом усилия по оформлению практически сводятся на нет. Если есть желание, чтобы код R набирался моноширинным шрифтом, то в LaTeX-преамбуле RNW-файла следует добавить строчку:

 \usepackage[noae]{Sweave}

Исходный код и авторскую документацию профессора Фридриха Лайша (Friedrich Leisch) можно найти здесь: http://www.ci.tuwien.ac.at/~leisch/Sweave/.

Помимо Sweave, есть и другие системы генерации отчетов: например, уже упомянутый пакет R2HTML умеет производить похожие отчеты в HTML. Есть пакет brew, который позволяет создавать автоматические отчеты в текстовой форме (разумеется, без графиков), и совсем новый пакет odfWeave, который может работать с ODF (формат OpenOffice.org). LXF

Персональные инструменты
купить
подписаться
Яндекс.Метрика