Как перебирать строки в DataFrame в Pandas

У меня DataFrame от Панд:

import pandas as pd
inp = [{'c1':10, 'c2':100}, {'c1':11,'c2':110}, {'c1':12,'c2':120}]
df = pd.DataFrame(inp)
print df

Выход:

   c1   c2
0  10  100
1  11  110
2  12  120

Теперь я хочу перебрать строки этого кадра. Для каждой строки я хочу иметь доступ к ее элементам (значениям в ячейках) по имени столбцов. Например:

for row in df.rows:
   print row['c1'], row['c2']

Возможно ли это сделать в пандах?

Я нашел этот похожий вопрос. Но он не дает мне нужного ответа. Например, там предлагается использовать:

for date, row in df.T.iteritems():

or

for row in df.iterrows():

Но я не понимаю, что это за объект row и как с ним работать.


person Roman    schedule 10.05.2013    source источник
comment
Df.iteritems () выполняет итерацию по столбцам, а не по строкам. Таким образом, чтобы заставить его перебирать строки, вы должны транспонировать (T), что означает, что вы меняете строки и столбцы друг в друга (отражаете по диагонали). В результате вы эффективно перебираете исходный фрейм данных по его строкам, когда используете df.T.iteritems ()   -  person Stefan Gruenwald    schedule 15.12.2017
comment
В отличие от того, что говорит cs95, есть вполне веские причины хотеть перебирать фрейм данных, поэтому новые пользователи не должны расстраиваться. Один из примеров - если вы хотите выполнить некоторый код, используя значения каждой строки в качестве входных. Кроме того, если ваш фрейм данных достаточно мал (например, менее 1000 элементов), производительность на самом деле не проблема.   -  person oulenz    schedule 16.10.2019
comment
@ cs95 Мне кажется, что фреймы данных - это формат таблицы в Python. Поэтому всякий раз, когда вы хотите читать в CSV, или у вас есть список dicts, значениями которых вы хотите манипулировать, или вы хотите выполнить простые операции соединения, группировки или окна, вы используете фрейм данных, даже если ваши данные сравнительно малы.   -  person oulenz    schedule 16.11.2019
comment
@ cs95 Нет, но это вообще было ответом на использование DataFrame. Я считаю, что именно поэтому данные могут храниться в фреймворке данных. Если вы хотите, например, запустите сценарий для каждой строки ваших данных, вы должны перебрать этот фрейм данных.   -  person oulenz    schedule 16.11.2019
comment
Я второй @oulenz. Насколько я могу судить, pandas - это лучший выбор для чтения файла csv, даже если набор данных небольшой. Просто программировать манипулировать данными с помощью API.   -  person Forever    schedule 19.11.2019
comment
Если вы новичок в этой теме и не знакомы с библиотекой pandas, стоит сделать шаг назад и оценить, является ли итерация действительно решением вашей проблемы. В некоторых случаях это так. В большинстве случаев это не так. Мой пост ниже знакомит новичков с библиотекой, облегчая им концепцию векторизации, чтобы они знали разницу между написанием хорошего кода и просто работающим кодом, а также знали, когда какой использовать. Некоторые люди довольны последним, они могут продолжать голосовать за комментарий @oulenz сколько угодно.   -  person cs95    schedule 25.02.2021
comment
Мне нужно сгенерировать список штатов США из двух букв + население. Что может быть лучше, чем повторение. мой df и с помощью print?   -  person user1854182    schedule 26.04.2021
comment
используйте 1_. Для получения дополнительной информации см. geeksforgeeks.org/   -  person Pavindu    schedule 26.05.2021


Ответы (28)


DataFrame.iterrows - генератор, который выдает как индекс, так и строку (как серию):

import pandas as pd

df = pd.DataFrame({'c1': [10, 11, 12], 'c2': [100, 110, 120]})

for index, row in df.iterrows():
    print(row['c1'], row['c2'])
10 100
11 110
12 120
person waitingkuo    schedule 10.05.2013
comment
Примечание. Поскольку iterrows возвращает Series для каждой строки, он не сохраняет типы данных в строках. Кроме того, вы никогда не должны изменять то, что вы повторяете. Согласно pandas 0.19.1 docs - person viddik13; 07.12.2016
comment
@ viddik13 это отличное примечание, спасибо. Из-за этого я столкнулся с тем, что числовые значения, такие как 431341610650, читаются как 4.31E+11. Есть ли способ сохранить типы dtypes? - person Aziz Alto; 05.09.2017
comment
@AzizAl, чтобы использовать itertuples, как описано ниже. См. Также pandas.pydata.org/pandas-docs/ стабильный / сгенерированный / - person Axel; 07.09.2017
comment
Как изменится объект строки, если мы не будем использовать индексную переменную во время итерации ?? В этом случае мы должны использовать row [0], row [1] вместо меток? - person Prateek Agrawal; 05.10.2017
comment
Не используйте iterrows. Itertuples работает быстрее и сохраняет тип данных. Дополнительная информация - person James L.; 01.12.2017
comment
если вам не нужно сохранять тип данных, iterrows в порядке. Совет @ waitkuo по разделению индекса значительно упрощает синтаксический анализ. - person beep_check; 03.05.2018
comment
Из документации: Итерация по объектам pandas обычно медленный. Во многих случаях повторение строк вручную не требуется [...]. Ваш ответ правильный (в контексте вопроса), но нигде об этом не упоминается, так что это не очень хороший ответ. - person cs95; 28.05.2019

Как перебирать строки в DataFrame в Pandas?

Ответ: НЕ *!

Итерация в Pandas - это антипаттерн, и вы должны делать это только тогда, когда вы исчерпали все остальные варианты. Вы не должны использовать какую-либо функцию с iter в названии для более чем нескольких тысяч строк, иначе вам придется привыкнуть к большому ожиданию.

Вы хотите распечатать DataFrame? Используйте DataFrame.to_string() .

Вы хотите что-то вычислить? В этом случае ищите методы в этом порядке (список изменен с здесь):

  1. Векторизация
  2. Подпрограммы Cython
  3. Составление списков (стандартный for цикл)
  4. DataFrame.apply() : i) редукции, которые могут быть выполнены в Cython, ii) итерация в пространстве Python
  5. DataFrame.itertuples() и iteritems()
  6. DataFrame.iterrows() < / strong>

iterrows и itertuples (оба получили много голосов в ответах на этот вопрос) должны использоваться в очень редких случаях, таких как создание объектов строк / наборов имен для последовательной обработки, что на самом деле единственное, для чего эти функции полезны.

Обращение к властям

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

Итерация по объектам pandas обычно выполняется медленно. Во многих случаях повторение строк вручную не требуется [...].

* На самом деле это немного сложнее, чем нет. df.iterrows() - правильный ответ на этот вопрос, но векторизация ваших операций - лучший вариант. Я признаю, что есть обстоятельства, при которых невозможно избежать итерации (например, некоторые операции, результат которых зависит от значения, вычисленного для предыдущей строки). Тем не менее, чтобы понять, когда это произойдет, требуется некоторое знакомство с библиотекой. Если вы не уверены, нужно ли вам итеративное решение, скорее всего, нет. PS: Чтобы узнать больше о моем обосновании написания этого ответа, перейдите в самый конец.


Быстрее, чем цикл: векторизация, Cython

Большое количество базовых операций и вычислений векторизованы пандами (либо через NumPy, либо через функции Cythonized). Это включает в себя арифметику, сравнения, (большинство) сокращений, изменение формы (например, поворот), объединения и групповые операции. Просмотрите документацию по Essential Basic Functionality найти подходящий векторизованный метод для вашей задачи.

Если такового не существует, вы можете написать свой собственный, используя custom Расширения Cython.


Следующая лучшая вещь: Составление списка *

Понимание списков должно быть вашим следующим портом обращения, если 1) нет доступного векторизованного решения, 2) производительность важна, но недостаточно важна, чтобы пройти через трудности цитонизации вашего кода, и 3) вы пытаетесь выполнить поэлементное преобразование в вашем коде. Существует достаточное количество доказательств, позволяющих предположить, что понимание списка достаточно быстры (а иногда и быстрее) для многих распространенных задач Pandas.

Формула проста,

# 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.

Предостережения

Составление списков предполагает, что с вашими данными легко работать - это означает, что ваши типы данных согласованы и у вас нет NaN, но это не всегда может быть гарантировано.

  1. Первый из них более очевиден, но при работе с NaN предпочитайте встроенные методы pandas, если они существуют (потому что они имеют гораздо лучшую логику обработки угловых случаев), или убедитесь, что ваша бизнес-логика включает соответствующую логику обработки NaN.
  2. При работе со смешанными типами данных вы должны перебирать zip(df['A'], df['B'], ...) вместо df[['A', 'B']].to_numpy(), поскольку последний неявно преобразует данные в наиболее распространенный тип. Например, если A является числовым, а B - строкой, to_numpy() преобразует весь массив в строку, что может быть не тем, что вам нужно. К счастью, zip объединение столбцов вместе - самый простой способ решения этой проблемы.

* Ваш пробег может отличаться по причинам, указанным в разделе Предостережения выше.


Очевидный пример

Продемонстрируем разницу на простом примере добавления двух столбцов панд A + B. Это векторизуемый оператор, поэтому будет легко сравнить производительность методов, описанных выше.

для справки. Строка внизу измеряет функцию, написанную в numpandas, стиле Pandas, который сильно смешивается с NumPy, чтобы выжать максимальную производительность. Следует избегать написания кода numpandas, если вы не знаете, что делаете. Придерживайтесь API там, где это возможно (т. Е. Предпочитайте vec, а не vec_numpy).

Однако я должен упомянуть, что это не всегда так резко. Иногда ответ на вопрос, какой метод является лучшим для операции, зависит от ваших данных. Мой совет - протестировать разные подходы к своим данным, прежде чем останавливаться на одном.


Дальнейшее чтение

* Строковые методы Pandas векторизованы в том смысле, что они указаны в серии, но работают с каждым элементом. Базовые механизмы по-прежнему являются итеративными, потому что строковые операции по своей природе трудно векторизовать.


Почему я написал этот ответ

Обычная тенденция, которую я замечаю у новых пользователей, - это задавать вопросы в форме «Как я могу перебрать мой df, чтобы выполнить X?». Показан код, который вызывает iterrows() при выполнении чего-либо внутри цикла for. Вот почему. Новый пользователь библиотеки, не знакомый с концепцией векторизации, скорее всего, представит код, который решает их проблему, как итерацию по их данным, чтобы что-то сделать. Не зная, как перебирать DataFrame, первое, что они делают, - это Google и в конечном итоге здесь, на этом вопросе. Затем они видят принятый ответ, говорящий им, как это сделать, закрывают глаза и запускают этот код, даже не задавшись вопросом, является ли повторение правильным.

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

