Специалисты по обработке данных: Ваши имена переменных ужасны. Вот как их исправить.

Простой способ значительно улучшить качество кода

Быстро, что делает следующий код?

for i in range(n):
    for j in range(m):
        for k in range(l): 
            temp_value = X[i][j][k] * 12.5
            new_array[i][j][k] = temp_value + 150

Невозможно сказать, верно? Если бы вы пытались изменить или отладить этот код, вы были бы в растерянности, если бы не смогли прочитать мысли автора. Даже если бы вы были автором, через несколько дней после написания этого кода вы бы не узнали, что он делает, из-за бесполезных имен переменных и использования « магических чисел ».

Работая с кодом для науки о данных, я часто вижу примеры, подобные приведенным выше (или хуже): код с именами переменных, такими как X, y, xs, x1, x2, tp, tn, clf, reg, xi, yi, ii, и множеством безымянных значений констант. Откровенно говоря, специалисты по обработке данных (в том числе и я) ужасно плохо именуют переменные, когда мы вообще пытаемся дать им имена.

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

  • Бесполезные / запутанные / расплывчатые имена переменных
  • Без названия «магические» постоянные числа

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

Примечание. Я сосредотачиваюсь на Python, поскольку это, безусловно, наиболее широко используемый язык в отраслевых науках о данных. На Python (подробнее см. Здесь ):

  • Имена переменных / функций lower_caseиseparated_with_underscores
  • Именованные константы находятся в ALL_CAPITAL_LETTERS
  • Занятия в CamelCase

Именование переменных

При именовании переменных следует помнить о трех основных идеях:

  1. Имя переменной должно описывать информацию, представленную переменной. Имя переменной должно прямо словами сообщать вам, что обозначает переменная.
  2. Ваш код будет прочитан больше раз, чем написан. Сделайте ставку на то, насколько легко понять ваш код, а не на скорость его написания.
  3. Примите стандартные соглашения об именах, чтобы вы могли принять одно глобальное решение вместо нескольких локальных.

Как это выглядит на практике? Давайте рассмотрим некоторые улучшения имен переменных:

  • X и y. Если вы видели их несколько сотен раз, то знаете, что это функции и цели, но это может быть неочевидно для других разработчиков, читающих ваш код. Вместо этого используйте имена, описывающие, что представляют собой эти переменные, например house_features и house_prices.
  • value. Что представляет собой ценность? Это может быть velocity_mph, customers_served, efficiency, revenue_total. Такое имя, как value, ничего не говорит о назначении переменной, и его легко спутать.
  • temp. Даже если вы используете переменную только как временное хранилище значений, все равно дайте ей осмысленное имя. Возможно, это значение, в котором вам нужно преобразовать единицы, поэтому в этом случае сделайте его явным:
# Don't do this
temp = get_house_price_in_usd(house_sqft, house_room_count)
final_value = temp * usd_to_aud_conversion_rate
# Do this instead
house_price_in_usd = get_house_price_in_usd(house_sqft, 
                                            house_room_count)
house_price_in_aud = house_price_in_usd * usd_to_aud_conversion_rate
  • Если вы используете такие сокращения, как usd, aud, mph, kwh, sqft, убедитесь, что вы установили их заранее. Согласуйте с остальной частью вашей команды общие сокращения и запишите их. Затем, при проверке кода, убедитесь, что эти письменные стандарты соблюдены.
  • tp, tn, fp, fn: избегайте сокращений, связанных с машинным обучением. Эти значения представляют true_positives, true_negatives, false_positives и false_negatives, поэтому сделайте это явным. Более короткие имена переменных не только трудны для понимания, но и могут быть введены с ошибками. Слишком просто использовать tp, когда вы имели в виду tn, поэтому напишите описание полностью.
  • Выше приведен пример приоритета легкости чтения кода, а не того, насколько быстро вы можете его написать. Чтение, понимание, тестирование, изменение и отладка плохо написанного кода занимает гораздо больше времени, чем хорошо написанный код. В целом, пытаясь писать код быстрее - используя более короткие имена переменных - вы фактически увеличиваете время разработки вашей программы! Если вы мне не верите, вернитесь к коду, который вы написали 6 месяцев назад, и попробуйте его изменить. Если вы обнаружите, что пытаетесь расшифровать свой код, это признак того, что вам следует сосредоточиться на лучших соглашениях об именах.
  • xs и ys. Они часто используются для построения графиков, и в этом случае значения представляют x_coordinates и y_coordinates. Однако я видел, как эти имена используются для многих других задач, поэтому избегайте путаницы, используя конкретные имена, описывающие назначение переменных, например times и distances или temperatures и energy_in_kwh.

