Как стать автором
Обновить

Комментарии 15

У pandas вообще очень дурной api. В этом плане гораздо больше приятен dplyr из R, там всё чистенько и потоково. Но, массовая разработка, все используют Питон. Мыши плакали, кололись, и нам приходится :)

Спасибо за комментарий! Да, соглашусь, что R для классической аналитики очень даже хорош, и его много где недооценивают)

Как-то нанимал команду дата саентистов с 0 (задолго еще до последней волны хайпа). Собеседовал исключительно синиоров и лидов. Тоже был несказанно поражен тем, насколько среди них популярен пандас, и какой малый % из них способен писать SQL чуть сложнее совсем базового. Вопросы вида "а как будешь готовить обучающий датасет на 100ГБ?" в тупик ставят, многие до синиоров доросли, ни разу даже в 1ГБ не пощупав данных в проде. Кто-то вспоминает о костыльных способах масштабирования пандаса, библиотеках, совместимых с ним по сигнатурам, но раскладывающим задачи в map-reduce.

К слову, как по мне, то, что от pandas, что от dplyr, надо совсем немного, когда умеешь работать с SQL - их задача лишь какие-то мелочи поправить, чтобы сшить result set со входом модели. Как не крути, но для абсолютного большинства задач современные SQL движки аналитических баз (или исполнителей SQL поверх файлов, как Presto) сделают подготовку входа для библиотеки ML/анализа/визуализации куда эффективнее, и, главное, управляемее и масштабированее, чем библиотеки манипулирования датасетами, запускаемые из под того же python в его же процессе

Согласна про большие объемы данных и SQL, с ними точно стоит уметь работать.
Хотя есть достаточно приятные и Python-специфичные инструменты визуализации (я, например, люблю библиотеку Plotly и фреймворк Dash для практически моментального написания веб-приложений), и тут в случае подготовки какого-то конкретного отчета очень даже подходит pandas

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

DS/DI-ники под свои UDF часто пишут не тесты, а используют личный краткий тестовый df с чрезвычайно грязными данными (смесь типов, псевдокириллица из латиницы, смесь кодировок, пять разных пробелов и чисто статловушки (смесь разных ед измерения). На нем прекрасно вылезают всё непредусмотренные случаи и ошибки. Это как сквозной пример в бухучете - в конце должен получиться баланс с круглыми цифрами (а в DS - ML модель должна сойтись и дать 97,5% Accuracy).

Благодарю за комментарий, тоже отличная стратегия, особенно если код использует в основном только автор, вполне можно так заменить тесты)

У пандаса один плюс, всю эту кодолапшу в терминах его API теперь нам пишет ChatGPT. А потому что столько пандаса кругом, ChatGPT очень хорошо его выучил. Это радует, не приходится так расстраиваться теперь при его использовании :) С долей сарказма, но тут только доля шутки.

Ссылаться на «Чистый код» и Боба Мартина в 2024 — дурной тон. Это сборник вредных советов. Даже на момент издания код был мягко говоря безобразным.

Я открыта к дискуссии, подскажете, пожалуйста, что из советов в данной заметке вы считаете вредным?
(Хотя я лишь автор перевода, а не самой статьи, перевела её именно потому, что считаю написанное не необходимым, конечно, но как минимум адекватным)

Эх, если бы наш бывший дата-саентист следовал этим советам.. У меня даже отвращение к DataFrame выработалось, типа вам мало динамичности в Python - получайте ещё удар в пах.

Чет не торт:

В примере:

def compute_length(word: str) -> int:
    return len(word)

def prepare_data(df: pd.DataFrame) -> pd.DataFrame:
    return pd.concat([
        ...
        df.name.apply(compute_length).rename("name_len"),
        ...
    ],...)

Неужели только у меня екнула мылсь о тотальной ненужности compute_length как и тестов к этой функции :

def prepare_data(df: pd.DataFrame) -> pd.DataFrame:
    return pd.concat([
        ...
        df.name.apply(len).rename("name_len"),
        ...
    ],...)

Про падающий len(None) отписал ниже.

Далее, в оригинале непонятно что имел ввиду автор тут:

eries = df["name"].str.title()  # not a copy whatsoever (в любом случае не копия)

Из документации: str.len()возвращает новую серию, содержащую длины строк исходной серии. Таким образом, исходная серия остаётся неизменной. Так что это результат с числами вместо строк, о какой копии или не копии может идти речь?

Незнание базового Python тоже удручает:

def find_max_name_length(df: pd.DataFrame) -> int:
    df["name_len"] = df["name"].str.len()  # побочный эффект
    return max(df["name_len"])

@pytest.mark.parametrize("df, result", [
    (pd.DataFrame({"name": []}), 0),  # упс, здесь тест упадет

Да, точно, давайте создадим Safe Max функцию:

def find_max_element(collection: Collection) -> int:
    return max(collection) if len(collection) else 0

Вместо того что бы почитать документацию про max тут:

def find_max_element(collection: Collection) -> int:
    return max(collection, default=0)

Ну и, напоследок, про "пойманный баг". В оригинале автор(ка) прежде, чем работать с данными, их не провалидировала. По тестам видно, что данные - это набор строк, который вероятно, может содержать нулевые значения. Разумеется, можно на функции просчета len возвращать 0 если передано значение Null. Это, кстати, придется делать для всех функций падающих на Null. Ещё можно функцию применять не к df.Name а к df.Name.str. Падать len уже не будет, потому как будут передаваться 'None'. Но результаты плачевные, это видно тут:

def create_name_len_col(series: pd.Series) -> pd.Series:
    return series.str.len()

На каждое Noneзначение в исходной серии значений получим 4, вместо 0. Но в тестах это пропущено.

Так что... Товарищи! Мойте руки Валидируйте данные перед едой! Ну и про тесты не забывайте, а то - тут одно протестировали, там другое... не надо так.

p.s. За перевод спасибо!

Это ценный комментарий, благодарю!)

Точно могу согласиться с поинтом про валидацию данных заранее, хотя на практике всякое случается)
Что касается момента с "не-копией", я поняла его так, что там присвоили значение новому объекту Series, а потому он не имеет отношения к изменению исходного датафрейма (вполне очевидно, но это подчеркнули)

(И в принципе ООП даже в контексте Python — это последнее, с чем ассоциируется анализ данных с использованием pandas. (примечание автора перевода)) - гугл-транслейт уже начал примечания писать? :о)

Ага, гугл-транслейт так поумнел, что еще и ваш комментарий промодерировал и отвечает

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории