Команда 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 и его экосистеме. Подписывайтесь, чтобы ничего не пропустить!

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


  1. izibrizi2
    30.11.2025 11:10

    Питон в целом лучше для ml, cv, nlp, big data задач ну и кучи всего остального, прикладного.

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


  1. economist75
    30.11.2025 11:10

    Для контраста можно на R что-нить тоже несвойственное выложить - телеграм-бота или тетрис. И вот тогда окажется что все "доводы - контрасты" - так себе. И R, и Pandas очень близки и делают одно и то же, одинаково бесплатно. Но один падает, другой растет.


  1. dyanishev
    30.11.2025 11:10

    Пока как-то ни разу не убедительно. Может, дальнейшие примеры реально что-то покажут, но пока ни одного такого примера, где в питоне было бы прям криво, а где-то в другом языке прям хорошо, я не увидел.


  1. ivchatov309
    30.11.2025 11:10

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

    Про плюсы python уже пару человек тут высказали главное.


    1. RaptorTV
      30.11.2025 11:10

      Так какой язык вы веберете?


      1. ivchatov309
        30.11.2025 11:10

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

        Жену учить - попробую оба варианта, что ей будет проще воспринимать.


  1. kmatveev
    30.11.2025 11:10

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

    Примеры на R+tidyverse и Python+pandas не особо отличаются визуально, и надо понимать, что отсутствие кавычек и скобочек в R работает благодаря такой фишке как метапрограммирование (напоминает lisp-макросы). Вам повезло, если у вас что-то простое и оно работает, а вот если надо разобраться, почему оно не работает - удачи, я не осилил, проще переписать совсем другим способом.

    Мне кажется, хорошими языками для data science были бы Scheme или OCaml.


  1. JetRonin
    30.11.2025 11:10

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


    1. economist75
      30.11.2025 11:10

      Если сравнивать R и Pandas - то почти одно и то же. Оба инструмента прекрасны, state-of-the-art.


  1. Nuflyn
    30.11.2025 11:10

    В нашем институте биологи тоже используют R.) Интересно, это просто так совпало или просто биологи его любят с давних пор?


  1. Arkronus
    30.11.2025 11:10

    Можно взять другую библиотеку (polars) и разница с R будет минимальна и читаема.

    import polars as pl
    from palmerpenguins import load_penguins
    
    penguins = pl.from_pandas(load_penguins())
    
    result = (
        penguins
            .drop_nulls(subset=["body_mass_g"])
            .groupby(["species", "island"])
            .agg(
                body_weight_mean = pl.col("body_mass_g").mean(),
                body_weight_sd   = pl.col("body_mass_g").std(),
            )
    )


    1. MegaPey
      30.11.2025 11:10

      Спасибо, с вашим комментом пришло понимание, чем R удобнее: действительно, в R нет такого количества скобочек.

      pipe - оператор "|>" упрощает сборку конвейера обработки данных, так как хорошо ложится в логику научного поиска, проб и ошибок.

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

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


      1. economist75
        30.11.2025 11:10

        В Pandas конвейеры делают 3-мя способами:

        • внутри скобок ( )

        • с df.pipe()

        • сторонним инструментом (в sklearn, например)

        Допускаю что R тут чуть-чуть читаемее. Но Python отыграется на списковых включениях, декораторах и др. мелком сахарке. Плюс DS-ник почти всегда свой ETL-конвейер в python оформляет как пакет/либу, с документацией, доктестами, CI/CD, контейнерами и вот этим вот всем. Экосистема Python выглядит тут гораздо более зрелой и массовой чем R, а размеры сообщества и число звезд на гитхабе и сравнивать нечего. Но, повторюсь, оба инструмента прекрасны.


      1. kmatveev
        30.11.2025 11:10

        но в R неудобные библиотеки создания интерактивных веб-приложений

        Вы пробовали Shiny и она показалась вам неудобной?


    1. kmatveev
      30.11.2025 11:10

      Не знаю python, но очень интересно, как работает agg ? У меня есть такая гипотеза: внутри agg используется синтаксис с именованными параметрами, а p.col("...").mean() - это не значение, а функция, в которую внутри agg будет передан сам dataframe, из которого оно уже извлечёт столбец и посчитает значение. Получается, что у agg неспецифицированы параметры, они всегда должны быть именованными, и она сама разбирает, что за параметры ей были переданы. Не очень понятно только, зачем скобки.


      1. economist75
        30.11.2025 11:10

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


  1. Alexander428
    30.11.2025 11:10

    Вода. Много воды. Примеры неубедительные. Можно было подвергнуть критике

    • Ужасную документацию. Серьезно. У какой-то ноунейм библиотеки на java например для работы с exel документация в 100 раз лучше, чем у python-библиотек. Приходится лезть на гитхаб, открывать код, спускаться сквозь кучу слоев абстракции предоставляемого API, чтобы понять а что же делает этот код и какие данные на вход ожидает.

    • Проблем с документацией добавляет kwargs. Потому что что? Фичу добавили, а в доки параметр не прописали. Оно и понятно, если функция/метод вызывает функцию, передавая ей kwargs, а та тоже вызывает функцию, тоже передавая kwargs, то это ж везде надо дублировать доку на каждый ключ словаря. Почему в языках без kwargs такой проблемы нет? А какую проблему kwargs решает? - Проблему гиганстких сигнатур методов (пришлось бы все аргументы явно перечислять для всех функций в цепочке вызовов). А как решают в других языках эту проблему? Создают отдельных класс с полями. Всё. Задокументировал только этот класс и нет проблем. Добавил потом поле, фичу в функцию и доку для поля 1 раз. Всё.

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

    • Из предыдущего пункта плавно вытекает плохая встроенная в язык реализация ФП (имхо удобнее для анализа данных)

    • Отсуствие нормальной поддержки аннотаций типов для библиотек. Там все еще изобилует нетипизированный код. Статическая типизация и борьба с компилятором - это конечно боль для людей которым нужно просто писать бизнеслогику и запускать интерактивно код, но типизация помогает искать и исключать ошибки. И когда добавили аннотации в Python для удобства разработчика, а не компьютера - это было круто. Но некруто когда спустя много лет, mypy бессилен с проверкаами на типы кода вызывающего библиотечные функции.

    • Нейминг порой убивает. Вроде snake_style, но startswith, tolist и т д.

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

    • Ruby (лучшая поддержка ООП)

    • Scala Spark (типизация, ФП)

    • Julia (скорость, типизация)

    Jupyter кстати поддерживает эти языки. А у Julia есть своя альтернатива в виде Pluto. Но как по мне, у Julia пока полный хаос с библиотеками (много маленьких, часто долго приходится выбирать нужную).