Как организовать большие программы R?

161

Когда я беру проект R любой сложности, мои сценарии быстро становятся длинными и запутанными.

Какие методы я могу использовать, чтобы с моим кодом всегда было приятно работать? Я думаю о таких вещах, как

  • Размещение функций в исходных файлах
  • Когда что-то разбить на другой исходный файл
  • Что должно быть в мастер-файле
  • Использование функций в качестве организационных единиц (стоит ли это, учитывая, что R затрудняет доступ к глобальному состоянию)
  • Отступы / практики разрыва строки.
    • Лечить (как {?
    • Положить такие вещи, как)} на 1 или 2 строки?

Каковы ваши основные правила организации больших R-скриптов?

Дэн Гольдштейн
источник
12
должно быть сообщество вики
SilentGhost
Вы также можете посмотреть на ProjectTemplateпакет.
ctbrown

Ответы:

71

Стандартный ответ - использовать пакеты - см. Руководство по Writing R Extensions, а также различные учебные пособия в Интернете.

Это дает вам

  • квазиавтоматический способ организации вашего кода по темам
  • настоятельно рекомендует вам написать файл справки, чтобы вы думали об интерфейсе
  • много проверок здравомыслия через R CMD check
  • возможность добавить регрессионные тесты
  • а также средство для пространств имен.

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

Что касается части «как редактировать», то в руководстве R Internals есть отличные стандарты кодирования R в Разделе 6. В противном случае, я склонен использовать значения по умолчанию в режиме ESS Emacs .

Обновление 2008-Август-13: Дэвид Смит просто блог о R Style Guide Google .

Дирк Эддельбюттель
источник
8
Если вы выращиваете свое исходное дерево / анализ "органически", вам не трудно это сделать / громоздко? Если вы заметили в своем коде ошибку (часто встречающуюся при исследовании новой проблемной области), вы должны (i) исправить исходный код; (ii) переустановить пакет; (iii) перезагрузить его в свое рабочее пространство? Есть ли способ вызвать библиотеку (...), чтобы перезагрузить уже загруженный пакет (шаг iii выше)? Разве вам не нужно убивать ваше рабочее пространство, перезапустите R и перезагрузите вашу библиотеку / пакет, чтобы увидеть, правильно ли это?
Стив Лианоглу
1
Попытка поиска в Google для стиля кодирования.
Хэдли
3
@ SteveLianoglou Я знаю, что это довольно старый, но пакет devtools Хэдли делает перезагрузку всего вашего кода очень легкой.
Дейсон
1
Это сообщение в блоге дает (мое мнение) действительно хорошее быстрое руководство по созданию первого пакета: hilaryparker.com/2014/04/29/writing-an-r-package-from-scratch
panterasBox
1
Вот рабочая ссылка на Google R Style Guide
Денис Расулев,
51

Мне нравится помещать различные функции в свои файлы.

Но мне не нравится система пакетов R. Это довольно сложно использовать.

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

util = new.env()

util$bgrep = function [...]

util$timeit = function [...]

while("util" %in% search())
  detach("util")
attach(util)

Это все в файле util.R . Когда вы получаете его, вы получаете среду «util», чтобы вы могли звонить util$bgrep()и тому подобное; но, кроме того, attach()призыв делает это так справедливо bgrep()и такая работа напрямую. Если бы вы не поместили все эти функции в их собственное окружение, они бы загрязнили пространство имен верхнего уровня интерпретатора (которое ls()показывает).

Я пытался смоделировать систему Python, где каждый файл является модулем. Это было бы лучше иметь, но это, кажется, хорошо.