Что вызывает неправильные имена переменных?

Большинство проблем с именованием переменных проистекают из

  • Желание, чтобы имена переменных были короткими
  • Прямой перевод формул в код

По первому пункту, в то время как языки вроде Фортрана действительно ограничивают длину имен переменных (до 6 символов), современные языки программирования не имеют ограничений, поэтому не чувствуйте себя обязанным использовать надуманные сокращения. Не используйте слишком длинные имена переменных, но если вам нужно отдать предпочтение одной стороне, стремитесь к удобочитаемости.

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

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

temp = m1 * x1 + m2 * (x2 ** 2)
final = temp + b

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

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

house_price = price_per_room * rooms + \
              price_per_floor_squared * (floors ** 2)
house_price = house_price + expected_mean_house_price

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

Прочие соображения

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

Агрегации в именах переменных

Итак, вы освоили основную идею использования описательных имен, заменяя xs на distances, e на efficiency и v на velocity. Что же произойдет, если взять среднее значение скорости? Это должно быть average_velocity, velocity_mean или velocity_average? Два шага могут решить эту проблему:

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

Следуя этим правилам, ваш набор агрегированных переменных может быть velocity_avg, distance_avg, velocity_min и distance_max. Правило 2 - это своего рода личный выбор, и если вы не согласны, это нормально, если вы последовательно применяете то правило, которое выберете.

Сложный момент возникает, когда у вас есть переменная, представляющая номер элемента. У вас может возникнуть соблазн использовать building_num, но относится ли это к общему количеству зданий или к конкретному индексу конкретного здания? Во избежание двусмысленности используйте building_count для обозначения общего количества зданий и building_index для обозначения конкретного здания. Вы можете адаптировать это к другим задачам, таким как item_count и item_index. Если вам не нравится count, то item_total также является лучшим выбором, чем num. Такой подход устраняет двусмысленность и поддерживает единообразие размещения агрегатов в конце имен.

Индексы цикла

По какой-то досадной причине типичные переменные цикла стали i, j и k. Это может быть причиной большего количества ошибок и разочарований, чем любая другая практика в области науки о данных. Объедините неинформативные имена переменных с вложенными циклами (я видел вложенные циклы, включающие использование ii, jj и даже iii), и вы получите идеальный рецепт для нечитаемого, подверженного ошибкам кода. Это может быть спорным, но я никогда не использую i или любую другую букву для переменных цикла, вместо этого я предпочитаю описывать то, что я повторяю, например

for building_index in range(building_count):
   ....

or

for row_index in range(row_count):
    for column_index in range(column_count):
        ....

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

(В Python, если вы не используете переменную цикла, используйте _ в качестве заполнителя. Таким образом вы не запутаетесь, используется ли индекс.)

Больше имен, которых следует избегать

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

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

Никогда не используйте магические числа

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

final_value = unconverted_value * 1.61
final_quantity = quantity / 60
value_with_offset = value + 150

(Эти имена переменных плохие!)

Магические числа - большой источник ошибок и путаницы, потому что:

  • Только один человек, автор, знает, что они представляют
  • Для изменения значения необходимо найти все места, где оно используется, и вручную ввести новое значение.

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