person cs95    schedule 07.04.2019
comment
Обратите внимание, что есть важные оговорки с iterrows и itertuples. См. этот ответ и pandas docs для получения дополнительных сведений. - person viddik13; 30.05.2019
comment
Это единственный ответ, который фокусируется на идиоматических методах, которые следует использовать с пандами, что делает его лучшим ответом на этот вопрос. Научиться получать правильный ответ с помощью правильного кода (вместо правильного ответ с неправильным кодом - т.е. неэффективным, не масштабируемым, слишком подходящим для определенных данных) - это большая часть изучения панд (и данные в целом). - person LinkBerest; 30.05.2019
comment
Я думаю, что вы несправедливо относитесь к циклу for, так как в моих тестах они лишь немного медленнее, чем понимание списка. Уловка состоит в том, чтобы перебрать zip(df['A'], df['B']) вместо df.iterrows(). - person Imperishable Night; 24.06.2019
comment
Хорошо, я понимаю, что вы говорите, но если мне нужно распечатать каждую строку (с числовыми данными) таблицы, отсортированную по возрастанию - я думаю, нет другого способа, кроме как перебирать строки, верно? - person sdbbs; 20.11.2019
comment
@sdbbs есть, используйте sort_values ​​для сортировки ваших данных, затем вызовите to_string () для результата. - person cs95; 20.11.2019
comment
В разделе «Составление списков» в примере с итерацией по нескольким столбцам необходимо сделать оговорку: DataFrame.values преобразует каждый столбец в общий тип данных. DataFrame.to_numpy() тоже делает то же самое. К счастью, мы можем использовать zip с любым количеством столбцов. - person David Wasserman; 16.01.2020
comment
@DavidWasserman, это фантастическое замечание, спасибо за ваши комментарии. В самом деле, это то, чего стоит остерегаться со смешанными столбцами, если вы сначала не конвертируете в объект (что, зачем вам)! - person cs95; 16.01.2020
comment
Интересно, поскольку iterrows, apply и понимание списка, похоже, имеют тенденцию к масштабируемости O (n), я бы избегал любых микрооптимизаций и выбрал наиболее удобочитаемую. Набор данных, слишком медленный при использовании любого метода, скорее всего, потребует времени, потраченного на поиск решения, отличного от Pandas, вместо того, чтобы пытаться сократить миллисекунды на for-цикле. - person c z; 29.01.2020
comment
@cz график логарифмический. Разница в производительности для больших наборов данных выражается в секундах и минутах, а не в миллисекундах. - person cs95; 29.01.2020
comment
Я знаю, что опаздываю на отвечающую сторону, но если вы конвертируете фрейм данных в массив numpy, а затем используете векторизацию, это даже быстрее, чем векторизация фреймов данных pandas (и это включает время, чтобы превратить его обратно в серию фреймов данных). Например: def np_vectorization(df): np_arr = df.to_numpy() return pd.Series(np_arr[:,0] + np_arr[:,1], index=df.index) И ... def just_np_vectorization(df): np_arr = df.to_numpy() return np_arr[:,0] + np_arr[:,1] - person bug_spray; 24.03.2020
comment
@AndreRicardo, почему бы не опубликовать это в ответе, где это станет более заметным? - person cs95; 24.03.2020
comment
На самом деле это то, что мне было трудно найти, идя по пути Google, описанному в ответе. Спасибо за это! - person Mike_K; 11.05.2020
comment
К сожалению, у некоторых из нас нет возможности последовать вашему предложению. Потому что некоторые библиотеки просто принудительно используют DataFrame без надобности. (Я здесь пытался перебрать паркетный файл в Python без Spark и преобразовать данные в JSON. И я вынужден использовать DataFrame) Если вы пишете библиотеки - пожалуйста, не забывайте не навязывать нам Pandas. - person Aleksandr Panzin; 21.05.2020
comment
@Dean Я получаю такой ответ довольно часто, и, честно говоря, меня это смущает. Все дело в формировании хороших привычек. Мои данные малы, а производительность не имеет значения, поэтому мое использование этого антипаттерна может быть оправдано ..? Когда однажды производительность действительно будет иметь значение, вы будете благодарить себя за то, что заранее подготовили нужные инструменты. - person cs95; 26.07.2020

Сначала подумайте, действительно ли вам нужно перебирать строки в DataFrame. Альтернативы см. В этом ответе.

Если вам все еще нужно перебирать строки, вы можете использовать методы, указанные ниже. Обратите внимание на некоторые важные предостережения, которые не упоминаются ни в одном из других ответов.

itertuples() должен быть быстрее, чем iterrows()

Но имейте в виду, согласно документации (на данный момент pandas 0.24.2):

  • # P7 #
    # P8 #
  • # P9 #
    # P10 #
    # P11 #
    new_df = df.apply(lambda x: x * 2)
    
  • # P12 #
    # P13 #

Дополнительные сведения см. В документации pandas по итерации.

person viddik13    schedule 07.12.2016
comment
Небольшой вопрос от кого-то, кто читает эту ветку так долго после ее завершения: как df.apply () сравнивается с itertuples с точки зрения эффективности? - person Raul Guarini; 26.01.2018
comment
Примечание: вы также можете сказать что-то вроде for row in df[['c1','c2']].itertuples(index=True, name=None):, чтобы включить в итератор строк только определенные столбцы. - person Brian Burns; 29.06.2018
comment
Вместо getattr(row, "c1") можно использовать просто row.c1. - person viraptor; 13.08.2018
comment
Я примерно на 90% уверен, что если вы используете getattr(row, "c1") вместо row.c1, вы потеряете любое преимущество в производительности itertuples, и если вам действительно нужно получить доступ к свойству через строку, вы должны вместо этого использовать iterrows. - person Noctiphobia; 24.08.2018
comment
Когда я попробовал это, он напечатал только значения столбцов, но не заголовки. Заголовки столбцов исключены из атрибутов строк? - person Marlo; 06.12.2018
comment
Я наткнулся на этот вопрос, потому что, хотя я знал, что есть split-apply-comb, мне все еще действительно нужно было перебирать DataFrame (как указано в вопросе). Не у всех есть возможность улучшить с помощью numba и cython (в тех же документах говорится, что сначала всегда стоит оптимизировать Python). Я написал этот ответ, чтобы помочь другим избежать (иногда разочаровывающих) проблем, поскольку ни один из других ответов не упоминает эти предостережения. Вводить кого-либо в заблуждение или говорить, что это правильно, никогда не входило в мои намерения. Я улучшил ответ. - person viddik13; 30.05.2019
comment
А что, если я хочу перебрать фрейм данных с размером шага больше 1, например выбрать только каждую 3-ю строку? Спасибо - person Confounded; 16.12.2019
comment
@Confounded Быстрый поиск в Google показывает, что вы можете использовать iloc для предварительной обработки фрейма данных: df.iloc[::5, :] предоставит вам каждую 5-ю строку. Дополнительные сведения см. В этом вопросе. - person viddik13; 17.12.2019
comment
К вашему сведению, ссылка «pandas docs on iteration» не работает. - person David Doria; 18.06.2021