Брендан Оконнор
источник
Спасибо, Брендан. Это очень полезно. Что случилось с циклом while? Что не так с if (! ("Util"% in% search ())) attach (util)
Дэн Голдштейн
2
так что вы можете делать исходные тексты ("util.R") снова и снова, если вы хотите настроить его и тому подобное.
Брендан Оконнор
вам не нужен цикл while - все, что вам нужно, это detach (util). я не могу вспомнить, выдает ли это ошибку или нет, если она еще не загружена, но это наиболее безопасно и работает. предложения приветствуются.
Брендан Оконнор
1
Создание специфичных для функций сред и присоединение к ним - путь для меня. Другой способ добиться того же по-другому (с большей модульности) заключается в использовании sys.source: MyEnv <- attach(NULL, name=s_env); sys.source(file, MyEnv). Я даже объявляю (в своем собственном окружении!) При запуске функцию, sys.source2которая будет искать, если окружение с таким же именем уже здесь, и передает это вместо создания нового. Это делает добавление личных функций быстрым, простым и организованным :-)
Антуан Лизе
5
Только что видел это сегодня, и это относится к пакетной альтернативе: github.com/klmr/modules
ctbrown
34

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

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

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

Когда извлекать логические единицы в их собственные физические единицы (например, исходные файлы и большие группы, такие как пакеты)? У меня есть два случая. Во-первых, если файл становится слишком большим, и прокрутка между логически не связанными единицами - раздражение. Во-вторых, если у меня есть функции, которые могут быть использованы другими программами. Я обычно начинаю с помещения некоторого сгруппированного блока, скажем, функций манипулирования данными, в отдельный файл. Затем я могу получить этот файл из любого другого сценария.

Если вы собираетесь развернуть свои функции, то вам нужно начать думать о пакетах. Я не использую R-код в производстве или для повторного использования другими по различным причинам (кратко: культура org предпочитает другие языки, проблемы с производительностью, GPL и т. Д.). Кроме того, я склонен постоянно совершенствовать и добавлять свои коллекции исходных файлов, и я бы предпочел не иметь дело с пакетами при внесении изменений. Так что вы должны проверить другие ответы, связанные с пакетом, такие как Дирк, для более подробной информации по этому вопросу.

Наконец, я думаю, что ваш вопрос не обязательно относится к R. Я действительно рекомендовал бы прочитать Code Complete Стивом Макконнеллом, в котором содержится много знаний о таких проблемах и практике кодирования в целом.

АРС
источник
3
Очень полезный комментарий, ars, спасибо. Я программист, но это хорошо, чтобы проверить с другими. Когда вы говорите: «Так как R не поддерживает вызов по ссылке, я обычно опасаюсь, что мои функции слишком детализированы», я слышу вас. Я привык писать такие функции, как ReadData (); CleanData (); AnalyzeData (); GraphData (); и R делает это громоздким. Я осознаю, что мне нужно использовать «источник» так, как я использую функции в других языках.
Дэн Голдштейн
2
Ты прав, Дэн. Я использую «исходный код» таким образом для задач подготовки набора данных, поэтому я могу просто использовать подготовленный фрейм data.frame для других сценариев, где проводится настоящий анализ. Я никогда не был уверен, что это хорошая практика, потому что это выглядит странно по сравнению с другими языками - больше похоже на сценарии оболочки. Это хорошо для сравнения заметок. :)
АРС
Хороший ответ и комментарии. Я нахожу, что это особенно раздражает в R, потому что вы часто работаете с данными в определенном формате, для которого действительно сложно писать повторно используемые функции. Итак, вы пишете хороший функциональный код, хотя вы знаете, что он никогда не будет использоваться повторно, или вы просто пишете быстрый и неприятный процедурный код, просто чтобы получить немного больше эффективности с большими объектами? Немного дилеммы .. R был бы просто потрясающим с помощью обращения по ссылке ..
naught101
Ну, это можно сделать, вроде как , но повышения эффективности нет ...
naught101
19

Я согласен с советом Дирка! ИМХО, организация ваших программ от простых скриптов до документированных пакетов для программирования на R - это как переключение с Word на TeX / LaTeX для написания. Я рекомендую взглянуть на очень полезное создание пакетов R: Учебное пособие Фридриха Лейша.

