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

К счастью, у меня была возможность поработать со множеством инженеров, которые научили меня и объяснили, почему код должен быть чистым и эффективным - если я могу резюмировать эту потребность в одном предложении, лучшее из них взято из стихотворения Джона Донна ( говорят, что ему почти 400 лет!): «Ни один человек - не остров».

Когда дело доходит до разработки нашего кода и скриптов, мы не остров. А совместная работа - один из важнейших навыков, которыми нужно обладать при работе в качестве специалиста по данным, аналитика (или почти в любой другой профессии) - если вы хотите сделать карьеру на анализе данных, вероятность того, что кому-то придется взглянуть на ваш код. в будущем вероятно 99,99%. Чем лучше организован ваш код, тем проще будет для кого-то искать, отлаживать и улучшать его в будущем. И это касается не только других людей, с которыми вам, возможно, придется работать, это также избавит вас от многих хлопот (кто никогда не смотрел на свой собственный код и не думал: «Что, черт возьми, я делаю в этом функция? ”)

Итак, давайте сразу перейдем к некоторым распространенным передовым методам программирования на R (некоторые оспариваются, другие широко принимаются сообществом)!

Библиотеки идут первыми

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

Большинство руководств по стилю согласны с этой рекомендацией, и вам не следует делать что-то вроде:

my_vector <- c(1,2,3)
library(readxl)
read_excel(path_file)

Выше создается вектор my_vector перед импортом библиотеки readxl -, как правило, это плохая практика.

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

На втором месте - жестко запрограммированные переменные