Вам следует использовать df.iterrows(). Хотя итерация строка за строкой не особенно эффективна, поскольку необходимо создавать Series объектов.

person Wes McKinney    schedule 24.05.2012
comment
Это быстрее, чем преобразование DataFrame в массив numpy (через .values) и непосредственная работа с массивом? У меня такая же проблема, но в итоге я преобразовал ее в массив numpy, а затем использовал cython. - person vgoklani; 07.10.2012
comment
@vgoklani Если итерация строка за строкой неэффективна и у вас есть массив, не являющийся объектом numpy, то почти наверняка использование необработанного массива numpy будет быстрее, особенно для массивов с большим количеством строк. вам следует избегать итерации по строкам, если в этом нет крайней необходимости - person Phillip Cloud; 16.06.2013
comment
Я провел небольшое тестирование времени, затрачиваемого на df.iterrows (), df.itertuples () и zip (df ['a'], df ['b']), и опубликовал результат в ответе другого вопрос: stackoverflow.com/a/34311080/2142098 - person Richard Wong; 16.12.2015

Хотя iterrows() - хороший вариант, иногда itertuples() может быть намного быстрее:

df = pd.DataFrame({'a': randn(1000), 'b': randn(1000),'N': randint(100, 1000, (1000)), 'x': 'x'})

%timeit [row.a * 2 for idx, row in df.iterrows()]
# => 10 loops, best of 3: 50.3 ms per loop

%timeit [row[1] * 2 for row in df.itertuples()]
# => 1000 loops, best of 3: 541 µs per loop
person e9t    schedule 20.09.2015
comment
Большая часть разницы во времени в ваших двух примерах кажется, что это связано с тем, что вы, кажется, используете индексирование на основе меток для команды .iterrows () и индексирование на основе целых чисел для команды .itertuples (). - person Alex; 20.09.2015
comment
Для фрейма данных на основе финансовых данных (временная метка и 4-кратное число с плавающей запятой) itertuples в 19,57 раз быстрее, чем iterrows на моем компьютере. Только for a,b,c in izip(df["a"],df["b"],df["c"]: почти одинаково быстр. - person harbun; 19.10.2015
comment
Вы можете объяснить, почему это быстрее? - person Abe Miessler; 11.01.2017
comment
@AbeMiessler iterrows() помещает каждую строку данных в серию, а itertuples() - нет. - person miradulo; 13.02.2017
comment
Обратите внимание, что порядок столбцов на самом деле не определен, потому что df создается из словаря, поэтому row[1] может относиться к любому из столбцов. Как оказалось, время примерно одинаково для целочисленных столбцов и столбцов с плавающей запятой. - person Brian Burns; 05.11.2017
comment
@jeffhale, время, которое вы цитируете, точно такое же, как это возможно? Также я имел в виду что-то вроде row.iat [1], когда имел в виду целочисленное индексирование. - person Alex; 29.09.2018
comment
@Alex, это действительно выглядит подозрительно. Я просто повторил это несколько раз, и это заняло в 3 раза больше времени, чем ряд. С пандами 0.23.4. Удаляю другой комментарий, чтобы избежать путаницы. - person jeffhale; 29.09.2018
comment
Затем при запуске на гораздо большем DataFrame, больше похожем на реальную ситуацию, itertuples был в 100 раз быстрее, чем iterrows. Itertuples для победы. - person jeffhale; 29.09.2018
comment
Я получаю ›50-кратное увеличение i.stack.imgur.com/HBe9o.png (при переходе на метод доступа attr во втором запуске). - person Ajasja; 07.11.2018

Вы также можете использовать df.apply() для перебора строк и доступа к нескольким столбцам для функции.

документы: DataFrame.apply ()

def valuation_formula(x, y):
    return x * y * 0.5

df['price'] = df.apply(lambda row: valuation_formula(row['x'], row['y']), axis=1)
person cheekybastard    schedule 01.06.2015
comment
Обращается ли df ['price'] к имени столбца во фрейме данных? Я пытаюсь создать словарь с уникальными значениями из нескольких столбцов в файле csv. Я использовал вашу логику для создания словаря с уникальными ключами и значениями и получил сообщение об ошибке TypeError: (объекты "Series" изменяемы, поэтому они не могут быть хешированы, u'occurred at index 0 ') - person SRS; 01.07.2015
comment
Код: df ['Workclass'] = df.apply (лямбда-строка: dic_update (row), axis = 1) конец строки id = 0 конец строка def dic_update (row): если строка не в dic: dic [row] = id id = id + 1 - person SRS; 01.07.2015
comment
Неважно, я понял. Строка вызова функции изменена на df_new = df ['Workclass']. Apply (то же самое) - person SRS; 01.07.2015
comment
Значение по умолчанию для оси 0 - худшее - person zthomas.nc; 30.11.2017
comment
Обратите внимание, что apply не выполняет итерацию по строкам, а применяет функцию построчно. Приведенный выше код не будет работать, если вам действительно действительно нужны итерации и индексы, например, при сравнении значений в разных строках (в этом случае вы можете делать только итерацию). - person gented; 04.04.2018
comment
@gented ... где вы здесь увидели слово итератит? - person cs95; 29.06.2019
comment
это подходящий ответ для панд - person dhruvm; 25.07.2020

Вы можете использовать функцию df.iloc следующим образом:

for i in range(0, len(df)):
    print df.iloc[i]['c1'], df.iloc[i]['c2']
person PJay    schedule 07.09.2016
comment
Я знаю, что этого следует избегать в пользу iterrows или itertuples, но было бы интересно узнать, почему. Есть предположения? - person rocarvaj; 05.10.2017
comment
Это единственный известный мне допустимый метод, если вы хотите сохранить типы данных, а также ссылаться на столбцы по имени. itertuples сохраняет типы данных, но избавляется от любых имен, которые им не нравятся. iterrows делает наоборот. - person Ken Williams; 18.01.2018
comment
Потратил часы, пытаясь преодолеть особенности структур данных pandas, чтобы сделать что-то простое И выразительное. Это приводит к читаемому коду. - person Sean Anderson; 19.09.2018
comment
Хотя for i in range(df.shape[0]) может немного ускорить этот подход, он все равно примерно в 3,5 раза медленнее, чем подход iterrows (), описанный выше для моего приложения. - person Kim Miller; 14.12.2018
comment
На больших Datafrmes это кажется лучше, поскольку my_iter = df.itertuples() требует вдвое больше памяти и много времени для ее копирования. то же самое для iterrows(). - person Bastiaan; 04.01.2019

Как эффективно выполнять итерацию

Если вам действительно нужно выполнить итерацию фрейма данных Pandas, вы, вероятно, захотите избежать использования iterrows (). Есть разные методы, и обычный iterrows() далеко не лучший. itertuples () может работать в 100 раз быстрее.

Короче:

  • Как правило, используйте df.itertuples(name=None). В частности, когда у вас есть фиксированное количество столбцов и меньше 255 столбцов. См. пункт (3)
  • В противном случае используйте df.itertuples(), кроме случаев, когда в ваших столбцах есть специальные символы, такие как пробелы или '-'. См. пункт (2)
  • Можно использовать itertuples(), даже если в вашем фрейме данных есть странные столбцы, используя последний пример. См. пункт (4)
  • Используйте iterrows() только в том случае, если вы не можете использовать предыдущие решения. См. пункт (1)

Различные методы перебора строк в кадре данных Pandas:

Создайте случайный фрейм данных с миллионом строк и 4 столбцами:

    df = pd.DataFrame(np.random.randint(0, 100, size=(1000000, 4)), columns=list('ABCD'))
    print(df)

1) Обычный iterrows() удобно, но чертовски медленно:

