Дев’ять помилок, яких новачкам варто уникати при роботі з Pandas
Ви аналітик чи людина, яка працює з даними? Студент, який хоче освоїти новий інструмент для роботи з даними? Просто ентузіаст, що цікавиться всім на світі? Вам настільки немає чим зайнятися, що ви вирішили приділити свій дорогоцінний час вивченню цієї теми? Якщо у вас є хоча б одне «так», тоді у мене для вас чудові новини — ви натрапили на правильну сторінку.
Мене звати Олександр Чакалов, я 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!
16 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів