programing

팬더에서 데이터 프레임의 행에 걸쳐 반복하는 방법

topblog 2023. 11. 4. 09:49
반응형

팬더에서 데이터 프레임의 행에 걸쳐 반복하는 방법

팬더 데이터 프레임이 있는데,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()  # make sure indexes pair with number of rows

for index, row in df.iterrows():
    print(row['c1'], row['c2'])
10 100
11 110
12 120

문서의 의무적 면책 사항

팬더의 물체를 반복하는 것은 일반적으로 느립니다. 많은 경우 간에 수동으로 반복할 필요가 없으며 다음 방법 중 하나를 사용하면 방지할 수 있습니다.

  • 벡터화된 솔루션을 찾으십시오. 내장된 메서드 또는 NumPy 함수, (부울) 인덱싱 등을 사용하여 많은 작업을 수행할 수 있습니다.
  • 전체 DataFrame/Series에 대해 한 번에 작업할 수 없는 함수가 있는 경우 값을 반복하는 대신 apply()를 사용하는 것이 좋습니다.기능 적용에 관한 문서를 참조합니다.
  • 값을 반복적으로 조작해야 하지만 성능이 중요한 경우 cyton 또는 numba로 내부 루프를 작성하는 것을 고려합니다.이 접근 방식의 몇 가지 예는 성능 향상 섹션을 참조하십시오.

이 스레드의 다른 답변에서는 자세한 내용을 알고 싶을 경우 iter* 기능을 대체할 수 있는 방법에 대해 자세히 설명합니다.

팬더에서 데이터 프레임의 행에 걸쳐 반복하는 방법

정답: 하지* 마!

팬더에서의 반복은 안티패턴이며 다른 모든 옵션을 다 사용했을 때만 수행해야 하는 작업입니다."와 함께 어떤 함수도 사용해서는 안 됩니다.iter" 몇천 줄이 넘는 줄에 대한 그것의 이름으로, 그렇지 않으면 당신은 많은 기다림에 익숙해져야 할 것입니다.

데이터 프레임을 인쇄하시겠습니까?사용.

계산하고 싶은 게 있습니까?이 경우 다음 순서로 메서드를 검색합니다(여기서 수정한 목록).

  1. 벡터화
  2. 싸이톤 루틴
  3. 목록 요약(vanilla)for루프)
  4. DataFrame.apply()DataFrame.apply()i) Cython에서 수행할 수 있는 축소, ii) Python 공간에서의 반복
  5. items() iteritems() (v1.5.0 이후 deprec)
  6. DataFrame.itertuples()
  7. DataFrame.iterrows()

iterrows그리고.itertuples(둘 다 이 질문에 대한 답변에서 많은 표를 받고 있음) 순차적 처리를 위해 행 객체/네임 튜플을 생성하는 등 매우 드문 경우에 사용해야 합니다. 이 기능이 실제로 유일하게 유용합니다.

권한에 호소

반복에 대한 설명서 페이지에는 다음과 같은 빨간색 경고 상자가 있습니다.

팬더의 물체를 반복하는 것은 일반적으로 느립니다.많은 경우 행에서 수동으로 반복할 필요가 없습니다 [...].

* 사실은 "하지마" 보다 좀 더 복잡합니다.df.iterrows()이 질문에 대한 정답이지만 "작전을 vector화"하는 것이 더 좋습니다.반복을 피할 수 없는 상황(예를 들어, 결과가 이전 행에 대해 계산된 값에 의존하는 일부 작업)이 있음을 인정하겠습니다.하지만 언제가 될지 알기 위해서는 도서관에 어느 정도 익숙함이 필요합니다.반복적인 솔루션이 필요한지 확신할 수 없다면 아마 필요하지 않을 것입니다.추신: 이 답변을 작성하는 이유에 대해 자세히 알고 싶다면 맨 아래로 건너뜁니다.


루프보다 빠름:벡터화, 사이톤

수많은 기본 연산과 계산이 팬더에 의해 (NumPy 또는 Cytonized 함수를 통해) "벡터화"됩니다.여기에는 산술, 비교, (대부분) 축소, 형상 변경(예: 피벗), 조인 및 작업별 그룹화 등이 포함됩니다.필수 기본 기능에 대한 설명서를 검토하여 문제에 적합한 벡터화된 방법을 찾으십시오.

없는 경우 사용자 지정 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'])]

비즈니스 로직을 함수에 캡슐화할 수 있다면 이를 호출하는 목록 이해를 사용할 수 있습니다.원시 파이썬 코드의 단순성과 속도를 통해 임의로 복잡한 작업을 수행할 수 있습니다.

주의사항

