Дев’ять помилок, яких новачкам варто уникати при роботі з Pandas

💡 Усі статті, обговорення, новини про Python — в одному місці. Приєднуйтесь до Python спільноти!

Ви аналітик чи людина, яка працює з даними? Студент, який хоче освоїти новий інструмент для роботи з даними? Просто ентузіаст, що цікавиться всім на світі? Вам настільки немає чим зайнятися, що ви вирішили приділити свій дорогоцінний час вивченню цієї теми? Якщо у вас є хоча б одне «так», тоді у мене для вас чудові новини — ви натрапили на правильну сторінку.

Мене звати Олександр Чакалов, я Senior Big Data Developer в компанії N-ix, і сьогодні ми з вами спробуємо, не пірнаючи з головою у тонкощі використання Pandas, розглянути певні базові помилки, яких можна уникнути з першого дня роботи з цією бібліотекою. Таким чином убережемо ваших тіммейтів та тімліда від зайвих нервових розладів чи депресії.

Що ж... Pandas... Якщо коротко, Pandas — це доволі потужна бібліотека Python для роботи з даними, яка стала невіднятною частиною арсеналу будь-якого аналітика чи дата саєнтиста. Вона в рази спростила обробку та аналіз даних, оптимізувала кількість рядків коду з десяти до одного (доволі грубий підрахунок, але ідея зрозуміла), що дозволяє зекономити час і зусилля, зосереджуючись на більш важливих аспектах аналізу. Використовуючи цю бібліотеку, ми маємо усі інструменти для ефективної маніпуляції таблицями, масивами, часовими рядами тощо.

У Pandas ми можемо знайти доволі зручні та інтуїтивно зрозумілі методи для фільтрації, агрегації, злиття і зміни даних. Окрім того, вона інтегрується з іншими популярними інструментами Python для роботи з даними, як-то NumPy, Matplotlib чи SciPy, дозволяючи створювати комплексні рішення для різноманітних задач.

Як і у випадку з абсолютно усім, що використовує людина — яким би не був потужним та інтуїтивно зрозумілим інструмент, його завжди можна використовувати не за призначенням. І понаписувати такі «костилі», що зроблять ситуацію ще гіршою, ніж було спочатку. Сьогодні ми спробуємо розглянути деякі моменти використання цієї бібліотеки, яких бажано навчитися уникати з перших днів вивчення або використання.

Перші кроки й налаштування

Гаразд, полетіли. Встановили IDE — не обовʼязково, але бажано мати, наприклад, PyCharm, Visual Studio Code тощо. Завантажити Python (тут) та Pandas (інструкції, як встановити Pandas, можна знайти тут). Потім створили новий файлик і написали славнозвісне:

import pandas as pd

Тут починається ваша робота з бібліотекою. На цьому етапі все ще має працювати безвідмовно та передбачувано. Якщо виникли помилки під час встановлення чи налаштування IDE — спробуйте використати гугл, він вам допоможе. Тепер розглянемо, які підводні камені можуть нас очікувати на початку роботи з цією бібліотекою.

Ігнорування типів даних

Типи даних у Pandas визначають, як зберігаються і обробляються значення в таблицях. Надзвичайно легко не вказувати типи даних, але як результат це може призвести до неефективності, помилок обчислень і непередбачуваних результатів. Цей нюанс можна легко нівелювати за допомогою перевірки та кореляції типу даних за допомогою методів pd.to_numeric, pd.to_datetime тощо.

df['column'] = pd.to_numeric(df['column'], errors='coerce')

P.S. Що таке «coerce»? Офіційна документація нам з цим допоможе:

errors{‘ignore’, ‘raise’, ‘coerce’}, default ‘raise’

If ‘raise’, then invalid parsing will raise an exception.

If ‘coerce’, then invalid parsing will be set as NaN.

If ‘ignore’, then invalid parsing will return the input.

Оформлення вашого коду

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

Часто, коли ми виконуємо запит у Pandas, робимо це послідовно, що своєю чергою може дещо ускладнити читання коду. Розгляньмо, як можна зробити запити більш зрозумілими, використовуючи символи «()» і «\».