start_time = time.clock()
result = 0
for _, row in df.iterrows():
    result += max(row['B'], row['C'])

total_elapsed_time = round(time.clock() - start_time, 2)
print("1. Iterrows done in {} seconds, result = {}".format(total_elapsed_time, result))

2) Значение по умолчанию itertuples() уже намного быстрее, но оно не работает с именами столбцов, такими как My Col-Name is very Strange (вам следует избегать этого метода, если ваши столбцы повторяются или если имя столбца не может быть просто преобразовано в имя переменной Python) .:

start_time = time.clock()
result = 0
for row in df.itertuples(index=False):
    result += max(row.B, row.C)

total_elapsed_time = round(time.clock() - start_time, 2)
print("2. Named Itertuples done in {} seconds, result = {}".format(total_elapsed_time, result))

3) Значение по умолчанию itertuples() с использованием name = None даже быстрее, но не очень удобно, поскольку вам нужно определять переменную для каждого столбца.

start_time = time.clock()
result = 0
for(_, col1, col2, col3, col4) in df.itertuples(name=None):
    result += max(col2, col3)

total_elapsed_time = round(time.clock() - start_time, 2)
print("3. Itertuples done in {} seconds, result = {}".format(total_elapsed_time, result))

4) Наконец, именованный itertuples() медленнее, чем предыдущий пункт, но вам не нужно определять переменную для каждого столбца, и он работает с именами столбцов, такими как My Col-Name is very Strange.

start_time = time.clock()
result = 0
for row in df.itertuples(index=False):
    result += max(row[df.columns.get_loc('B')], row[df.columns.get_loc('C')])

total_elapsed_time = round(time.clock() - start_time, 2)
print("4. Polyvalent Itertuples working even with special characters in the column name done in {} seconds, result = {}".format(total_elapsed_time, result))

Выход:

         A   B   C   D
0       41  63  42  23
1       54   9  24  65
2       15  34  10   9
3       39  94  82  97
4        4  88  79  54
...     ..  ..  ..  ..
999995  48  27   4  25
999996  16  51  34  28
999997   1  39  61  14
999998  66  51  27  70
999999  51  53  47  99

[1000000 rows x 4 columns]

1. Iterrows done in 104.96 seconds, result = 66151519
2. Named Itertuples done in 1.26 seconds, result = 66151519
3. Itertuples done in 0.94 seconds, result = 66151519
4. Polyvalent Itertuples working even with special characters in the column name done in 2.94 seconds, result = 66151519

Эта статья представляет собой очень интересное сравнение iterrows и iterrows

person Romain Capron    schedule 19.12.2019

Я искал Как перебирать строки и столбцы и закончил вот так:

for i, row in df.iterrows():
    for j, column in row.iteritems():
        print(column)
person Lucas B    schedule 17.01.2018
comment
По возможности следует избегать использования iterrows (). Я объясняю, почему в ответе Как итерация эффективно - person Romain Capron; 20.07.2020

Вы можете написать свой собственный итератор, реализующий namedtuple

from collections import namedtuple

def myiter(d, cols=None):
    if cols is None:
        v = d.values.tolist()
        cols = d.columns.values.tolist()
    else:
        j = [d.columns.get_loc(c) for c in cols]
        v = d.values[:, j].tolist()

    n = namedtuple('MyTuple', cols)

    for line in iter(v):
        yield n(*line)

Это напрямую сопоставимо с pd.DataFrame.itertuples. Я стремлюсь выполнять ту же задачу с большей эффективностью.