목록에 대한 이해는 사용자의 데이터가 작업하기 쉽다고 가정합니다. 즉, 데이터 유형이 일관되고 NaN이 없다는 것을 의미하지만 항상 보장할 수는 없습니다.

  1. 첫 번째 방법이 더 명확하지만 NaN을 처리할 때는 내장 팬더 방법이 존재하는 경우(코너 케이스 처리 로직이 훨씬 우수하기 때문에) 내장 팬더 방법을 선호하거나 비즈니스 로직에 적절한 NaN 처리 로직이 포함되도록 해야 합니다.
  2. 혼합 데이터 유형을 처리할 때는 다음과 같이 반복해야 합니다.zip(df['A'], df['B'], ...)대신에df[['A', 'B']].to_numpy()후자가 가장 일반적인 유형으로 암묵적으로 데이터를 업로드하기 때문입니다.예를 들어 A가 숫자이고 B가 문자열인 경우,to_numpy()전체 배열을 문자열로 캐스트합니다. 원하는 것이 아닐 수도 있습니다.다행히도.zip열을 함께 ping하는 것이 가장 간단한 해결 방법입니다.

*위의 주의 사항 섹션에 설명된 이유로 마일리지가 변동될 수 있습니다.


명백한 예

두 개의 팬더 열을 추가하는 간단한 예로 차이점을 설명해 보겠습니다.A + B. 이것은 벡터화 가능한 연산이므로 위에서 설명한 방법의 성능을 비교하기 쉽습니다.

참고로 벤치마킹 코드입니다.아래에 있는 선은 최대 성능을 짜내기 위해 NumPy와 많이 섞이는 판다 스타일인 NumPandas로 쓰여진 기능을 측정합니다.당신이 무엇을 하고 있는지 알지 못하는 한 numpandas 코드를 쓰는 것은 피해야 합니다.가능한 API를 고수합니다(즉, 선호).vec위에vec_numpy).

하지만 항상 이렇게 잘리고 건조한 것은 아니라는 점을 말씀드려야겠습니다.때때로 "작업에 가장 적합한 방법은 무엇인가"에 대한 답은 "데이터에 따라 다릅니다.제 조언은 데이터를 결정하기 전에 데이터에 대한 다양한 접근 방식을 테스트해 보는 것입니다.


나의 개인 의견

이터 패밀리의 다양한 대안에 대해 수행된 대부분의 분석은 성능의 렌즈를 통해 이루어졌습니다.그러나 대부분의 상황에서는 일반적으로 합리적인 크기의 데이터셋(수천 행 또는 10만 행을 넘지 않음)에서 작업하게 되며, 성능은 솔루션의 단순성/가독성에 뒤지지 않습니다.

문제에 사용할 방법을 선택할 때 개인적으로 선호하는 방법은 다음과 같습니다.

초보자의 경우:

벡터화(가능한 경우); 목록 이해; /;iteritems() Cython

더 경험이 풍부한 경험이 필요합니다.

벡터화(가능한 경우); 목록 이해; Cython; /;iteritems()

벡터화는 벡터화할 수 있는 모든 문제에 대해 가장 관용적인 방법으로 널리 사용됩니다.항상 벡터화를 추구하세요!의심스러운 경우 문서를 참조하거나 스택 오버플로에서 특정 작업에 대한 기존 질문을 확인합니다.

나는 얼마나 나쁜지에 대해 말하는 경향이 있습니다.apply제 게시물에 많이 올라와 있지만 초보자가 하는 일에 대해 머리를 싸매는 것이 더 쉽다는 것을 인정합니다.또한, 에 대한 사용 사례가 상당히 많습니다.apply이 게시물에서 설명했습니다.

Cython은 정확하게 수행하는 데 더 많은 시간과 노력이 필요하기 때문에 목록에서 더 낮은 순위를 차지합니다.여러분은 보통 목록 이해력으로도 만족할 수 없는 수준의 성능을 요구하는 팬더와 코드를 작성할 필요가 없을 것입니다.

* 어떤 개인적인 의견이든 간에, 부디 많은 소금을 가지고 가시기 바랍니다!


더보기

* 팬더 스트링 방법은 시리즈에 지정되어 있지만 각 요소에 대해 작동한다는 점에서 "벡터화"됩니다.문자열 연산은 본질적으로 벡터화하기 어렵기 때문에 기본 메커니즘은 여전히 반복적입니다.


내가 이 답변을 쓴 이유