У нас є два DataFrame: employees та salaries. Ми хочемо об’єднати їх за ID працівника, відфільтрувати результати за певним департаментом, а потім вивести топ-5 працівників з найвищою зарплатою.

Без форматування:

pd.merge(employees, salaries, on='employee_id').query('department == "Sales"').sort_values('salary', ascending=False).head(5)

З форматуванням:

    pd.merge(employees, salaries, on='employee_id') \
      .query('department == "Sales"') \
      .sort_values('salary', ascending=False) \
      .head(5)

Гадаю, додаткові коментарі зайві.

Наявність пробілів у назвах стовпців

У Pandas назви колонок, які містять пробіли, можуть спричиняти труднощі при роботі з даними у цих же колонках. З ними можна працювати, але тоді потрібно використовувати лапки і квадратні дужки щоразу, коли ви до них звертаєтесь. Що є не надзвичайно user friendly і може спричинити подальші помилки й певну кількість витраченого часу і зусиль на пошук проблем.

Якщо у вас є DataFrame з колонкою, назва якої містить пробіл, наприклад «Total Sales», спроба звернутись до цієї колонки за допомогою точкової нотації викликає синтаксичну помилку:

df.Total Sales

Щоб звернутися до такої колонки, потрібно використовувати квадратні дужки та лапки:

df['Total Sales']

Однак регулярне використання квадратних дужок може бути незручним і призводити до помилок, особливо коли у вас багато колонок з пробілами.

Крім цього, деякі функції Pandas можуть некоректно обробляти такі назви колонок. Наприклад, спроба згрупувати дані за такою колонкою:

df.groupby('Total Sales')['Quantity'].sum()

викличе помилку KeyError, оскільки Pandas не може розпізнати ім’я колонки з пробілом.

Щоб обійти ці проблеми, краще за все уникати пробілів у назвах колонок, використовуючи замість цього нижнє підкреслення або camelCase. Наприклад, «Total_Sales» або «totalSales».

Використання циклів замість векторизованих операцій

Цикли — річ цікава, але зловживати ними не варто. Використання циклів for або while для виконання операцій над даними, що зберігаються у DataFrame, призводить до зниження продуктивності та може збільшити час виконання коду.

Щоб уникнути використання циклів, слід вдатись до векторизованих операцій, які дають змогу виконувати обчислення над цілими колонками чи рядками DataFrame одразу. Вони реалізовані на низькому рівні й оптимізовані для швидкого виконання.

На практиці це виглядатиме наступним чином.

З використанням циклу:

import pandas as pd
# Створимо DataFrame з даними про продажі
data = pd.DataFrame({'sales': range(1, 100001)})
# Виконуємо операцію збільшення продажів на 10% за допомогою циклу
for i in range(len(data)):
    data.at[i, 'sales'] *= 1.10
print(data.head())

З використанням векторизованої операції:

import pandas as pd
# Створимо DataFrame з даними про продажі
data = pd.DataFrame({'sales': range(1, 100001)})
# Виконуємо операцію збільшення продажів на 10% за допомогою векторизованої операції
data['sales'] *= 1.10
print(data.head())

Векторизовані операції також можуть допомогти вам замінити apply() та lambda функції. Метод apply() є надзвичайно потужним інструментом, який дає змогу залучити довільні функції для кожного значення в Series або до рядків чи стовпців у DataFrame. Але не завжди apply() це найкраще рішення, бо для великих наборів даних це може бути неефективним і доволі повільним.

Lambda-функції — короткі анонімні (в них немає імені, по якому можна знову викликати в коді), які зазвичай використовуються для швидких маніпуляцій з даними. Ці анонімні (інлайн) функції забезпечують компактний спосіб виконання операцій над DataFrame та Series без формального оголошення функції.

Ми можемо замінити використання apply() та lambda за допомогою векторизованих операцій.

Використання apply() + lambda:

import pandas as pd

# Створимо DataFrame з однією колонкою
df = pd.DataFrame({'column1': range(1, 100001)})
# Використання apply() для додавання 1 до кожного значення
df['new_column'] = df['column1'].apply(lambda x: x + 1)
print(df.head())