Paolo
источник
6
Пакеты выглядят неотразимо. Тем не менее, я волновался, что они могут быть излишними. Я не пишу код общего назначения. Большая часть того, что я делаю, это проверка этой гипотезы, проверка этой гипотезы, построение графика, настройка параметров графика, построение графика, изменение формы данных, построение графика. Я делаю вещи, которые после завершения, вероятно, никогда не будут перезапущены.
Дэн Голдштейн
1
В этом случае вы должны взглянуть на Sweave. Он сочетает в себе код R с LaTeX. Таким образом, у вас есть анализ и источник отчета вместе.
Тьерри
15

Мой краткий ответ:

  1. Тщательно напишите свои функции, указав достаточно общие выводы и входы;
  2. Ограничить использование глобальных переменных;
  3. Используйте объекты S3 и, при необходимости, объекты S4;
  4. Поместите функции в пакеты, особенно когда ваши функции вызывают C / Fortran.

Я считаю, что R все больше и больше используется в производстве, поэтому потребность в повторно используемом коде больше, чем раньше. Я нахожу переводчика гораздо более надежным, чем раньше. Нет сомнений в том, что R в 100-300 раз медленнее, чем C, но обычно узкое место концентрируется вокруг нескольких строк кода, которые можно делегировать C / C ++. Я думаю, что было бы ошибкой делегировать сильные стороны R в манипулировании данными и статистическом анализе на другой язык. В этих случаях снижение производительности является низким и в любом случае оправдывает затраты на разработку. Если бы речь шла только о времени выполнения, мы бы все писали на ассемблере.

с промежутками
источник
11

Я хотел выяснить, как писать пакеты, но не потратил время. Для каждого из моих мини-проектов я храню все свои низкоуровневые функции в папке с именем 'functions /' и помещаю их в отдельное пространство имен, которое я явно создаю.

Следующие строки кода создадут среду с именем "myfuncs" в пути поиска, если он еще не существует (с помощью attach), и заполнят его функциями, содержащимися в файлах .r в моем каталоге 'functions /' (используя sys.source). Я обычно помещаю эти строки вверху основного сценария, предназначенного для «пользовательского интерфейса», из которого вызываются функции высокого уровня (вызывая функции низкого уровня).

if( length(grep("^myfuncs$",search()))==0 )
  attach("myfuncs",pos=2)
for( f in list.files("functions","\\.r$",full=TRUE) )
  sys.source(f,pos.to.env(grep("^myfuncs$",search())))

Когда вы вносите изменения, вы всегда можете повторно использовать его с теми же строками или использовать что-то вроде

evalq(f <- function(x) x * 2, pos.to.env(grep("^myfuncs$",search())))

оценить дополнения / модификации в среде, которую вы создали.

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

Что касается соглашений о кодировании, то это единственное, что я видел в отношении эстетики (мне они нравятся, и я немного следую им, но я не использую слишком много фигурных скобок в R):

http://www1.maths.lth.se/help/R/RCC/

Существуют и другие «соглашения» относительно использования [, drop = FALSE] и <- как предложил оператор присваивания в различных презентациях (обычно ключевых) на useR! конференции, но я не думаю, что какие-либо из них являются строгими (хотя [, drop = FALSE] полезен для программ, в которых вы не уверены в ожидаемом вводе).

hatmatrix
источник
6

Считай меня другим человеком в пользу посылок. Я признаю, что был довольно слаб в написании man-страниц и виньеток до тех пор, пока / когда мне придется (т.е. быть освобожденным), но это делает реальный удобный способ связать исходную лань. Кроме того, если вы серьезно относитесь к поддержанию своего кода, все пункты, о которых говорит Дирк, вступают в силу.

geoffjentry
источник
4

Я тоже согласен. Используйте функцию package.skeleton () для начала. Даже если вы думаете, что ваш код больше никогда не будет запущен, это может помочь мотивировать вас на создание более общего кода, который впоследствии сэкономит ваше время.

