Команда Python for Devs подготовила перевод статьи Клауса Вилке о том, почему Python, несмотря на статус языка №1 в data science, вовсе не идеален для анализа данных. Автор показывает на реальных примерах из лабораторной практики, что многие операции в Python оказываются куда более громоздкими, чем в R, — и это не вина программистов, а архитектурные особенности инструментов.


Возможно, Python неплохой язык для data science, но точно не отличный.

Да, я готов разворошить осиное гнездо! Свистать всех в комментарии!

Но на самом деле первое, что я хочу сказать, — вот что: используйте инструмент, который вам знаком. Если это Python — прекрасно, используйте его. И ещё: используйте лучший инструмент для конкретной задачи. Если это Python — отлично, используйте его. И ещё: вполне нормально использовать инструмент для какой-то одной задачи только потому, что вы и так применяете его для множества других и он всегда под рукой. Если вы целый день забиваете гвозди, ничего страшного, если вы тем же молотком открываете бутылку пива или почесываете спину. Так же и с Python: если вы весь день программируете на Python, вполне нормально использовать его и для подбора смешанных линейных моделей. Если вам удобно — замечательно! Продолжайте. Но если вы спотыкаетесь на ровном месте, если всё кажется сложнее, чем должно быть, — эта серия статей может быть для вас.

Фото: Zach Graves / Unsplash
Фото: Zach Graves / Unsplash

Мне кажется, роль Python как языка для data science сильно переоценивают. У него есть ограничения, которые, на мой взгляд, нельзя игнорировать. Есть много задач data science, которые я с куда большим удовольствием делаю в R, а не в Python.1 Я считаю, что популярность Python в data science — это историческая случайность, усиленная тем, что он «вроде как неплох почти во всём», а не следствие его особой пригодности к работе с данными.

При этом Python, на мой взгляд, очень хорош ��ля deep learning.2 Не зря PyTorch стал индустриальным стандартом. Когда я здесь говорю о data science, я специально исключаю deep learning. Речь про всё остальное: подготовку данных, разведочный анализ, визуализацию, статистическое моделирование и т. д. И, как я сказал в начале, я прекрасно понимаю, что если вы по веской причине весь день работаете в Python (например, обучаете AI-модели), то вам может захотеться делать в Python и всё остальное. Сам я поступаю так же, когда веду курсы по deep learning. Но это не отменяет того, что работа с данными в мире Python нередко оказывается мучительно громоздкой.

Наблюдения с передовой

Начну с личного опыта, без попыток объяснить, что именно его вызывает. Я руковожу исследовательской лабораторией в области вычислительной биологии уже больше двадцати лет. За это время я работал примерно с тридцатью аспирантами и постдоками — все они очень сильные специалисты по вычислительным методам. В моей лаборатории действует простой принцип: каждый волен пользоваться любым языком программирования и любыми инструментами. Я никому не указываю. И чаще всего люди выбирают Python как основной рабочий язык.

И вот типичная ситуация, которая повторяется снова и снова со студентами, использующими Python. Студент приходит ко мне в кабинет и показывает какой-то результат. Я говорю: «Отлично, а можешь быстро построить график немного по-другому?» или «Можешь быстро посчитать вот эту величину, которую я только что придумал, и показать, как она выглядит на графике?» или что-то в этом роде. Обычно я прошу о вещах, которые сам мог бы сделать в R за несколько минут. Примеры: преобразовать boxplot в violin plot ил�� наоборот, превратить линейный график в тепловую карту, построить оценку плотности вместо гистограммы, выполнить вычисление не по исходным данным, а по ранжированным значениям и так далее. И неизменно студенты, работающие в Python, отвечают: «Это займёт какое-то время. Я вернусь к себе, разберусь и приду обратно». Чтобы не было недопонимания: это отличные студенты. Проблема не в том, что они плохо знают инструменты. Мне действительно кажется, что дело в самих инструментах. Они оказываются настолько громоздкими или запутанными, что даже элементарные просьбы часто перестают быть элементарными.3

Как бы ни объяснять этот феномен, я вынужден заключить: в том, как в Python устроена работа с данными, есть что-то фундаментально сломанное. Возможно, это особенности самого языка; возможно — ограничения библиотек; скорее всего — сочетание того и другого. Но как бы то ни было, последствия вполне реальные, и я сталкиваюсь с ними постоянно. Приведу ещё один пример, если вам вдруг захочется возразить: «Это проблема навыков — нужно брать студентов посильнее». Прошлой осенью я вёл курс по AI-моделям для биологии совместно с опытным дата-сайентистом, который делает всю свою работу в Python. Он знает NumPy, pandas и matplotlib как свои пять пальцев. На занятиях я читал теорию, а он разбирал практические упражнения — в Python. Так что я получил возможность наблюдать, как эксперт проходит через разнообразные примеры. И очень часто, глядя на код, я думал: «Зачем всё так усложнено?» Слишком часто то, что в R заняло бы несколько простых строк, в Python оказывалось куда более длинным и путаным. Я бы точно не смог написать такой код без основательного изучения и полной перенастройки мышления ��од необходимые шаблоны. Всё выглядело очень чуждым — но не в смысле «необычно, но изящно», а в смысле «необычно, странно и неудобно». И снова: дело не в том, что мой коллега недостаточно силён. Он исключительно хорош в своём деле. Похоже, проблема в архитектурных основах инструментов.

Некоторые общие мысли о том, каким должен быть хороший язык для data science

Давайте немного отступим и рассмотрим базовые критерии выбора языка для data science. Под data science я понимаю разбор и суммирование данных, поиск закономерностей, построение моделей и визуализаций. Короче говоря, то, чем занимаются учёные и другие исследователи4, когда анализируют данные. Эта деятельность отличается от data engineering’а или разработки приложений, даже если приложение обрабатывает большой объём данных.