З використанням векторизованої операції:

import pandas as pd

# Створимо DataFrame з однією колонкою
df = pd.DataFrame({'column1': range(1, 100001)})
# Використання векторизованої операції для додавання 1 до кожного значення
df['new_column'] = df['column1'] + 1
print(df.head())

Ігнорування groupby() при роботі з агрегаційними функціями

Не слід ігнорувати можливості, які надають нам агрегаційні функції, тому потрібно звернути увагу на метод groupby(), створений для групування та агрегації даних у Pandas. Він спрощує та робить ефективним виконання складних операцій над групами даних. groupby() можна використати для групування за однією або кількома колонками та виконання агрегаційних функцій, як-то mean(), sum(), count() тощо.

Приклад:

Уявімо, що у нас є DataFrame df з інформацією про продажі, і ми хочемо обчислити середнє значення продажів для кожної категорії продуктів.

import pandas as pd
# Створимо DataFrame з даними про продажі
data = {
    'product_category': ['A', 'B', 'A', 'B', 'A'],
    'sales': [100, 200, 150, 250, 300]
}
df = pd.DataFrame(data)
# Групування даних за категорією продукту і обчислення середнього значення продажів для кожної категорії
grouped_df = df.groupby('product_category').mean()
print(grouped_df)

І ще один, більш наочний приклад, коли потрібно знайти середню вартість продажів для різних регіонів.

Через створення піднаборів даних (без groupby):

import pandas as pd
sales_data = pd.read_csv('sales.csv')

# Створення піднаборів даних та обчислення середньої вартості продажів для кожного регіону
north_avg = sales_data.query('region == "North"')['sales'].mean()
south_avg = sales_data.query('region == "South"')['sales'].mean()
east_avg = sales_data.query('region == "East"')['sales'].mean()
west_avg = sales_data.query('region == "West"')['sales'].mean()

Цей підхід займає багато часу та може запросто приховати якесь typo чи помилку в розрахунках. Натомість можна використовувати метод groupby(), який дозволяє ефективно обчислювати статистичні міри для категорій даних:

import pandas as pd
sales_data = pd.read_csv('sales.csv')

# Використання groupby для обчислення середньої вартості продажів для кожного регіону
average_sales = sales_data.groupby('region')['sales'].mean()
print(average_sales)

Також серед переваг groupby() можна виокремити ефективність, зрозумілість (ваш код стає чистішим та легшим для сприйняття) та можливість використання власних функцій, крім вбудованих.

Неприємності при збереженні CSV-файлів

Часто після завершення обробки даних виникає потреба зберегти їх у CSV-файл. Збереження даних у CSV без належних параметрів може призвести до непередбачуваних результатів, які ускладнюють подальшу роботу з файлом. Не забувайте використовувати index=False, якщо ви хочете побачити додаткові колонки індексу, які можуть трішки змінити вигляд вашого файлу.

import pandas as pd
# Завантаження даних
df = pd.read_csv('dataset.csv')
# Збереження даних без додаткового індексу
df.to_csv('dataset_clean', index=False)

Цей підхід допоможе уникнути непотрібних складнощів і дозволить зберегти дані в чіткому та зрозумілому форматі. Завжди використовуйте належні параметри при збереженні CSV-файлів, щоб ваші дані залишалися впорядкованими та легкодоступними. До речі, у методу to_csv() багато параметрів, ви можете ознайомитись з ними за посиланням.

CSV — не єдиний формат у Pandas

Не обмежуйте себе лише одним форматом даних, адже ситуації можуть суттєво відрізнятися залежно від даних та потреб користувачів. Особливо це стосується моментів, коли ви працюєте з великими наборами даних і намагаєтесь покладатися лише на CSV-формат, який не є дуже оптимізованим для такого роду завдань.