Для данного фрейма данных с моей функцией:

list(myiter(df))

[MyTuple(c1=10, c2=100), MyTuple(c1=11, c2=110), MyTuple(c1=12, c2=120)]

Или с pd.DataFrame.itertuples:

list(df.itertuples(index=False))

[Pandas(c1=10, c2=100), Pandas(c1=11, c2=110), Pandas(c1=12, c2=120)]

Комплексный тест
Мы тестируем доступность всех столбцов и подмножество столбцов.

def iterfullA(d):
    return list(myiter(d))

def iterfullB(d):
    return list(d.itertuples(index=False))

def itersubA(d):
    return list(myiter(d, ['col3', 'col4', 'col5', 'col6', 'col7']))

def itersubB(d):
    return list(d[['col3', 'col4', 'col5', 'col6', 'col7']].itertuples(index=False))

res = pd.DataFrame(
    index=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
    columns='iterfullA iterfullB itersubA itersubB'.split(),
    dtype=float
)

for i in res.index:
    d = pd.DataFrame(np.random.randint(10, size=(i, 10))).add_prefix('col')
    for j in res.columns:
        stmt = '{}(d)'.format(j)
        setp = 'from __main__ import d, {}'.format(j)
        res.at[i, j] = timeit(stmt, setp, number=100)

res.groupby(res.columns.str[4:-1], axis=1).plot(loglog=True);

введите описание изображения здесь

введите описание изображения здесь

person piRSquared    schedule 07.11.2017
comment
Для людей, которые не хотят читать код: синяя линия - intertuples, оранжевая линия - это список от итератора до блока yield. interrows не сравнивается. - person James L.; 01.12.2017

Чтобы зациклить все строки в dataframe, вы можете использовать:

for x in range(len(date_example.index)):
    print date_example['Date'].iloc[x]
person Pedro Lobito    schedule 11.03.2017
comment
Это цепная индексация. Я не рекомендую этого делать. - person cs95; 19.04.2019
comment
@ cs95 Что бы вы порекомендовали вместо этого? - person Pedro Lobito; 19.04.2019
comment
Если вы хотите, чтобы эта работа работала, вызовите df.columns.get_loc, чтобы получить целочисленную позицию индекса столбца даты (вне цикла), а затем используйте один вызов индексации iloc внутри. - person cs95; 19.04.2019

Иногда полезный паттерн:

# Borrowing @KutalmisB df example
df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]}, index=['a', 'b'])
# The to_dict call results in a list of dicts
# where each row_dict is a dictionary with k:v pairs of columns:value for that row
for row_dict in df.to_dict(orient='records'):
    print(row_dict)

Что приводит к:

{'col1':1.0, 'col2':0.1}
{'col1':2.0, 'col2':0.2}
person Zach    schedule 27.06.2018

Чтобы зациклить все строки в dataframe и использовать значения каждой строки, удобно, namedtuples можно преобразовать в ndarrays. Например:

df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]}, index=['a', 'b'])

Итерации по строкам:

for row in df.itertuples(index=False, name='Pandas'):
    print np.asarray(row)

приводит к:

[ 1.   0.1]
[ 2.   0.2]

Обратите внимание, что если index=True, индекс добавляется как первый элемент кортежа, что может быть нежелательно для некоторых приложений.

person Herpes Free Engineer    schedule 23.04.2018

И для просмотра, и для изменения значений я бы использовал iterrows(). В цикле for и с использованием распаковки кортежей (см. Пример: i, row) я использую row только для просмотра значения и использую i с методом loc, когда я хочу изменить значения. Как указано в предыдущих ответах, здесь вам не следует изменять то, что вы повторяете.

for i, row in df.iterrows():
    df_column_A = df.loc[i, 'A']
    if df_column_A == 'Old_Value':
        df_column_A = 'New_value'  

Здесь row в цикле - это копия этой строки, а не ее представление. Следовательно, вам НЕ следует писать что-то вроде row['A'] = 'New_Value', это не изменит DataFrame. Однако вы можете использовать i и loc и указать DataFrame для выполнения работы.

person Hossein    schedule 27.02.2019

Суммируя

  • Если возможно, используйте векторизацию
  • Если операция не может быть векторизована - используйте составные части списка.
  • Если вам нужен один объект, представляющий всю строку - используйте itertuples
  • Если это слишком медленно, попробуйте swifter.apply.
  • Если он по-прежнему слишком медленный, попробуйте процедуру Cython.

Контрольный показатель

Контрольный показатель итерации по строкам в фрейме данных Pandas

person artoby    schedule 01.06.2020

Есть способ перебрать строки throw, получая взамен DataFrame, а не Series. Я не вижу, чтобы кто-нибудь упоминал, что вы можете передать index как список для строки, которая должна быть возвращена как DataFrame:

for i in range(len(df)):
    row = df.iloc[[i]]

Обратите внимание на использование двойных скобок. Это возвращает DataFrame с одной строкой.

person Zeitgeist    schedule 17.10.2019
comment
Это было очень полезно для получения n-й по величине строки во фрейме данных после сортировки. Спасибо! - person Jason Harrison; 03.12.2019

cs95 показывает, что Pandas векторизация намного превосходит другие методы Pandas для вычислений с фреймами данных.

Я хотел добавить, что если вы сначала конвертируете фрейм данных в массив NumPy, а затем используете векторизацию, это даже быстрее, чем векторизация фрейма данных Pandas (и это включает время, чтобы превратить его обратно в серию фреймов данных).

Если вы добавите следующие функции в тестовый код cs95, это станет довольно очевидным:

def np_vectorization(df):
    np_arr = df.to_numpy()
    return pd.Series(np_arr[:,0] + np_arr[:,1], index=df.index)

def just_np_vectorization(df):
    np_arr = df.to_numpy()
    return np_arr[:,0] + np_arr[:,1]

Введите здесь описание изображения