Data science в моём определении предполагает много интерактивного исследования данных и быстрых разовых анализов или экспериментов. Поэтому язык, подходящий для data science, должен быть интерпретируемым, работать в интерактивной консоли или в формате ноутбуков. Это также означает, что производительность — вторична. Когда вы хотите быстро прогнать линейную регрессию на каком-то наборе данных, вам совершенно не важно, займёт задача 50 миллисекунд или 500 миллисекунд. Вам важно, сможете ли вы просто открыть оболочку, набрать несколько строчек кода и получить результат за минуту-другую, а не настраивать новый проект, писать тонны шаблонного кода ради компилятора и тратить больше времени на компиляцию, чем на выполнение.

Если принять, что возможность работать интерактивно и с минимальными накладными расходами — критически важное свойство языка для data science, то мы сразу приходим к скриптовым языкам вроде Python или специализированным языкам для анализа данных, таким как R, Matlab или Mathematica. Есть ещё Julia, но, честно говоря, я знаю о ней недостаточно, чтобы писать связно. Возможно, это лучший язык для data science из всех. Но я замечаю, что даже у людей, которые много на ней работали, есть сомнения. Так или иначе, обсуждать её я здесь не буду. Я также не рассматриваю проприетарные языки вроде Matlab или Mathematica и малоизвестные решения без развитой экосистемы пакетов, такие как Octave. В итоге остаются два реалистичных варианта — R и Python.5

Прежде чем идти дальше, скажу ещё пару слов о производительности. Производительность почти всегда конфликтует с другими свойствами языка. Если упростить, высокую производительность приходится оплачивать либо дополнительными усилиями программиста (как в Rust), либо повышенным риском странных, трудноуловимых багов (как в C), либо и тем и другим. Для задач data science высокий риск скрытых ошибок или некорректных результатов, на мой взгляд, недопустим, а удобство для программиста важнее сырой скорости. Компьютеры быстрые, а думать больно. Я лучше затрачу меньше умственной энергии на то, чтобы объяснить компьютеру, что нужн�� сделать, и подожду подольше результата. Чем легче язык делает мою работу, тем лучше. Если в каком-то анализе я действительно упираюсь в производительность, я всегда могу переписать конкретный участок на Rust — когда уже точно понимаю, что делаю и какие вычисления нужны.

Разделение логики и рутины

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

Для наглядности возьмём датасет с пингвинами из архипелага Палмер. В нём есть три вида пингвинов, и живут они на трёх разных островах. Допустим, я хочу вычислить ср��днее и стандартное отклонение массы тела для каждой комбинации вида и острова, исключив случаи, где масса пингвина неизвестна. Идеальный язык для data science позволил бы выразить это именно в таких терминах и потребовал бы примерно столько же кода, сколько мне понадобилось для этого предложения по-английски. И это действительно возможно — и в R, и в Python.

Вот соответствующий код на R в стиле tidyverse:

library(tidyverse)
library(palmerpenguins)

penguins |>
  filter(!is.na(body_mass_g)) |>
  group_by(species, island) |>
  summarize(
    body_weight_mean = mean(body_mass_g),
    body_weight_sd = sd(body_mass_g)
  )

А вот эквивалент на Python с использованием pandas:

import pandas as pd
from palmerpenguins import load_penguins

penguins = load_penguins()

(penguins
 .dropna(subset=['body_mass_g'])
 .groupby(['species', 'island'])
 .agg(
     body_weight_mean=('body_mass_g', 'mean'),
     body_weight_sd=('body_mass_g', 'std')
 )
 .reset_index()
)

Эти два примера очень похожи. При такой сложности анализа Python вполне справляется. Я бы сказал, что код на R немного легче читать (обратите внимание, сколько кавычек и скобок нужно Python), но разница невелика. В обоих случаях мы берём датасет, убираем пингвинов с неизвестной массой, указываем, что хотим делать вычисления по всем сочетаниям вида и острова, и затем считаем средние и стандартные отклонения.

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

from palmerpenguins import load_penguins
import math

penguins = load_penguins()

# Convert DataFrame to list of dictionaries
penguins_list = penguins.to_dict('records')

# Filter out rows where body_mass_g is missing
filtered = [row for row in penguins_list if not math.isnan(row['body_mass_g'])]

# Group by species and island
groups = {}
for row in filtered:
    key = (row['species'], row['island'])
    if key not in groups:
        groups[key] = []
    groups[key].append(row['body_mass_g'])

# Calculate mean and standard deviation for each group
results = []
for (species, island), values in groups.items():
    n = len(values)
    
    # Calculate mean
    mean = sum(values) / n
    
    # Calculate standard deviation
    variance = sum((x - mean) ** 2 for x in values) / (n - 1)
    std_dev = math.sqrt(variance)
    
    results.append({
        'species': species,
        'island': island,
        'body_weight_mean': mean,
        'body_weight_sd': std_dev
    })

# Sort results to match order used by pandas
results.sort(key=lambda x: (x['species'], x['island']))

# Print results
for result in results:
    print(f"{result['species']:10} {result['island']:10} "
          f"Mean: {result['body_weight_mean']:7.2f} g, "
          f"SD: {result['body_weight_sd']:6.2f} g")

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

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

Русскоязычное сообщество про Python

Друзья! Эту статью подготовила команда Python for Devs — канала, где каждый день выходят самые свежие и полезные материалы о Python и его экосистеме. Подписывайтесь, чтобы ничего не пропустить!

Комментарии (0)