Звісно, це один з найпоширеніших і найлегших способів збереження даних, але все ж не найкращий. Також крім проблем з продуктивністю (читання і запис великих CSV-файлів може бути повільним та викликати високе навантаження на систему), нам потрібно змиритися з наступними моментами:

  • Обмеженість типів даних. CSV підтримує тільки основні типи даних, такі як текст, числа та дати. Якщо у вас є складніші дані, наприклад, Json-обʼєкти чи зображення, CSV не підходить.
  • Відсутність метаданих. CSV-файли не зберігають метадані — типи даних, назви стовпців тощо. Це може ускладнити експорт, імпорт і аналіз даних.
  • Відсутність перевірки даних. CSV-формат також не забезпечує build-in перевірку даних, що може призвести до помилок і непослідовностей у самих даних.

Якщо ви зіткнетеся з великими наборами даних, я пропоную розглянути наступні альтернативи CSV-формату. Плюс отримаєте плюсик в карму на співбесіді, коли скажете, що маєте досвід використання Big Data форматів:

  • Parquet — це колонковий формат зберігання, що найкраще підходить для read-heavy задач, оптимізований для великих наборів даних. Він обробляє складні типи даних і підтримує стиснення, що робить його ідеальним для великих DataFrame.
df.to_parquet('dataset.parquet')
  • Feather — легкий бінарний формат файлу, створений для швидкого читання і запису, підтримує як R, так і Python.

df.to_feather(’dataset.feather’)Copy

  • Apache Arrow — платформа для обробки даних в пам’яті, яка забезпечує стандартизований формат для використання різними мовами програмування.
import pyarrow as pa
import pyarrow.parquet as pq

table = pa.Table.from_pandas(df)
pq.write_table(table, ’dataset.arrow’)

Це далеко не всі альтернативи, які є на ринку, тому коли зіткнетеся на практиці з поганою продуктивністю, подумайте або погугліть, який саме формат найкраще відповідатиме вашим потребам та вашим вхідним даним.

Візуалізація... Візуалізація повсюди

Розпочинаючи своє знайомство з Python чи Data Science, початківці для візуалізації даних зазвичай обирають бібліотеку Matplotlib або схожі. Але про Pandas також не варто забувати. Він теж має можливості для візуалізації даних за допомогою методу plot() і, як завжди, уся інформація є в офіційній документації у доволі зрозумілій формі.

Pandas — це не швейцарський ніж

Якою потужною не була б бібліотека, неможливо обійтися лише нею. Звісно, ви можете спробувати зробити все, що вам треба, використовуючи одну бібліотеку. Але тоді колись ви зіткнетеся з ситуацією, в якій будете пробувати їсти борщ виделкою.

Обирайте інструменти, які найкраще підходять для конкретних завдань. Використовуйте Pandas для маніпуляції з таблицями та аналізу даних, але для глибинних математичних і числових обчислень розгляньте використання бібліотеки NumPy.

І наостанок

Негоже вас залишати без логічного завершення попередньої писанини (надзвичайно шокований і вдячний, що ви дійшли аж до цього моменту), тому ось кілька завершальних слів.

Pandas — це must have інструмент як мінімум для ознайомлення, якщо ви плануєте зануритися у світ даних. Пункти, які ми розглянули — це не догми, а лише поради, на що слід звернути увагу. Сподіваюся, вони будуть корисними на початку роботи з цією бібліотекою.

При роботі з даними не забуваємо аналізувати наші дані, памʼятаємо про важливість типів даних та можливості для оптимізації, які надають векторизовані операції. Зосереджуємось на написанні чистого, добре структурованого коду, який легко читати і підтримувати. Адже код пишеться один раз, а читається — безліч, тому попіклуйтесь про своїх тіммейтів і про себе з майбутнього. Приділіть трішки більше часу і зусиль, щоб ваш код виглядав красиво і зрозуміло.

CSV — це не єдиний формат. Не знаєте, що використати — гугл знає. Але зберігайте дрібку скептицизму, навіть сер Гугл іноді помиляється. Також не варто забувати про всі можливості Pandas. Розробники передбачили надзвичайно багато сценаріїв, які можна і варто використовувати. У більшості випадків build-in солюшени працюватимуть краще, ніж система «костилів», які будуть підпирати інші «костилі». Здатність обирати правильні інструменти для конкретних завдань зробить вас більш продуктивним і цінним фахівцем.