Что касается доступа к глобальной среде, это легко сделать с помощью оператора << -, хотя это не рекомендуется.

cameron.bracken
источник
3

Еще не научившись писать пакеты, я всегда организовывал поиск субскриптов. Это похоже на написание уроков, но не так сложно. Это не очень элегантно, но я считаю, что со временем я строю анализы. Когда у меня работает большой раздел, я часто перемещаю его в другой сценарий и просто использую его, так как он будет использовать объекты рабочей области. Возможно, мне нужно импортировать данные из нескольких источников, отсортировать их все и найти пересечения. Я мог бы поместить этот раздел в дополнительный скрипт. Однако, если вы хотите распространять свое «приложение» для других людей или оно использует какой-то интерактивный ввод, пакет, вероятно, является хорошим маршрутом. Как исследователь, мне редко нужно распространять свой код анализа, но мне ЧАСТО нужно увеличивать или настраивать его.

kpierce8
источник
Я использовал этот метод, но с тех пор понял, что функции и пакеты лучше, чем источник ("next_script.R"). Я написал об этом здесь: stackoverflow.com/questions/25273166/…
Артур Ип
1

Я также искал святой Грааль правильного рабочего процесса для создания большого проекта R. В прошлом году я нашел этот пакет под названием rsuite , и, конечно, это было то, что я искал. Этот R-пакет был явно разработан для развертывания больших R-проектов, но я обнаружил, что его можно использовать для R-проектов меньшего, среднего и большого размера. Я дам ссылки на примеры из реальной жизни через минуту (ниже), но сначала я хочу объяснить новую парадигму построения R проектов сrsuite .

Заметка. Я не создатель или разработчик rsuite.

  1. Мы работали над проектами совсем не так с RStudio; цель должна быть не в создании проекта или пакета, а в более широком масштабе. В rsuite вы создаете супер-проект или мастер-проект, который содержит стандартные проекты R и пакеты R во всех возможных комбинациях.

  2. Имея супер-проект R, вам больше не нужен Unix makeдля управления нижними уровнями R-проектов; Вы используете R скрипты наверху. Позволь мне показать тебе. Когда вы создаете мастер-проект rsuite, вы получаете следующую структуру папок:

введите описание изображения здесь

  1. В Rэту папку вы помещаете свои сценарии управления проектами, которые будут заменены make.

  2. Папка packages- это папка, в которой rsuiteхранятся все пакеты, составляющие супер-проект. Вы также можете скопировать и вставить пакет, который недоступен из Интернета, и rsuite также создаст его.

  3. папка deployment, где rsuiteбудет записывать все двоичные файлы пакетов , которые были указаны в пакетах DESCRIPTIONфайлов. Таким образом, это само по себе делает проект полностью воспроизводимым по времени.

  4. rsuiteпоставляется с клиентом для всех операционных систем. Я проверил их все. Но вы также можете установить его как addinдля RStudio.

  5. rsuiteтакже позволяет создавать изолированную condaустановку в своей собственной папке conda. Это не среда, а физическая установка Python, созданная Anaconda на вашем компьютере. Это работает вместе с R SystemRequirements, из которого вы можете установить все пакеты Python, которые вы хотите, с любого канала conda, который вы хотите.

  6. Вы также можете создавать локальные репозитории, чтобы получать пакеты R, когда вы не в сети, или хотите собрать все быстрее.

  7. Если вы хотите, вы также можете собрать проект R в виде zip-файла и поделиться им с коллегами. Он будет работать при условии, что у ваших коллег будет установлена ​​та же версия R.

  8. Другим вариантом является создание контейнера всего проекта в Ubuntu, Debian или CentOS. Таким образом, вместо того, чтобы делиться zip-файлом со сборкой проекта, вы делите весь Dockerконтейнер с вашим проектом, готовым к запуску.

Я много экспериментировал с rsuiteпоиском полной воспроизводимости и избегал зависимости от пакетов, которые устанавливаются в глобальной среде. Это неправильно, потому что как только вы устанавливаете обновление пакета, проект, чаще всего, перестает работать, особенно те пакеты с очень конкретными вызовами функции с определенными параметрами.

Первое, что я начал экспериментировать, было с bookdown электронными книгами. Мне никогда не удавалось получить скидку, чтобы выдержать испытание временем более шести месяцев. Итак, что я сделал, так это конвертировал исходный проект bookdown, чтобы следовать rsuiteструктуре. Теперь мне не нужно беспокоиться об обновлении моей глобальной среды R, потому что у проекта есть собственный набор пакетов в deploymentпапке.

Следующее, что я сделал, было создание проектов машинного обучения, но в rsuiteпути. Главный, оркестрирующий проект наверху, и все подпроекты и пакеты должны находиться под контролем мастера. Это действительно меняет способ кодирования на R, делая вас более продуктивным.

После этого я начал работать в новом пакете под названием rTorch. Это было возможно, в значительной степени, из-заrsuite ; это позволяет вам думать и развиваться.

Один совет, хотя. Обучениеrsuite нелегко. Поскольку он представляет новый способ создания проектов R, он чувствует себя тяжело. Не смущайтесь с первых попыток, продолжайте подниматься по склону, пока не сделаете это. Это требует глубоких знаний вашей операционной системы и вашей файловой системы.

Я ожидаю, что один день RStudioпозволит нам создать такие проекты, какrsuite делается из меню. Это было бы замечательно.

Ссылки:

RSuite GitHUb репо

r4ds bookdown

керас и блестящий учебник

moderndive книга-rsuite

interpretable_ml-rsuite

IntroMachineLearningWithR-rsuite

кларк-intro_ml-rsuite

Гайндман-bookdown-rsuite

statistical_rethinking-rsuite

Fread-тесты-rsuite

DataViz-rsuite

торгово-сегментация-h2o-учебник

телекоммуникационный-клиент-маслобойка-учебник

sclerotinia_rsuite

f0nzie
источник
-7

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

Джон Д. Кук
источник
1
Есть серьезно большие пакеты (то есть программы) там. Вы серьезно предлагаете переписать их на другом языке? Зачем???
Эдуардо Леони
4
Одним из соображений является эффективность. Я часто переписывал код R как код C ++ и сделал его в 100 раз быстрее. Другой инструмент поддержки. R не имеет ничего похожего на IDE, такие как Eclipse или Visual Studio. Наконец, если программа очень большая, то, скорее всего, она выполняет нестатистические задачи, для которых R плохо подходит.
Джон Д. Кук
2
Существует плагин (Stat-ET), который позволяет Eclipse взаимодействовать с R. Я согласен, что C ++ может работать намного быстрее, чем R. Но сколько времени вам нужно, чтобы перекодировать R в C ++? Если вы не можете многократно использовать код, преимущество более быстрого кода не стоит большого по сравнению с усилиями по его перекодированию в C ++.
Тьерри
2
Да, есть компромисс (производительность против производительности). И для чисто анализа данных / статистической работы R часто побеждает. Но для написания других задач, например, GUI, веб и т. Д., Я не уверен, что это так. Мы часто создаем прототипы и работаем в R, но разворачиваем производственный код на Python / C ++. С последним вы получаете производительность и очень зрелые и многократно используемые библиотеки / фреймворки для различных задач. Но это нестабильная ситуация, и экосистема R постоянно развивается.
АРС
Использование Rcppпакета, включая код C ++ в программах на R, становится довольно простым. Таким образом, переписывание определенных частей кода R может быть легко интегрировано в R. Кроме того, с появлением RStudio была введена среда IDE для R, хотя, возможно, она еще и не такая мощная, как Visual Studio.
Пол Химстра