제가 새로운 사용자들에게 주목하는 일반적인 경향은 "어떻게 하면 제 df to do X를 반복할 수 있을까요?"라는 형태의 질문을 하는 것입니다.호출하는 코드 표시iterrows()안에서 무엇인가를 하면서for루프. 이게 그 이유입니다.벡터화의 개념을 아직 접하지 못한 라이브러리의 새로운 사용자는 무엇인가를 하기 위해 데이터를 반복하면서 문제를 해결하는 코드를 상상할 것입니다.데이터 프레임을 반복하는 방법을 모르기 때문에 가장 먼저 구글에서 검색하여 이 질문을 받게 됩니다.그러면 그들은 어떻게 해야 하는지를 알려주는 수용된 대답을 보고, 눈을 감고 이 코드를 실행합니다. 반복이 옳은 일인지에 대해 처음부터 의심하지 않고 말이죠.

이 답변의 목적은 반복이 반드시 모든 문제에 대한 해결책은 아니며, 더 나은, 더 빠르고 더 관용적인 해결책이 존재할 수 있으며, 이를 탐구하는 데 시간을 투자할 가치가 있다는 것을 새로운 사용자가 이해할 수 있도록 돕는 것입니다.반복 대 벡터라이제이션의 전쟁을 시작하려는 것이 아니라, 새로운 사용자들이 이 라이브러리를 통해 자신의 문제에 대한 해결책을 개발할 때 알려주길 바랍니다.

그리고 마지막으로... 이 게시물을 요약하자면 TLDR.

enter image description here

먼저 DataFrame에서 행을 반복해야 하는 경우를 고려합니다.대안에 대해서는 이 답변을 참조하십시오.

행을 반복해야 하는 경우 아래 방법을 사용할 수 있습니다.다른 답변에는 언급되지 않은 몇 가지 중요한 주의 사항을 기록합니다.

itertuples()보다 더 빠를 것으로 예상됩니다.iterrows()

그러나 문서에 따르면 (현재 판다 0.24.2):

  • 행:dtype행 간에 일치하지 않을 수 있음

각 행에 대해 영상 시리즈를 반환하므로 행 전체에 걸쳐 유형이 보존되지 않습니다(DType은 DataFrame의 경우 열 전체에 걸쳐 보존됨).행을 반복하면서 유형을 보존하려면 값의 명명된 튜플을 반환하고 일반적으로 반복 행()보다 훨씬 빠른 반복 튜플()을 사용하는 것이 좋습니다.

  • 행:행 수정 안 함

반복하는 내용을 수정하면 안 됩니다.이것이 모든 경우에 작동하는 것을 보장하지는 않습니다.데이터 유형에 따라 반복자는 보기가 아닌 복사본을 반환하며, 이 복사본에 쓰는 것은 영향을 미치지 않습니다.

대신 DataFrame.apply()를 사용합니다.

    new_df = df.apply(lambda x: x * 2, axis = 1)
  • 반복 중복:

잘못된 Python 식별자이거나 반복되거나 밑줄로 시작하는 경우 열 이름이 위치 이름으로 바뀝니다.많은 수의 열(>255)이 있는 경우 일반 튜플이 반환됩니다.

자세한 내용은 반복에 관한 팬더 문서를 참조하십시오.

이거 써야 돼요.다음과 같이 한 줄씩 반복하는 것이 특별히 효율적이지는 않지만,Series오브젝트를 생성해야 합니다.

하는 동안에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

다음과 같은 기능을 사용할 수 있습니다.

for i in range(0, len(df)):
    print(df.iloc[i]['c1'], df.iloc[i]['c2'])

사용할 수도 있습니다.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)

효율적인 반복 방법

Pandas 데이터 프레임을 반복해야 한다면 행()을 사용하지 않는 것이 좋을 것입니다.다른 방법과 일반적인 방법이 있습니다.iterrows()최고와는 거리가 멀어요이터는 ()이 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

이 기사는 아이터로우와 아이터튜플을 아주 재미있게 비교한 것입니다.

에서 반복하는 방법을 찾고 있었는데 여기서 끝이 났습니다.

for i, row in df.iterrows():
    for j, column in row.iteritems():
        print(column)

동일한 작업을 수행할 수 있는 여러 가지 옵션이 있으며, 많은 사람들이 답변을 공유했습니다.

아래 두 가지 방법을 쉽고 효율적으로 수행할 수 있었습니다.

  1. DataFrame.iterrows()
  2. DataFrame.itterpules()

예:

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

 # With the iterrows method

 for index, row in df.iterrows():
     print(row["c1"], row["c2"])

 # With the itertuples method

 for row in df.itertuples(index=True, name='Pandas'):
     print(row.c1, row.c2)

참고: titerupples()는 titerrows()보다 빨라야 합니다.

구현하는 반복기를 직접 작성할 수 있습니다.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);