person bug_spray    schedule 24.03.2020

Есть так много способов перебирать строки в фрейме данных Pandas. Один очень простой и интуитивно понятный способ:

df = pd.DataFrame({'A':[1, 2, 3], 'B':[4, 5, 6], 'C':[7, 8, 9]})
print(df)
for i in range(df.shape[0]):
    # For printing the second column
    print(df.iloc[i, 1])

    # For printing more than one columns
    print(df.iloc[i, [0, 2]])
person shubham ranjan    schedule 19.01.2019

Самый простой способ - использовать функцию apply

def print_row(row):
   print row['c1'], row['c2']

df.apply(lambda row: print_row(row), axis=1)
person François B.    schedule 02.11.2020

Вы также можете выполнить индексацию NumPy для еще большего ускорения. Это не совсем итерация, но работает намного лучше, чем итерация для определенных приложений.

subset = row['c1'][0:5]
all = row['c1'][:]

Вы также можете преобразовать его в массив. Предполагается, что эти индексы / выборки уже действуют как массивы NumPy, но у меня возникли проблемы, и мне нужно было использовать

np.asarray(all)
imgs[:] = cv2.resize(imgs[:], (224,224) ) # Resize every image in an hdf5 file
person James L.    schedule 01.12.2017

В этом примере iloc используется для выделения каждой цифры во фрейме данных.

import pandas as pd

 a = [1, 2, 3, 4]
 b = [5, 6, 7, 8]

 mjr = pd.DataFrame({'a':a, 'b':b})

 size = mjr.shape

 for i in range(size[0]):
     for j in range(size[1]):
         print(mjr.iloc[i, j])
person mjr2000    schedule 16.03.2019

Некоторые библиотеки (например, библиотека взаимодействия Java, которую я использую) требуют, чтобы значения передавались по очереди, например, при потоковой передаче данных. Чтобы воспроизвести природу потоковой передачи, я «транслирую» свои значения фрейма данных одно за другим, я написал ниже, что время от времени может пригодиться.

class DataFrameReader:
  def __init__(self, df):
    self._df = df
    self._row = None
    self._columns = df.columns.tolist()
    self.reset()
    self.row_index = 0

  def __getattr__(self, key):
    return self.__getitem__(key)

  def read(self) -> bool:
    self._row = next(self._iterator, None)
    self.row_index += 1
    return self._row is not None

  def columns(self):
    return self._columns

  def reset(self) -> None:
    self._iterator = self._df.itertuples()

  def get_index(self):
    return self._row[0]

  def index(self):
    return self._row[0]

  def to_dict(self, columns: List[str] = None):
    return self.row(columns=columns)

  def tolist(self, cols) -> List[object]:
    return [self.__getitem__(c) for c in cols]

  def row(self, columns: List[str] = None) -> Dict[str, object]:
    cols = set(self._columns if columns is None else columns)
    return {c : self.__getitem__(c) for c in self._columns if c in cols}

  def __getitem__(self, key) -> object:
    # the df index of the row is at index 0
    try:
        if type(key) is list:
            ix = [self._columns.index(key) + 1 for k in key]
        else:
            ix = self._columns.index(key) + 1
        return self._row[ix]
    except BaseException as e:
        return None

  def __next__(self) -> 'DataFrameReader':
    if self.read():
        return self
    else:
        raise StopIteration

  def __iter__(self) -> 'DataFrameReader':
    return self

Что можно использовать:

for row in DataFrameReader(df):
  print(row.my_column_name)
  print(row.to_dict())
  print(row['my_column_name'])
  print(row.tolist())

И сохраняет сопоставление значений / имен для повторяемых строк. Очевидно, это намного медленнее, чем использование apply и Cython, как указано выше, но в некоторых случаях это необходимо.

person morganics    schedule 10.12.2019

Наряду с отличными ответами в этом посте я собираюсь предложить подход Разделяй и властвуй, я пишу этот ответ не для отмены других отличных ответов, а для их выполнения с помощью другого подхода, который эффективно работал для меня. . Он состоит из двух шагов splitting и merging фрейма данных pandas:

ПЛЮСЫ «Разделяй и властвуй»:

  • Вам не нужно использовать векторизацию или какие-либо другие методы для преобразования типа вашего фрейма данных в другой тип.
  • Вам не нужно цитонизировать свой код, что обычно требует от вас дополнительного времени.
  • И iterrows(), и itertuples() в моем случае имели одинаковую производительность по всему фрейму данных
  • В зависимости от вашего выбора нарезки index вы сможете экспоненциально ускорить итерацию. Чем выше index, тем быстрее будет ваш процесс итерации.

МИНУСЫ «Разделяй и властвуй»:

  • Вы не должны зависеть в процессе итерации от одного и того же фрейма данных и другого среза. Это означает, что если вы хотите читать или писать из другого фрагмента, это может быть сложно сделать.

=================== Подход "разделяй и властвуй" =================

Шаг 1. Разделение / нарезка

На этом этапе мы собираемся разделить итерацию на весь фрейм данных. Подумайте, что вы собираетесь прочитать файл csv в pandas df, а затем перебрать его. На всякий случай у меня есть 5 000 000 записей, и я собираюсь разделить их на 100 000 записей.

ПРИМЕЧАНИЕ. Мне нужно повторить, как в другом анализе времени выполнения, описанном в других решениях на этой странице, количество записей имеет экспоненциальную долю времени выполнения при поиске в df. Вот результаты, основанные на тесте на моих данных:

Number of records | Iteration per second
========================================
100,000           | 500 it/s
500,000           | 200 it/s
1,000,000         | 50 it/s
5,000,000         | 20 it/s

Шаг 2. Объединение

Это будет простой шаг, просто объедините все записанные файлы csv в один фрейм данных и запишите его в более крупный файл csv.

Вот пример кода:

