- Подписка на печатную версию:
- Подписка на электронную версию:
- Подшивки старых номеров журнала (печатные версии)
LXF102:R
Материал из Linuxformat.
- R Свободный инструментарий для статистической обработки данных
Содержание |
Данные и графики
R |
---|
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