enter image description here

enter image description here

의 모든 행을 루프하는 방법dataframe다음을 사용할 수 있습니다.

for x in range(len(date_example.index)):
    print date_example['Date'].iloc[x]
 for ind in df.index:
     print df['c1'][ind], df['c2'][ind]

유용한 패턴은 다음과 같습니다.

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

업데이트: cs95는 그의 답변을 일반적인 numpy 벡터화를 포함하도록 업데이트했습니다.그의 대답을 간단히 참고할 수 있습니다.


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]

Enter image description here

의 모든 행을 루프하는 방법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, 인덱스는 튜플의 첫번째 요소로 추가되며, 이는 일부 응용 프로그램에 바람직하지 않을 수 있습니다.

요컨대

  • 가능한 경우 벡터화 사용
  • 연산을 벡터화할 수 없는 경우 - 목록 이해를 사용합니다.
  • 전체 행을 나타내는 단일 개체가 필요한 경우에는 titeruple을 사용합니다.
  • 위의 내용이 너무 느릴 경우 swifer.apply를 시도합니다.
  • 그래도 너무 느리다면 Cython 루틴을 시도해 보십시오.

벤치마크

Benchmark of iteration over rows in a Pandas DataFrame

시리즈가 아닌 데이터 프레임을 반환 받는 동안 스로우 행을 반복하는 방법이 있습니다.데이터 프레임으로 반환할 행에 대한 목록으로 인덱스를 전달할 수 있다고 언급한 사람이 없습니다.

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

이중 대괄호의 사용을 기록합니다.행이 하나인 DataFrame을 반환합니다.

가끔은 벡터화된 코드보다 루프가 더 나을 때도 있습니다.

여기서 많은 답변이 정확하게 지적하듯이, 팬더에서 기본 계획은 명시적인 루프를 직접 시도하는 것이 아니라 벡터화된 코드(암묵적 루프를 사용하여)를 작성하는 것이어야 합니다.하지만 팬더에서 루프를 작성해야 하는지, 만약 그렇다면 그러한 상황에서 루프를 수행하는 가장 좋은 방법은 무엇인지에 대한 의문이 남아 있습니다.

루프가 적절한 일반적인 상황이 적어도 하나 있다고 생각합니다. 즉, 다른 의 값에 의존하는 함수를 다소 복잡한 방식으로 계산해야 할 때입니다.이 경우 루핑 코드는 벡터화된 코드보다 더 간단하고, 더 판독성이 높으며, 오류가 발생하기 쉬운 경우가 많습니다.

루프 코드는 아래에서 볼 수 있듯이 훨씬 더 빠를 도 있으므로 속도가 가장 중요한 경우에는 루프를 사용할 수 있습니다.하지만 실제로는 최적화된 눔피/눔바가 거의 항상 판다보다 빠르기 때문에 처음에는 판다가 아닌 눔피/눔바에서 작업했어야 할 경우의 하위 집합이 될 것입니다.

예를 들어 보여드리겠습니다.열의 누적 합계를 취하되 다른 열이 0이 될 때마다 재설정한다고 가정합니다.

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

이것은 이것을 달성하기 위해 분명히 한 줄의 판다를 쓸 수 있는 좋은 예입니다. 비록 그것은 특별히 읽을 수 있는 것은 아니지만, 특히 여러분이 판다에 대해 아직 제대로 경험이 없다면 말입니다.

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 어레이로 변환하는 데는 약간의 오버헤드가 필요하지만, Panda나 NumPy에 대해 아무것도 모르더라도 읽을 수 있는 코드 한 줄이 핵심 코드입니다.

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

그리고 이 코드는 벡터화된 코드보다 더 빠릅니다.100,000개의 행이 있는 일부 빠른 테스트에서는 위의 내용이 접근 방식에 의한 그룹보다 약 10배 빠릅니다.속도에 대한 한 가지 키는 선택 사항인 numba입니다."@nb.jit" 라인이 없으면 루프 코드는 실제로 접근 방식에 의한 그룹보다 약 10배 느립니다.

분명히 이 예는 충분히 간단하기 때문에 관련된 머리 위로 고리를 작성하는 것보다 한 줄의 팬더를 선호할 수 있습니다.그러나 NumPy/numba 루프 접근 방식의 가독성 또는 속도가 합리적인 이 문제의 더 복잡한 버전이 있습니다.

값을 보거나 수정할 때 모두 다음을 사용합니다.iterrows(). for loop에서 그리고 tuple unpacking을 사용하여 (예: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을 지정합니다.

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]])

사용을 권장합니다.df.at[row, column](소스) 모든 팬더 세포를 반복합니다.

