팬더 데이터 프레임이 있습니다 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():
for row in df.iterrows():
row
그러나 개체가 무엇이며 어떻게 작업할 수 있는지 이해하지 못합니다 .
DataFrame.iterrows
인덱스와 행(시리즈)을 모두 생성하는 생성기입니다.
import pandas as pd
df = pd.DataFrame({'c1': [10, 11, 12], 'c2': [100, 110, 120]})
df = df.reset_index()
for index, row in df.iterrows():
print(row['c1'], row['c2'])
10 100
11 110
12 120
Pandas에서 DataFrame의 행을 반복하는 방법은 무엇입니까?
답변: 하지 마세요 * !
Pandas의 반복은 안티 패턴이며 다른 모든 옵션을 소진했을 때만 수행해야 하는 것입니다. 이름에 " "가 포함된 함수는 수천 행이 넘는 행에 대해 사용해서는 안 됩니다. 그렇지 않으면 많은 기다림 iter
에 익숙해 져야 합니다.
DataFrame을 인쇄하시겠습니까? 사용 DataFrame.to_string()
.
계산을 하시겠습니까? 이 경우 다음 순서로 메서드를 검색합니다( 여기 에서 수정된 목록 ).
- 벡터화
- 사이썬 루틴
- 목록 이해(바닐라
for
루프)
DataFrame.apply()
: i) Cython에서 수행할 수 있는 축소, ii) Python 공간에서 반복
DataFrame.itertuples()
그리고iteritems()
DataFrame.iterrows()
iterrows
그리고 itertuples
(둘 다 이 질문에 대한 답변에서 많은 표를 얻음) 순차 처리를 위한 행 객체/네임튜플을 생성하는 것과 같이 매우 드문 상황에서 사용해야 합니다.
권위에 호소
반복 문서 페이지 에는 다음과 같은 거대한 빨간색 경고 상자가 있습니다.
pandas 객체를 반복하는 것은 일반적으로 느립니다. 많은 경우 행을 수동으로 반복할 필요가 없습니다[...].
* 실제로는 "하지마"보다 조금 더 복잡합니다. df.iterrows()
이 질문에 대한 정답이지만 "작업을 벡터화"하는 것이 더 좋습니다. 반복을 피할 수 없는 상황이 있음을 인정합니다(예: 결과가 이전 행에 대해 계산된 값에 따라 달라지는 일부 작업). 그러나 언제인지 알기 위해서는 라이브러리에 어느 정도 익숙해야 합니다. 반복 솔루션이 필요한지 여부가 확실하지 않은 경우에는 그렇지 않을 수 있습니다. 추신: 이 답변을 작성하는 이유에 대해 자세히 알아보려면 맨 아래로 건너뜁니다.
많은 기본 연산과 계산이 팬더에 의해 "벡터화"됩니다(NumPy 또는 Cythonized 함수를 통해). 여기에는 산술, 비교, (대부분) 축소, 형태 변경(예: 피벗), 조인 및 그룹별 연산이 포함됩니다. 필수 기본 기능 에 대한 문서를 살펴보고 문제에 적합한 벡터화 방법을 찾으십시오.
존재하지 않는 경우 사용자 정의 Cython 확장 을 사용하여 자유롭게 작성하십시오 .
1) 사용 가능한 벡터화된 솔루션이 없는 경우, 2) 성능이 중요하지만 코드를 암호화하는 번거로움을 겪을 만큼 중요하지 않은 경우, 3) 요소별 변환을 수행하려는 경우 목록 이해가 다음 호출 포트여야 합니다. 당신의 코드에. 목록 이해 가 많은 일반적인 Pandas 작업에 대해 충분히 빠릅니다(때로는 더 빠릅니다).
공식은 간단하고,
result = [f(x) for x in df['col']]
result = [f(x, y) for x, y in zip(df['col1'], df['col2'])]
result = [f(row[0], ..., row[n]) for row in df[['col1', ...,'coln']].to_numpy()]
result = [f(row[0], ..., row[n]) for row in zip(df['col1'], ..., df['coln'])]
비즈니스 로직을 함수로 캡슐화할 수 있다면 이를 호출하는 목록 이해를 사용할 수 있습니다. 원시 Python 코드의 단순성과 속도를 통해 임의로 복잡한 작업을 수행할 수 있습니다.
주의 사항
목록 이해는 데이터가 작업하기 쉽다고 가정합니다. 즉, 데이터 유형이 일관되고 NaN이 없지만 이것이 항상 보장되는 것은 아닙니다.
- 첫 번째 것이 더 분명하지만 NaN을 처리할 때 내장된 pandas 메서드가 있는 경우 이를 선호하거나(훨씬 더 나은 모서리 처리 논리를 가지고 있기 때문에) 비즈니스 논리에 적절한 NaN 처리 논리가 포함되어 있는지 확인하십시오.
- 혼합 데이터 유형을 다룰 때는 후자가 암시적으로 데이터를 가장 일반적인 유형으로 업캐스트하는
zip(df['A'], df['B'], ...)
대신 반복해야 합니다. df[['A', 'B']].to_numpy()
예를 들어 A가 숫자이고 B가 문자열인 to_numpy()
경우 전체 배열을 문자열로 캐스트합니다. 이는 원하는 것이 아닐 수 있습니다. 다행히 zip
열을 함께 ping하는 것이 이에 대한 가장 간단한 해결 방법입니다.
*마일리지는 위의 주의 사항 섹션 에 설명된 이유로 인해 달라질 수 있습니다 .
명백한 예
두 개의 pandas 열을 추가하는 간단한 예를 통해 차이점을 보여드리겠습니다 A + B
. 이것은 벡터화 가능한 연산이므로 위에서 논의한 방법의 성능을 쉽게 대조할 수 있습니다.
참조용으로 벤치마킹 코드 . 맨 아래 줄은 최대 성능을 짜내기 위해 NumPy와 많이 혼합되는 Pandas 스타일인 Numpandas로 작성된 함수를 측정합니다. 당신이 무엇을 하고 있는지 알지 못하는 한 numandas 코드를 작성하는 것은 피해야 합니다. 가능한 곳에서 API를 고수하십시오(즉, 보다는 선호 vec
) vec_numpy
.
그러나 항상 이렇게 잘리고 건조한 것은 아니라는 점을 언급해야 합니다. 때때로 "가장 좋은 작업 방법은 무엇입니까?"에 대한 대답은 "데이터에 따라 다릅니다."입니다. 제 조언은 하나에 정착하기 전에 데이터에 대한 다양한 접근 방식을 테스트하는 것입니다.
내 개인적인 의견 *
iter family에 대한 다양한 대안에 대해 수행된 대부분의 분석은 성능의 렌즈를 통해 이루어졌습니다. 그러나 대부분의 상황에서 일반적으로 합리적인 크기의 데이터 세트(수천 또는 100K 행을 초과하지 않음)에서 작업하게 되며 성능은 솔루션의 단순성/가독성 다음으로 중요합니다.
다음은 문제에 사용할 방법을 선택할 때 개인적인 선호도입니다.
초보자의 경우:
벡터화 (가능한 경우) ; apply()
; 목록 이해; itertuples()
/ iteritems()
; iterrows()
; 사이썬
더 많은 경험을 위해:
벡터화 (가능한 경우) ; apply()
; 목록 이해; 사이톤; itertuples()
/ iteritems()
;iterrows()
벡터화는 벡터화할 수 있는 모든 문제에 대한 가장 관용적인 방법입니다. 항상 벡터화를 추구하십시오! 확실하지 않은 경우 문서를 참조하거나 특정 작업에 대한 기존 질문에 대해 Stack Overflow를 확인하세요.
나는 많은 내 게시물에서 얼마나 나쁜지에 대해 계속하는 경향이 apply
있지만 초보자가 그것이하고있는 일에 대해 머리를 감싸는 것이 더 쉽다는 것을 인정합니다. 또한 이 게시물에apply
설명된 에 대한 몇 가지 사용 사례가 있습니다 .
Cython은 올바르게 수행하는 데 더 많은 시간과 노력이 필요하기 때문에 목록에서 더 낮은 순위를 기록했습니다. 일반적으로 목록 이해도 충족할 수 없는 이러한 수준의 성능을 요구하는 pandas로 코드를 작성할 필요가 없습니다.
* 개인의견과 마찬가지로 소금과 함께 섭취해주세요!
추가 읽기
* Pandas 문자열 메서드는 시리즈에 지정되지만 각 요소에서 작동한다는 의미에서 "벡터화"됩니다. 문자열 연산은 본질적으로 벡터화하기 어렵기 때문에 기본 메커니즘은 여전히 반복적입니다.
내가 이 답변을 쓴 이유
내가 새로운 사용자에게서 발견한 일반적인 경향은 "X를 수행하기 위해 내 df를 어떻게 반복할 수 있습니까?" 형식의 질문을 하는 것입니다. 루프 iterrows()
내에서 작업을 수행하는 동안 호출하는 코드를 표시합니다 . for
여기에 이유가 있습니다. 벡터화 개념을 접하지 않은 라이브러리의 새로운 사용자는 문제를 해결하는 코드를 데이터를 반복하여 무언가를 수행하는 것으로 상상할 것입니다. DataFrame을 반복하는 방법을 모르기 때문에 그들이 가장 먼저 하는 일은 Google에서 수행하는 것이며 이 질문에서 여기까지 옵니다. 그런 다음 그들은 방법을 알려주는 허용된 답변을 보고 눈을 감고 반복이 올바른 일인지 먼저 묻지 않고 이 코드를 실행합니다.
이 답변의 목적은 새로운 사용자가 반복이 반드시 모든 문제에 대한 해결책은 아니며 더 좋고 빠르며 관용적인 솔루션이 존재할 수 있으며 이를 탐색하는 데 시간을 투자할 가치가 있음을 이해하도록 돕는 것입니다. 나는 반복과 벡터화의 전쟁을 시작하려는 것이 아니지만 이 라이브러리의 문제에 대한 솔루션을 개발할 때 새로운 사용자에게 정보를 제공하기를 바랍니다.
먼저 DataFrame의 행에 대해 실제로 반복 해야 하는지 여부를 고려하십시오 . 대안 은 이 답변 을 참조하십시오 .
여전히 행을 반복해야 하는 경우 아래 방법을 사용할 수 있습니다. 다른 답변에서 언급되지 않은 몇 가지 중요한 주의 사항 에 유의하십시오.
itertuples()
보다 빠르다고 합니다.iterrows()
그러나 문서(현재 pandas 0.24.2)에 따르면 주의하십시오.
- iterrows:
dtype
행마다 일치하지 않을 수 있습니다 .
iterrows는 각 행에 대해 Series를 반환하기 때문에 행 전체에서 dtype을 유지하지 않습니다 (dtype은 DataFrame의 열 전체에서 유지됨). 행을 반복하는 동안 dtypes를 유지하려면 값의 명명된 튜플을 반환하고 일반적으로 iterrows()보다 훨씬 빠른 itertuples()를 사용하는 것이 좋습니다.
반복 중인 항목을 수정 해서는 안 됩니다. 모든 경우에 작동하는 것은 아닙니다. 데이터 유형에 따라 iterator는 뷰가 아닌 복사본을 반환하며 그것에 쓰는 것은 효과가 없습니다.
대신 DataFrame.apply() 를 사용하십시오 .
new_df = df.apply(lambda x: x * 2, axis = 1)
열 이름은 유효하지 않은 Python 식별자이거나 반복되거나 밑줄로 시작하는 경우 위치 이름으로 바뀝니다. 많은 수의 열(>255)에서는 일반 튜플이 반환됩니다.
자세한 내용 은 반복에 대한 pandas 문서를 참조하세요.