Pandas DataFrame에서 행을 반복하는 방법
Pandas DataFrame에서 행을 반복하는 방법은 데이터 분석 및 처리에서 매우 중요한 작업입니다. 특정 조건에 맞는 데이터를 찾거나, 각 행에 대한 계산을 수행하거나, 새로운 데이터를 생성하는 등 다양한 작업을 수행할 때 사용됩니다.
왜 행을 반복해야 할까요?
- 특정 조건에 맞는 데이터 찾기: 특정 값을 가진 행만 추출하거나, 특정 조건을 만족하는 행에 대한 처리를 수행할 수 있습니다.
- 각 행에 대한 계산: 각 행의 값을 기반으로 새로운 값을 계산하거나, 기존 값을 수정할 수 있습니다.
- 새로운 데이터 생성: 기존 데이터를 기반으로 새로운 데이터를 생성하거나, 데이터를 변형할 수 있습니다.
행 반복 방법
Pandas는 DataFrame의 행을 반복하기 위한 다양한 방법을 제공합니다. 각 방법마다 장단점이 있으므로, 데이터의 크기와 수행하려는 작업에 따라 적절한 방법을 선택해야 합니다.
iterrows()
- 기본적인 행 반복: 가장 간단하게 사용할 수 있는 방법입니다.
- 반환 값: (index, Series) 형태의 튜플을 반환합니다.
- 단점: 느릴 수 있으며, 많은 양의 데이터를 처리할 때는 비효율적일 수 있습니다.
import pandas as pd
# DataFrame 생성
data = {'컬럼1': [1, 2, 3], '컬럼2': ['A', 'B', 'C']}
df = pd.DataFrame(data)
# iterrows() 사용
for index, row in df.iterrows():
print(index, row)
itertuples()
- Namedtuple 형태로 반환: 각 행을 namedtuple 형태로 반환하여 속성 접근이 편리합니다.
- 성능: iterrows()보다 약간 빠릅니다.
for row in df.itertuples():
print(row.Index, row.컬럼1, row.컬럼2)
apply()
- 벡터화 연산: 각 행에 함수를 적용하여 새로운 Series를 생성합니다.
- 성능: 벡터화 연산을 활용하여 매우 빠릅니다.
def my_function(row):
# 각 행에 대한 처리
return row['컬럼1'] * 2
df['새로운_컬럼'] = df.apply(my_function, axis=1)
for 루프와 loc 또는 iloc
- 직접적인 인덱싱: loc 또는 iloc을 사용하여 특정 행에 직접 접근합니다.
- 유연성: 원하는 방식으로 행에 접근할 수 있습니다.
for i in range(len(df)):
row = df.loc[i]
# 각 행에 대한 처리
print(row)
어떤 방법을 선택해야 할까요?
- 간단한 작업: iterrows()가 가장 간단하지만, 성능이 중요한 경우에는 다른 방법을 고려해야 합니다.
- 벡터화 연산: apply()는 벡터화 연산을 활용하여 빠르게 처리할 수 있습니다.
- 유연성: for 루프와 loc 또는 iloc은 직접적인 인덱싱을 통해 유연하게 처리할 수 있습니다.
데이터의 크기, 수행하려는 작업의 복잡성, 성능 요구사항 등을 고려하여 적절한 방법을 선택하는 것이 중요합니다.
주의:
- 큰 DataFrame: 큰 DataFrame의 경우 iterrows()는 매우 느릴 수 있습니다. 가능한 한 벡터화 연산을 활용하는 것이 좋습니다.
- 변경 불가능한 Series: iterrows()로 얻은 Series는 변경 불가능합니다. Series를 수정하려면 새로운 Series를 생성해야 합니다.
Pandas DataFrame 행 반복 관련 샘플 코드
다양한 상황에 맞춰 활용할 수 있는 샘플 코드들을 준비했습니다.
iterrows()를 활용한 간단한 예시
import pandas as pd
# 샘플 데이터 생성
data = {'이름': ['홍길동', '김철수', '박영희'], '나이': [30, 25, 32], '성별': ['남', '남', '여']}
df = pd.DataFrame(data)
# 각 행에 대해 이름과 나이 출력
for index, row in df.iterrows():
print(f"인덱스: {index}, 이름: {row['이름']}, 나이: {row['나이']}")
itertuples()를 사용하여 namedtuple 형태로 접근
# 각 행을 namedtuple로 접근하여 속성으로 값에 접근
for row in df.itertuples():
print(f"인덱스: {row.Index}, 이름: {row.이름}, 나이: {row.나이}")
apply()를 활용한 벡터화 연산
# 나이에 5를 더한 새로운 컬럼 생성
def add_five(row):
return row['나이'] + 5
df['나이+5'] = df.apply(add_five, axis=1)
print(df)
for 루프와 loc를 사용한 직접적인 인덱싱
# 특정 조건에 맞는 행만 처리
for i in range(len(df)):
if df.loc[i, '성별'] == '남':
print(df.loc[i])
복잡한 조건과 연산
# 복잡한 조건과 연산을 통해 새로운 DataFrame 생성
new_df = pd.DataFrame()
for index, row in df.iterrows():
if row['나이'] >= 30:
new_row = {'이름': row['이름'], '나이': row['나이'] * 1.1}
new_df = new_df.append(new_row, ignore_index=True)
print(new_df)
성능 향상을 위한 벡터화 연산 (NumPy 활용)
import numpy as np
# NumPy를 이용하여 벡터화 연산 수행
df['나이 제곱'] = df['나이'] ** 2
각 샘플 코드에 대한 설명:
- iterrows(): 가장 기본적인 방법으로, 각 행을 순회하며 처리합니다.
- itertuples(): namedtuple 형태로 접근하여 속성으로 값에 접근할 수 있어 편리합니다.
- apply(): 각 행에 함수를 적용하여 새로운 컬럼을 생성하거나 값을 변환하는 데 사용됩니다.
- for 루프와 loc: 직접적인 인덱싱을 통해 특정 행에 접근하여 처리합니다.
- 복잡한 조건: 조건에 맞는 행을 선택하여 새로운 DataFrame을 생성하는 예시입니다.
- 벡터화 연산: NumPy를 활용하여 빠르게 연산을 수행합니다.
어떤 방법을 선택해야 할까요?
- 간단한 처리: iterrows() 또는 itertuples()
- 벡터화 연산: apply() 또는 NumPy 활용
- 유연한 처리: for 루프와 loc
- 성능: 벡터화 연산이 일반적으로 더 빠릅니다.
주의사항:
- 변경 불가능한 Series: iterrows()로 얻은 Series는 변경 불가능합니다.
다음에 어떤 내용을 알고 싶으신가요?
- 특정 조건에 맞는 데이터 추출
- 새로운 컬럼 생성 및 계산
- 그룹화 및 집계
- 데이터 정제
- 시각화
- ...
Pandas DataFrame에서 행을 반복하는 다른 방법들
앞서 Pandas DataFrame에서 행을 반복하는 다양한 방법들을 살펴보았습니다. 하지만 상황에 따라 더 효율적이고 간결한 방법이 있을 수 있습니다. 이번에는 다른 대체 방법들과 각 방법의 장단점에 대해 자세히 알아보겠습니다.
Vectorization (벡터화 연산):
- 개념: 전체 DataFrame에 대한 연산을 한 번에 수행하여 반복문을 사용하는 것보다 훨씬 빠릅니다.
- 방법: NumPy와 같은 라이브러리를 활용하여 벡터화 연산을 수행합니다.
- 장점: 속도가 매우 빠르며, 코드가 간결해집니다.
- 단점: 모든 연산이 벡터화될 수 있는 것은 아닙니다.
import numpy as np
# 나이 컬럼에 5를 더하는 연산 (벡터화)
df['나이'] += 5
List Comprehension:
- 개념: 간결한 표현으로 리스트를 생성하는 방법입니다.
- 방법: for 루프와 비슷한 구조로 리스트를 생성하지만, 한 줄로 표현할 수 있습니다.
- 장점: 코드가 간결하고 가독성이 좋습니다.
- 단점: 복잡한 연산에는 적합하지 않을 수 있습니다.
# 나이가 30 이상인 사람의 이름을 리스트로 생성
names = [name for name, age in zip(df['이름'], df['나이']) if age >= 30]
Lambda 함수와 apply:
- 개념: 익명 함수인 lambda 함수를 사용하여 apply 메서드에 전달하여 각 행에 함수를 적용합니다.
- 장점: 간결하고 유연하게 함수를 정의할 수 있습니다.
- 단점: 복잡한 로직에는 가독성이 떨어질 수 있습니다.
# 각 행의 나이를 문자열로 변환
df['나이_문자열'] = df['나이'].apply(lambda x: str(x))
NumPy의 where 함수:
- 개념: 조건에 따라 값을 선택하는 함수입니다.
- 방법: 특정 조건에 맞는 값을 다른 값으로 바꾸거나 새로운 배열을 생성할 때 사용합니다.
- 장점: 벡터화 연산을 활용하여 빠르게 처리할 수 있습니다.
# 나이가 30 이상인 사람의 나이를 30으로 변경
df['나이'] = np.where(df['나이'] >= 30, 30, df['나이'])
어떤 방법을 선택해야 할까요?
- 속도: 벡터화 연산이 가장 빠릅니다.
- 간결성: List comprehension, lambda 함수, NumPy의 where 함수가 간결합니다.
- 유연성: apply 메서드가 가장 유연합니다.
- 가독성: 상황에 따라 다르지만, 일반적으로 List comprehension이나 lambda 함수가 가독성이 좋습니다.
결론적으로, 어떤 방법을 선택할지는 데이터의 크기, 수행하려는 작업의 복잡성, 코드의 가독성 등을 고려하여 결정해야 합니다.
예시:
import pandas as pd
import numpy as np
# 샘플 데이터 생성
data = {'이름': ['홍길동', '김철수', '박영희'], '나이': [30, 25, 32], '성별': ['남', '남', '여']}
df = pd.DataFrame(data)
# 벡터화 연산: 나이에 5를 더하고, 30 이상이면 '성인'으로 변경
df['나이'] += 5
df['연령대'] = np.where(df['나이'] >= 30, '성인', '청년')
# List comprehension: 나이가 30 이상인 사람의 이름을 리스트로 생성
adults = [name for name, age in zip(df['이름'], df['나이']) if age >= 30]
# lambda 함수와 apply: 각 행의 이름을 대문자로 변환
df['이름_대문자'] = df['이름'].apply(lambda x: x.upper())
print(df)
print(adults)
python pandas dataframe