예를 들어 다음과 같습니다.

for row in range(len(df)):
  print(df.at[row, 'c1'], df.at[row, 'c2'])

출력은 다음과 같습니다.

10 100
11 110
12 120

보너스

다음과 같이 셀의 값을 수정할 수도 있습니다.df.at[row, column] = newValue.

for row in range(len(df)):
  df.at[row, 'c1'] = 'data-' + str(df.at[row, 'c1'])
  print(df.at[row, 'c1'], df.at[row, 'c2']) 

출력은 다음과 같습니다.

data-10 100
data-11 110
data-12 120

가장 쉬운 방법은,apply기능.

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

df.apply(lambda row: print_row(row), axis=1)

아마도 가장 우아한 솔루션일 것입니다. (물론 가장 효율적인 솔루션은 아닙니다.)

for row in df.values:
    c2 = row[1]
    print(row)
    # ...

for c1, c2 in df.values:
    # ...

참고:

  • 사용할 것을 명시적으로 권장하는 문서.to_numpy()대신
  • 생산된 NumPy 배열은 최악의 경우 모든 열에 맞는 adtype을 가질 것입니다.object
  • 애초에 루프를 사용하지 않을 좋은 이유가 있습니다.

그래도 사소한 문제에 대한 간단한 해결책으로 이 옵션이 여기에 포함되어야 한다고 생각합니다.

또한 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

df.iterrows()돌아온다tuple(a, b)어디에aindex그리고.brow.

이 예제에서는 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])

면책 사항:여기에 반복적(루프) 접근 방식을 사용하지 말 것을 권장하는 답변이 매우 많지만(그리고 대부분 동의합니다), 저는 여전히 다음 상황에 대해 합리적인 접근 방식이라고 생각합니다.

API의 데이터로 데이터 프레임 확장

불완전한 사용자 데이터를 포함하는 대규모 데이터 프레임이 있다고 가정해 보겠습니다.이제 이 데이터를 추가 열로 확장해야 합니다. 예를 들어, 사용자의 데이터를age그리고.gender.

두 값 모두 백엔드 API에서 가져와야 합니다.API가 여러 사용자 ID를 한 번에 허용하는 "배치" 엔드포인트를 제공하지 않는다고 가정합니다.그렇지 않으면 차라리 API를 한 번만 호출하는 것이 좋습니다.

네트워크 요청을 위한 비용(대기 시간)은 데이터 프레임의 반복을 훨씬 능가합니다.우리는 반복에 대한 대체 접근 방식을 사용할 때 얻을 수 있는 이득이 무시할 정도로 적은 수백 밀리초의 네트워크 왕복 시간에 대해 이야기하고 있습니다.

각 행에 대해 하나의 값비싼 네트워크 요청

그래서 이 경우에는 반복적인 접근법을 사용하는 것이 더 좋습니다.네트워크 요청은 비용이 많이 들지만 데이터 프레임의 각 행에 대해 한 번만 트리거되는 것이 보장됩니다.DataFrame.iterrows를 사용하는 예는 다음과 같습니다.

for index, row in users_df.iterrows():
  user_id = row['user_id']

  # Trigger expensive network request once for each row
  response_dict = backend_api.get(f'/api/user-data/{user_id}')

  # Extend dataframe with multiple data from response
  users_df.at[index, 'age'] = response_dict.get('age')
  users_df.at[index, 'gender'] = response_dict.get('gender')

일부 라이브러리(예: 내가 사용하는 Java interop 라이브러리)에서는 스트리밍 데이터와 같이 한 번에 한 행으로 값을 전달해야 합니다.스트리밍의 특성을 복제하기 위해 데이터 프레임 값을 하나씩 '스트리밍'하며, 때때로 유용한 다음과 같이 적었습니다.

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을 사용하는 것보다 훨씬 느리지만 일부 상황에서는 필요합니다.

수용된 답이 말하듯이, 함수를 행 위에 적용하는 가장 빠른 방법은 벡터화된 함수, 소위 NumPy를 사용하는 것입니다.ufuncs(범용 함수).

그런데 적용하고 싶은 기능이 아직 NumPy에 구현되어 있지 않은데 어떻게 해야 합니까?

음, 그를 이용해서.vectorize장식가 로부터numba, 다음과 같이 파이썬에서 직접 ufuncs를 쉽게 만들 수 있습니다.

from numba import vectorize, float64

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

이 함수에 대한 설명서는 다음과 같습니다. NumPy 범용 함수 만들기

언급URL : https://stackoverflow.com/questions/10729210/iterating-row-by-row-through-a-pandas-dataframe

반응형