Другое общепринятое правило - жестко запрограммированные переменные, такие как пути или файлы конфигурации для доступа к базам данных (если у вас есть пароли, еще лучше взглянуть на эту статью, чтобы управлять секретами: https://cran.r-project.org /web/packages/httr/vignettes/secrets.html ) в начале скрипта после импорта библиотек.

Многие сценарии используют данные из файлов CSV или XLSX, поэтому рекомендуется выполнять следующие действия:

library(readxl)
path_file <- "data/data.csv"
my_df <- read_excel(path_file)

Таким образом, всякий, кто читает ваш код, получает о нем две важные части информации:

  • От того, какие библиотеки вы кодируете, зависит.
  • Какие файлы и папки должны содержать исходная структура папок.

Да, и насчет путей к файлам ..

Относительные пути по абсолютным путям

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

Абсолютные пути имеют следующий вид:

"C:\Users\ivopb\My Documents\R Project\data\data.csv"

Если я передам свой код кому-то, кому нужно запустить его на своем компьютере, если у него нет имени пользователя ivopb, он никогда не сможет запустить код там, где мы его используем. этот файл. Даже если они запускают его в одной и той же структуре папок (Мои документы / R Project / и т. Д.).

Да, и даже если случайно они являются пользователем ivopb,, но на их жестком диске отображается другая буква, отличная от C :, удачи в выполнении кода!

Как правило, всегда предпочтительны относительные пути:

"data\data.csv"

Это заставит вас установить рабочий каталог в папку, над которой вы работаете, или открыть скрипт из папки - что на самом деле намного лучше, чем отладка и изменение огромного количества путей, которые могут быть в вашем скрипте!

Соглашения об именах - Имена файлов

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

# Good example
my_file.R
# Bad example
My File.R

Также старайтесь использовать имена в нижнем регистре в именах скриптов. Если ваш сценарий преследует цель, например, создать агрегацию некоторых конкретных данных в файле csv, используйте имя, привязанное к общей цели сценария:

aggregating_data.R

Соглашения об именах - объекты и функции

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

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

Мне нравится использовать футляр для змей (используя _) для объектов и футляр для верблюда или змейки для функций, но это открыто для обсуждения. Пример:

# Good
my_vector <- c(1,2,3)
# Bad
myvector <- c(1,2,3)
# Good
ThisFunction()
this_function()
# Bad
thisfunction() 

Кроме того, имена ваших объектов и функций должны быть как можно более явными и краткими, представляя функцию, которая принимает элемент и вычисляет степень числа:

ComputePowerOfBaseWithExponent <- function (base, exponent) {
  return (base**exponent)
}

Имя функции очень длинное, поэтому мы можем сократить его, и обычно рекомендуется это сделать:

ComputePower <- function (base, exponent) {
  return (base**exponent)
}

Повторюсь, мое единственное золотое правило - придерживаться единого стиля во всем сценарии.

Возврат

Это, наряду с именами переменных и функций, является одной из самых спорных тем внутри сообщества (взгляните на эту ветку, чтобы проверить обе стороны аргумента - https://stackoverflow.com/questions/11738823/explicitly-calling -Возврат-в-функции-или-нет )

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

ComputePower <- function (base, exponent) {
  return (base**exponent)
}

Оператор return можно удалить из функции, изменив с явного возврата на неявный возврат:

ComputePower <- function (base, exponent) {
  base**exponent
}

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

Я склонен думать, что явные возвраты проще для всех уровней кодировщиков R, поскольку явные возвраты облегчают новичку понимание потока кода. Но в основном это выбор программиста - люди склонны сидеть по обе стороны забора, когда доходит до этого, и мое личное мнение таково, что я не считаю справедливым ругать того, кто использует неявные или явные результаты.

Будьте откровенны в циклах

Одна из важных вещей при выполнении циклов для объектов - это явное наименование элементов, для которых выполняется цикл.

Представьте себе следующее упражнение: у вас есть вектор с возрастом определенной группы людей, которую вы хотите классифицировать как «Major» или «Minor», и вы используете для этого цикл for (давайте проигнорируем другие подходы, которые мы могли бы использовать, такие как лучшие реализации ради аргумента):

ages_people = c(10, 20, 20, 30, 40)
ClassifyAge <- function (ages) {
  age_class <- c()
  for (age in ages) {
    if (age < 18) {
      age_class <- c(age_class, 'Minor')
    }
    else {
      age_class <- c(age_class, 'Major')
    }
  }
  age_class
}

Следующий код будет работать, если мы вызовем age в цикле for как i или элемент:

ages_people = c(10, 20, 20, 30, 40)
ClassifyAge <- function (ages) {
  age_class <- c()
  for (i in ages) {
    if (i < 18) {
      age_class <- c(age_class, 'Minor')
    }
    else {
      age_class <- c(age_class, 'Major')
    }
  }
  age_class
}

Как правило, лучше явно называть элементы цикла - в приведенном выше случае мы делаем что-то на основе возраста, поэтому было бы лучше назвать элемент цикла age, а не i, element или j. Это облегчило бы понимание того, что код делает на функциональном уровне, для тех, кто не знаком с кодом.

Использование ‹- или = при назначении объекта

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

Одним из общепринятых правил является то, что когда вы используете оператор ‹для присваивания, вы оставляете пробелы между назначаемым объектом и именем переменной, как показано ниже:

# Good example
my_vector <- c(1, 2, 3)
# Bad example
my_vector<-c(1, 2, 3)

Конечно, поскольку я много переплетаю между скриптами Python и R, иногда непослушный = делается на задании :-)

Длина линии

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

Представьте некоторую функцию, которая вызывается с очень длинными аргументами:

CalculatesMeaningOfLife('This is a really long argument','This is another really long argument','This is a third really long argument!') 

При вызове этого кода рекомендуется делать следующее:

CalculatesMeaningOfLife(
  'This is a really long argument',
  'This is another really long argument',
  'This is a third really long argument!'
)

В R Studio есть автоматический отступ, когда вы нажимаете Enter после запятых. Это изящная функция, которая упрощает поддержание порядка и чистоты нашего кода.

Интервал

При вызове функций или индексировании объектов рекомендуется всегда ставить пробел после каждого ‘,’. Это делает код более читаемым и позволяет избежать идеи «сжатого кода». Сжатый код - это когда у нас есть следующее:

my_array = array(1:10,c(2,5))
my_array[,c(1,2)]

Код работает, но весь ваш код связан без пробелов. Визуально это затрудняет понимание того, какой бит относится к первому или второму измерению в массиве.

Более чистый способ сделать это - сделать следующее:

my_array = array(1:10, c(2, 5))
my_array[, c(1, 2)]

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

СУХОЙ - Не повторяйся

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

В качестве действительно простого примера представим, что мы хотим поприветствовать 8 студентов в классе, используя R:

paste('Hello','John')
paste('Hello','Susan')
paste('Hello','Matt')
paste('Hello','Anne')
paste('Hello','Joe')
paste('Hello','Tyson')
paste('Hello','Julia')
paste('Hello','Cathy')

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

GreetStudent <- function(name) {
 paste(‘Hello’,name)
}
class_names <- c(‘John’, ‘Susan’, ‘Matt’ ,’Anne’,
              ‘Joe’, ‘Tyson’, ‘Julia’, ‘Cathy’)
for (student in class_names){
 print(GreetStudent(student))
}

Замечательно то, что теперь вы можете добавить больше учеников в наш вектор class_names и избежать повторения команды вставки несколько раз!

Функции действительно являются одной из самых мощных концепций в R. На них следует полагаться несколько раз, поскольку они сделают наш код эффективным и тестируемым.

И это все! Есть что еще добавить?

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

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

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

Спасибо, что нашли время прочитать этот пост! Не стесняйтесь добавлять меня в LinkedIn (https://www.linkedin.com/in/ivobernardo/) и проверять веб-сайт моей компании (https://daredata.engineering/home).

Если вы заинтересованы в обучении по аналитике и науке о данных, вы также можете посетить мою страницу на Udemy (https://www.udemy.com/user/ivo-bernardo/)

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