Таким чином, Pandas — це не просто бібліотека, це ваш союзник у світі даних. Використовуючи правильні підходи та уникаючи поширених помилок, ви зможете досягти успіху у своїй професійній діяльності, відкриваючи перед собою нові можливості для аналізу та прийняття рішень на основі даних.

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

Дані — це головне в сучасному світі, нова нафта 21 століття. Даних ставатиме лише більше і більше, тому краще бути з ними на «ти». Успіхів вам у подорожі з Pandas!

👍ПодобаєтьсяСподобалось13
До обраногоВ обраному6
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Хочу упомянуть формат хранения hdf5 с зип уровня ~5. Он бинарен, зип 1-5 сжимает быстро (выше уже заметно медленнее), универсален для любого языка так как изначально кажется в наса его начали использовать.
Я конечно понимаю, что место на диске — это вообще копейки для комерческих проектов, но если речь идет про бигдату доморощенного проекта на условном макбуке с 1-2тб диска, где надо хранить сырой и разные версии обработанных датасетов — я для временных рядов не нашел ничего более эффективного по занимаемому обьему места на диске. Вероятно паркет или датасеты tf будут быстрее в чтении, но скорее gpu при обучении будет узким горлышком, чем нынешние м2/nvme ssd в чтении правильно оформленного датасета

Pandas прикольна цацка, роки півтора на ETL проекті працював з ним.
Але python за цей час так і не не пробудив теплих почуттів до себе=(
C# <3

Було б цікаво почитати про polars щось подібне

Підтримую. Треба внести його до списку актуальних тем.

Ех, як не вистачало мені Pandas в 2000-них для наукових завдань.
Цікаво, чому Python і та вся еко система не могла початися в 90-х?

Публічний реліз був у лютому 1991.

І хось ним користувався тоді?
Я вперше почув про Python в 2008, про Pandas в 2014.

Пайтоном користувались всі, Ютуб переписали на пайтон після купівлі Гуглом, більшість мережевих компаній завжди були на пайтоні. Пайтон в США був повсюди на бекенді. Pandas вийшов в 2008, екосистема тоді стрімко набирала обертів і зараз пайтон це перша мова, котру обирають для бекенду в Кремнієвій Долині у стартапах, інколи ноду.

Щоб обійти ці проблеми, краще за все уникати пробілів у назвах колонок, використовуючи замість цього нижнє підкреслення або camelCase. Наприклад, «Total_Sales» або «totalSales».

Від Senior Big Data Engineer я б очікував більш конкретної поради, аж до того щоб привести приклад як з df["C o l u m n with Spaces"] отримати df["Nicely_formated_column_according_to_projects_naming_convention"]

Ех, шкода, що не зміг покрити ваших запитів.

Ще варто було згадати json — чудовий формат який зберігає сам формат даних.

Люди або ORC або паркет опрацьовують, в залежності від структури та потреби інкрементальної обробки... CSV не бачив вже 10 років в ETL’ках. Ну й в чистому вигляді панди в здорових проектах майже не використовувать... зазвичай качок підпалюють.

так, було б цікаво порівняти pandas хоча б з sqlite, бо незрозуміло яки переваги...

... мова трохи не про те — існує багато ліб на сішці/расті які є drop-in replacement’ом панд, крім качок був ще Pola.rs але то лиш обгортка поверх Arrow

Формати датафрейму будуть залежати від вирівнювання цільової архітектури та потреб Data Locality, — зазвичай там не sqlite, а одразу в Arrow переганяється з Orc / Parquet. Таким чином якщо пандами щось числодробить, можна через shmem запихнуть там в pyspark / pyflink жабо-процес без зайвого копіювання фреймів (zero copy) й доданих витрат на комунікацію.

В якості БД частіш використовують колоночні типу Cassandra / Scylla з відповідною вигрузкою денормалізованого представлення...

(
    pd.merge(employees, salaries, on='employee_id') \
      .query('department == "Sales"') \
      .sort_values('salary', ascending=False) \
      .head(5)
)
Або дужечки або слеши щось одне.

Той випадок коли захотів показати все і одразу 😄

Підписатись на коментарі