Как перебирать строки в DataFrame в Pandas?
Ответ: НЕ * !
Итерации в пандах - это анти-паттерн, и вы должны делать это только тогда, когда исчерпали все остальные опции. Вы не должны использовать какую-либо функцию с " iter
" в названии более нескольких тысяч строк, иначе вам придется привыкнуть к большим ожиданиям.
Хотите распечатать DataFrame? Использование DataFrame.to_string()
.
Вы хотите что-то вычислить? В этом случае ищите методы в следующем порядке (список изменен здесь ):
- Векторизация
- Подпрограммы Cython
- Список Пониманий (ванильная
for
петля)
DataFrame.apply()
: i) Сокращения, которые могут быть выполнены в Cython, II) Итерация в пространстве Python
DataFrame.itertuples()
а также iteritems()
DataFrame.iterrows()
iterrows
и itertuples
(оба получают много голосов в ответах на этот вопрос) следует использовать в очень редких случаях, таких как создание объектов строк / именных кортежей для последовательной обработки, что на самом деле является единственной вещью, для которой эти функции полезны.
Обращение к власти
На странице документации по итерации есть огромное красное окно с предупреждением:
Итерация по объектам панд обычно медленная. Во многих случаях повторение вручную по строкам не требуется [...].
* Это на самом деле немного сложнее, чем «нет». df.iterrows()
это правильный ответ на этот вопрос, но «векторизация ваших операций» лучше. Я признаю, что существуют обстоятельства, в которых нельзя избежать итерации (например, некоторые операции, в которых результат зависит от значения, вычисленного для предыдущей строки). Однако для того, чтобы узнать, когда это нужно, нужно некоторое знакомство с библиотекой. Если вы не уверены, нужно ли вам итеративное решение, скорее всего, нет. PS: Чтобы узнать больше о моем обосновании написания этого ответа, перейдите к самому низу.
Большое количество базовых операций и вычислений "векторизовано" пандами (либо через NumPy, либо через функции Cythonized). Это включает в себя арифметику, сравнения, (большинство) сокращений, изменение формы (например, поворот), объединений и групповых операций. Просмотрите документацию по основным функциям, чтобы найти подходящий векторизованный метод для вашей проблемы.
Если ничего не существует, не стесняйтесь писать свои собственные, используя собственные расширения 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'])]
Если вы можете инкапсулировать свою бизнес-логику в функцию, вы можете использовать понимание списка, которое ее вызывает. Вы можете заставить произвольно сложные вещи работать благодаря простоте и скорости необработанного Python.
Понятия Caveats
List предполагают, что с вашими данными легко работать - это означает, что ваши типы данных согласованы и у вас нет NaN, но это не всегда может быть гарантировано.
- Первый более очевиден, но при работе с NaN предпочтите встроенные методы панд, если они существуют (потому что они имеют гораздо лучшую логику обработки угловых случаев), или убедитесь, что ваша бизнес-логика включает соответствующую логику обработки NaN.
- При работе со смешанными типами данных вы должны выполнять итерацию
zip(df['A'], df['B'], ...)
вместо того, df[['A', 'B']].to_numpy()
чтобы последний неявно преобразовывал данные в наиболее распространенный тип. Например, если A числовое, а B строковое, to_numpy()
приведёт весь массив к строковому, что может быть не тем, что вы хотите. К счастью, zip
объединить ваши столбцы - это самый простой обходной путь.
* YMMV по причинам, изложенным в разделе « Предостережения » выше.
Очевидный пример
Продемонстрируем разницу на простом примере добавления двух столбцов панд A + B
. Это векторизованный оперон, поэтому будет легко сравнить производительность методов, описанных выше.
Код для сравнения, для вашей справки. Линия внизу измеряет функцию, написанную на numpandas, стиле панд, который сильно смешивается с numpy, чтобы выжать максимальную производительность. Написание кода numpandas следует избегать, если вы не знаете, что делаете. Придерживайтесь API, где вы можете (то есть, предпочитаете vec
более vec_numpy
).
Я должен отметить, однако, что это не всегда так сухо. Иногда ответ «что является лучшим методом для операции» - «это зависит от ваших данных». Мой совет, чтобы проверить различные подходы к вашим данным, прежде чем остановиться на одном.
Дальнейшее чтение
* Строковые методы панд "векторизованы" в том смысле, что они указаны в серии, но работают с каждым элементом. Базовые механизмы все еще итеративны, потому что строковые операции по своей природе трудно векторизовать.
Почему я написал этот ответ
Обычная тенденция, которую я замечаю у новых пользователей, - это задавать вопросы в форме «как я могу перебрать свой df, чтобы сделать X?». Отображение кода, вызывающего iterrows()
при выполнении чего-либо внутри цикла for. Вот почему. Новый пользователь библиотеки, который не был ознакомлен с концепцией векторизации, скорее всего, представит код, который решает их проблему, как перебирая свои данные, чтобы что-то сделать. Не зная, как перебирать DataFrame, первое, что они делают, - это Google и в конечном итоге здесь, на этот вопрос. Затем они видят принятый ответ, говорящий им, как это сделать, и закрывают глаза и запускают этот код, даже не задавая вопрос, является ли итерация неправильной вещью.
Цель этого ответа состоит в том, чтобы помочь новым пользователям понять, что итерация не обязательно является решением каждой проблемы, и что могут существовать лучшие, более быстрые и более идиоматические решения, и что стоит потратить время на их изучение. Я не пытаюсь начать войну итераций против векторизации, но я хочу, чтобы новые пользователи были информированы при разработке решений своих проблем с этой библиотекой.