def convert_usd_to_aud(price_in_usd,            
                       aud_to_usd_conversion_rate):
    price_in_aus = price_in_usd * usd_to_aud_conversion_rate

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

USD_TO_AUD_CONVERSION_RATE = 1.61
price_in_aud = price_in_usd * USD_TO_AUD_CONVERSION_RATE

(Прежде чем мы начнем проект, мы должны установить с остальной частью нашей команды, что usd = доллары США и aud = австралийские доллары. Помните стандарты!

Вот еще один пример:

# Conversion function approach
def get_revolution_count(minutes_elapsed,                       
                         revolutions_per_minute):
    revolution_count = minutes_elapsed * revolutions_per_minute

# Named constant approach
REVOLUTIONS_PER_MINUTE = 60
revolution_count = minutes_elapsed * REVOLUTIONS_PER_MINUTE

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

В качестве реального примера опасностей, связанных с магическими числами, в колледже я работал над исследовательским проектом с данными об энергии зданий, которые первоначально поступали с 15-минутными интервалами. Никто не задумывался о том, что это может измениться, и мы написали сотни функций с магическим числом 15 (или 96 для количества ежедневных наблюдений). Это работало нормально, пока мы не начали получать данные с интервалами в 5 и 1 минуту. Мы потратили недели на изменение всех наших функций, чтобы принять параметр для интервала, но даже в этом случае мы месяцами боролись с ошибками, вызванными использованием магических чисел.

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

Важность стандартов и соглашений

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

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

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

Выводы

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

for i in range(n):
    for j in range(m):
        for k in range(l): 
            temp_value = X[i][j][k] * 12.5
            new_array[i][j][k] = temp_value + 150

и исправить это. Мы будем использовать описательные имена переменных и именованные константы.

Теперь мы видим, что этот код нормализует значения пикселей в массиве и добавляет постоянное смещение для создания нового массива (игнорируйте неэффективность реализации!). Когда мы передадим этот код нашим коллегам, они смогут его понять и изменить. Более того, когда мы вернемся к коду, чтобы протестировать его и исправить наши ошибки, мы будем точно знать, что делаем.

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

В этой статье мы рассмотрели некоторые способы улучшения имен переменных.

Общие моменты, которые следует запомнить

  1. Имя переменной должно описывать сущность, которую представляет переменная.
  2. Сделайте ставку на то, насколько легко ваш код понять, а не на то, как быстро вы можете написать код.
  3. Используйте единые стандарты на протяжении всего проекта, чтобы минимизировать когнитивную нагрузку, связанную с принятием небольших решений.

Конкретные точки

  • Используйте описательные имена переменных
  • Используйте параметры функции или именованные константы вместо «магических» чисел.
  • Не используйте сокращения, связанные с машинным обучением.
  • Опишите, что представляет собой уравнение или модель, с именами переменных.
  • Поместите агрегаты в конце имен переменных
  • Используйте item_count вместо num
  • Используйте описательные индексы цикла вместо i, j, k.
  • Принять соглашения об именах и форматировании в рамках проекта

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

Есть много других изменений, которые мы можем внести в наш код науки о данных, чтобы довести его до производственного уровня (мы даже не говорили об именах функций!). Вскоре у меня появятся другие статьи по этой теме, а пока ознакомьтесь с Примечаниями по созданию программного обеспечения из Code Complete ». Чтобы глубже погрузиться в передовые методы разработки программного обеспечения, возьмите Code Complete Стива МакКоннелла.

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

Я пишу о науке о данных и приветствую конструктивные комментарии или отзывы. Со мной можно связаться в Twitter @koehrsen_will. Если вам нравится помогать миру и одновременно увеличивать прибыль, то ознакомьтесь с вакансиями в Cortex Building Intelligence. Мы помогаем некоторым из крупнейших офисных зданий в мире сэкономить сотни тысяч долларов на расходах на электроэнергию при одновременном снижении выбросов углекислого газа.