Як перебрати рядки в DataFrame в Pandas?
Відповідь: НЕ * !
Ітерація в пандах є анти-закономірністю, і це те, що ви повинні робити тільки тоді, коли ви вичерпали всі інші варіанти. Ви не повинні використовувати жодну функцію з iter
назвою " " більше декількох тисяч рядків, або вам доведеться звикати до багатьох очікування.
Ви хочете надрукувати DataFrame? ВикористовуйтеDataFrame.to_string()
.
Ви хочете щось обчислити? У такому випадку знайдіть методи в цьому порядку (список, змінений тут ):
- Векторизація
- Програми цитонів
- Означення списку (ванільна
for
петля)
DataFrame.apply()
: i) Скорочення, які можна виконати в цитоні, ii) Ітерація в просторі пітона
DataFrame.itertuples()
і iteritems()
DataFrame.iterrows()
iterrows
і itertuples
(обидва, що отримують багато голосів у відповідях на це запитання), повинні використовуватися в дуже рідкісних обставинах, таких як генерування рядкових об'єктів / наметрів для послідовної обробки, що справді єдине, для чого ці функції корисні.
Звернення до авторитету
На сторінці документів на ітерації є величезне червоне поле попередження, яке говорить:
Ітерація через об’єкти панди, як правило, повільна. У багатьох випадках ітерація вручну над рядками не потрібна [...].
* Це насправді трохи складніше, ніж "не робити". df.iterrows()
є правильною відповіддю на це запитання, але "векторизуйте свої операційні" - це краще. Я визнаю, що існують обставини, коли ітерації неможливо уникнути (наприклад, деякі операції, де результат залежить від значення, обчисленого для попереднього рядка). Однак, щоб дізнатися, коли, знадобиться деяке знайомство з бібліотекою. Якщо ви не впевнені, чи потрібно вам ітеративне рішення, ви, мабуть, цього не зробите. PS: Щоб дізнатися більше про моє обґрунтування написання цієї відповіді, перейдіть до самого низу.
Значна кількість основних операцій та обчислень "векторизується" пандами (або через NumPy, або через цитонізовані функції). Сюди входять арифметичні, порівняння, (більшість) скорочень, перестановки (такі як поворотні), з'єднання та групові операції. Перегляньте документацію про основні функціональні можливості щоб знайти відповідний векторний метод для вашої проблеми.
Якщо такої немає, сміливо пишіть власні, використовуючи спеціальні розширення cython .
Наступна найкраща річ: Зрозуміння списку *
Ознайомлення зі списком повинні бути вашим наступним портом дзвінка, якщо 1) немає векторизованого рішення, 2) продуктивність важлива, але недостатньо важлива, щоб пройти через клопоти цитонізувати ваш код, і 3) ви намагаєтеся виконати елементарну трансформацію на свій код. Існує велика кількість доказів, які дозволяють зробити висновок про те, що розуміння списків є достатньо швидким (і навіть іноді швидшим) для багатьох загальних завдань панди.
Формула проста,
# iterating over one column - `f` is some function that processes your data
result = [f(x) for x in df['col']]
# iterating over two columns, use `zip`
result = [f(x, y) for x, y in zip(df['col1'], df['col2'])]
# iterating over multiple columns - same data type
result = [f(row[0], ..., row[n]) for row in df[['col1', ...,'coln']].to_numpy()]
# iterating over multiple columns - differing data type
result = [f(row[0], ..., row[n]) for row in zip(df['col1'], ..., df['coln'])]
Якщо ви можете вкласти свою логіку бізнесу у функцію, ви можете використати розуміння списку, яке викликає її. Ви можете змусити довільно складні речі працювати простотою і швидкістю необробленого пітона.
Зрозуміння
списку Caveats передбачає, що з вашими даними легко працювати - це означає, що ваші типи даних є послідовними та у вас немає NaN, але це не завжди може бути гарантовано.
- Перший є більш очевидним, але, працюючи з NaN, віддайте перевагу вбудованим методам панд, якщо вони існують (оскільки вони мають набагато кращу логіку обробки кутових випадків) або переконайтеся, що ваша бізнес-логіка включає відповідну логіку обробки NaN.
- У роботі зі змішаними типами даних вам слід повторити перегляд
zip(df['A'], df['B'], ...)
замість того, df[['A', 'B']].to_numpy()
як останні неявно перетворюють дані на найпоширеніший тип. Як приклад, якщо A числовий, а B - рядок, to_numpy()
буде передано весь масив до рядка, який може бути не тим, що ви хочете. На щастя, zip
пінг ваших стовпців разом є найбільш простим вирішенням цього питання.
* YMMV з причин, викладених у розділі " Caveats " вище.
Очевидний приклад
Продемонструємо різницю простим прикладом додавання двох стовпців панди A + B
. Це опектон, що ветеризується, тому легко буде протиставити ефективність методів, обговорених вище.
Код тестування для довідки.
Але я мушу зазначити, що це не завжди така порізка і суха. Іноді відповідь на те, "який найкращий метод для операції" - це "це залежить від ваших даних". Моя порада - перевірити різні підходи до своїх даних, перш ніж зупинитися на одному.
Подальше читання
* Строкові методи Pandas "векторизовані" в тому сенсі, що вони вказані на серії, але працюють на кожен елемент. Основні механізми все ще є ітераційними, оскільки струнні операції за своєю суттю важко векторизувати.
Чому я написав цю відповідь
Поширена тенденція, яку я помічаю від нових користувачів, - задавати питання форми "як я можу повторити свій df, щоб зробити X?". Показ коду, який дзвонить iterrows()
, роблячи щось усередині циклу. Ось чому. Новий користувач бібліотеки, який не ознайомився з поняттям векторизації, швидше за все, передбачає код, який вирішує їх проблему як ітерацію над своїми даними, щоб щось зробити. Не знаючи, як перебирати DataFrame, перше, що вони роблять, це Google це, і, нарешті, тут, на це питання. Потім вони бачать прийняту відповідь, яка говорить їм, як це робити, і вони закривають очі і виконують цей код, не замислюючись про те, чи ітерація не потрібна.
Мета цієї відповіді - допомогти новим користувачам зрозуміти, що ітерація не обов'язково є вирішенням кожної проблеми, і що краще, швидше та ідіоматичніші рішення можуть існувати, і що варто інвестувати час на їх вивчення. Я не намагаюся розпочати війну ітерації проти векторизації, але хочу, щоб нові користувачі були поінформовані при розробці рішень своїх проблем з цією бібліотекою.