Подтвердить что ты не робот

Практика кодирования в R: каковы преимущества и недостатки разных стилей?

Недавние вопросы, касающиеся использования require versus:: подняли вопрос о том, какие стили программирования используются при программировании в R, и каковы их преимущества/недостатки. Просматривая исходный код или просматривая в сети, вы видите много разных стилей.

Основные тенденции в моем коде:

  • Я много играю с индексами (и вложенными индексами), что иногда приводит к довольно неясному коду, но, как правило, намного быстрее, чем другие решения. например: x[x < 5] <- 0 вместо x <- ifelse(x < 5, x, 0)

  • Я стараюсь использовать функции, чтобы не перегружать память временными объектами, которые мне нужно очистить. Особенно с функциями, управляющими большими наборами данных, это может быть реальной нагрузкой. например: y <- cbind(x,as.numeric(factor(x))) вместо y <- as.numeric(factor(x)) ; z <- cbind(x,y)

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

  • Я избегаю циклов любой ценой, так как я считаю, что векторизация будет намного чище (и быстрее)

Тем не менее, я заметил, что мнения по этому поводу различаются, и некоторые люди склонны отступать от того, что они назовут моим "Perl" способом программирования (или даже "Lisp", причем все эти скобки, мой код. Я бы не пошел так далеко, хотя).

Что вы считаете хорошей практикой кодирования в R?

Каков ваш стиль программирования и как вы видите его преимущества и недостатки?

4b9b3361

Ответ 1

То, что я делаю, будет зависеть от того, почему я пишу код. Если я пишу анализ данных script для моего исследования (дневная работа), я хочу что-то, что работает, но это читаемый и понятный месяц или даже годы спустя. Мне неважно, как много времени вычислять. Векторизация с помощью lapply et al. может привести к обфускации, чего я бы хотел избежать.

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

Для ваших других пуль; Я бы склонен не к inline/nest вызовам, если они не были очень тривиальными. Если я четко изложу шаги, я считаю, что код легче читать и, следовательно, с меньшей вероятностью содержать ошибки.

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

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

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

Ответ 2

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

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

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

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

Я пытаюсь написать свой код, чтобы я мог его понять. Это означает, что я много комментирую и создаю куски кода, чтобы они как-то следовали идее того, чего я пытаюсь достичь. Я часто перезаписываю объекты по мере продвижения функции. Я думаю, что это обеспечивает прозрачность задачи, особенно если вы ссылаетесь на эти объекты позже в функции. Я думаю о скорости, когда вычислительное время превышает мое терпение. Если функция так долго заканчивается, что я начинаю просматривать SO, я вижу, могу ли я ее улучшить.

Я узнал, что хороший редактор синтаксиса с разворачиванием кода и раскраской синтаксиса (я использую Eclipse + StatET) сэкономил много головных болей.

Основываясь на сообщении VitoshKa, я добавляю, что я использую capitalizedWords (sensu Java) для имен функций и fullstop.delimited для переменных. Я вижу, что у меня может быть другой стиль для аргументов функции.

Ответ 3

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

  • camelCase для глобальных функций и объектов (например, doSomething, getXyyy, upperLimit)
  • функции начинаются с глагола
  • не экспортируемые и вспомогательные функции всегда начинаются с "."
  • локальные переменные и функции выполняются маленькими буквами и в синтаксисе "_" (do_something, get_xyyy). Это позволяет легко различать локальные и глобальные и, следовательно, приводит к более чистым кодам.

Ответ 4

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

require(RMySQL)
# selection of variables alongside conditions in SQL is really transparent
# even if conditional variables are not part of the selection
statement = "SELECT id,v1,v2,v3,v4,v5 FROM mytable
             WHERE this=5
             AND that != 6" 
mydf <- dbGetQuery(con,statement)
# some simple things get really tricky (at least in MySQL), but simple in R
# standard deviation of table rows
dframe$rowsd <- sd(t(dframe))

Поэтому я считаю это хорошей практикой и действительно рекомендую использовать базу данных SQL для ваших данных для большинства случаев использования. Я также изучаю TSdbi и экономя временные ряды в реляционной базе данных, но пока не могу судить об этом.