# Step 1 (Splitting/Slicing)
import pandas as pd
df_all = pd.read_csv('C:/KtV.csv')
df_index = 100000
df_len = len(df)
for i in range(df_len // df_index + 1):
    lower_bound = i * df_index 
    higher_bound = min(lower_bound + df_index, df_len)
    # splitting/slicing df (make sure to copy() otherwise it will be a view
    df = df_all[lower_bound:higher_bound].copy()
    '''
    write your iteration over the sliced df here
    using iterrows() or intertuples() or ...
    '''
    # writing into csv files
    df.to_csv('C:/KtV_prep_'+str(i)+'.csv')



# Step 2 (Merging)
filename='C:/KtV_prep_'
df = (pd.read_csv(f) for f in [filename+str(i)+'.csv' for i in range(ktv_len // ktv_index + 1)])
df_prep_all = pd.concat(df)
df_prep_all.to_csv('C:/KtV_prep_all.csv')

Ссылка:

Эффективный способ итерации над datafreame

Объедините файлы csv в один фрейм данных Pandas

person imanzabet    schedule 02.10.2020

Как многие ответы здесь правильно и четко указывают, вы обычно не должны пытаться зацикливаться в Pandas, а должны писать векторизованный код. Но остается вопрос, должны ли вы когда-либо писать циклы в Pandas, и если да, то как лучше всего использовать цикл в таких ситуациях.

Я считаю, что существует по крайней мере одна общая ситуация, когда циклы уместны: когда вам нужно вычислить некоторую функцию, которая зависит от значений в других строках несколько сложным образом. В этом случае код цикла часто проще, удобнее для чтения и меньше подвержен ошибкам, чем векторизованный код. Код цикла может быть даже быстрее.

Я попытаюсь показать это на примере. Предположим, вы хотите получить кумулятивную сумму столбца, но сбрасывать ее всякий раз, когда какой-либо другой столбец равен нулю:

import pandas as pd
import numpy as np

df = pd.DataFrame( { 'x':[1,2,3,4,5,6], 'y':[1,1,1,0,1,1]  } )

#   x  y  desired_result
#0  1  1               1
#1  2  1               3
#2  3  1               6
#3  4  0               4
#4  5  1               9
#5  6  1              15

Это хороший пример, в котором вы, безусловно, могли бы написать одну строку Pandas для достижения этой цели, хотя она не особенно удобочитаема, особенно если у вас еще нет опыта работы с Pandas:

df.groupby( (df.y==0).cumsum() )['x'].cumsum()

Это будет достаточно быстро для большинства ситуаций, хотя вы также можете написать более быстрый код, избегая groupby, но он, вероятно, будет еще менее читабельным.

Или что, если мы напишем это в виде цикла? С NumPy вы можете сделать что-то вроде следующего:

import numba as nb

@nb.jit(nopython=True)  # Optional
def custom_sum(x,y):
    x_sum = x.copy()
    for i in range(1,len(df)):
        if y[i] > 0: x_sum[i] = x_sum[i-1] + x[i]
    return x_sum

df['desired_result'] = custom_sum( df.x.to_numpy(), df.y.to_numpy() )

По общему признанию, там есть небольшие накладные расходы, необходимые для преобразования столбцов DataFrame в массивы NumPy, но основной фрагмент кода - это всего лишь одна строка кода, которую вы могли бы прочитать, даже если вы ничего не знали о Pandas или NumPy:

if y[i] > 0: x_sum[i] = x_sum[i-1] + x[i]

И этот код на самом деле быстрее, чем векторизованный код. В некоторых быстрых тестах со 100 000 строк это примерно в 10 раз быстрее, чем подход groupby. Обратите внимание, что одним из ключей к скорости является numba, который не является обязательным. Без строки @ nb.jit код цикла на самом деле примерно в 10 раз медленнее, чем подход groupby.

Очевидно, что этот пример достаточно прост, и вы, вероятно, предпочтете одну строку панд написанию цикла со связанными с ним накладными расходами. Однако есть более сложные версии этой проблемы, для которых удобочитаемость или скорость подхода цикла NumPy / numba, вероятно, имеет смысл.

person JohnE    schedule 21.12.2020

Используйте 1_. Например, используя dataframe 'rows_df':

Введите здесь описание изображения

Or

Чтобы получить значения из определенной строки, вы можете преобразовать фрейм данных в ndarray.

Затем выберите значения строки и столбца следующим образом:

Введите здесь описание изображения

person dna-data    schedule 04.03.2021
comment
Рассмотрите возможность размещения кода не в изображениях, а в виде текста в блоке кода. - person Scratte; 07.03.2021

Просто добавляю свои два цента,

Как говорится в принятом ответе, самый быстрый способ применить функцию к строкам - это использовать векторизованную функцию, так называемые numpy ufuncs (универсальные функции)

Но что делать, если функция, которую вы хотите применить, еще не реализована в numpy?

Хорошо, используя декоратор vectorize из numba, вы можете легко создавать ufuncs прямо в Python следующим образом:

from numba import vectorize, float64

@vectorize([float64(float64)])
def f(x):
    #x is your line, do something with it, and return a float

Документация по этой функции находится здесь: https://numba.pydata.org/numba-doc/latest/user/vectorize.html

person Nephanth    schedule 26.05.2021

df.iterrows () возвращает кортеж (a, b), где a - индекс, а b - строка.

person Ashvani Jaiswal    schedule 03.07.2021

person    schedule
comment
какова производительность этого параметра при использовании в большом фрейме данных (например, миллионы строк)? - person Bazyli Debowski; 10.09.2018
comment
Честно говоря, точно не знаю, думаю, что по сравнению с лучшим ответом затраченное время будет примерно одинаковым, потому что в обоих случаях используется for-construction. Но память в некоторых случаях может отличаться. - person Grag2015; 25.10.2018
comment
Это цепная индексация. Не используйте это! - person cs95; 19.04.2019