Pandas 기본 소개와 핵심 자료 구조:

  • Pandas는 파이썬에서 데이터 분석 및 전처리를 간편하게 해주는 라이브러리입니다.
  • 대규모 데이터를 처리할 수 있도록 도와주며, 스프레드시트(엑셀)처럼 행과 열로 된 구조를 통해 데이터를 다룰 수 있습니다.
  • NumPy 배열을 기반으로 만들어졌으며, 통계, 시각화, 머신러닝 등 다양한 라이브러리와 잘 호환됩니다.
    • Pandas가 NumPy 배열을 기반으로 만들어졌다는 것은, Pandas의 내부 데이터 구조(특히 Series와 DataFrame의 각 열)가 NumPy 배열을 사용하여 데이터를 저장하고 처리한다는 의미입니다.
    • NumPy 배열은 연속된 메모리 블록에 데이터를 저장하기 때문에, 대규모 데이터를 다룰 때 메모리 효율이 좋고 벡터화 연산(한 번에 여러 데이터에 대한 연산)을 빠르게 수행할 수 있습니다. Pandas는 이러한 NumPy의 장점을 활용하여 데이터 처리와 연산을 빠르게 할 수 있습니다.
    • NumPy 배열을 사용하면, 반복문 없이 배열 전체에 대해 수학적 연산을 한 번에 적용할 수 있습니다. 예를 들어, Pandas의 Series에서 모든 값에 대해 수학 연산을 수행할 때 내부적으로 NumPy의 벡터화 연산이 사용되어 빠른 처리 속도를 보장합니다.
    • 많은 데이터 분석 및 과학 연산 라이브러리들이 NumPy를 기반으로 만들어졌기 때문에, Pandas와 NumPy의 조합은 데이터 처리 파이프라인에서 서로 호환성이 좋고 연동하기가 수월합니다.

 

Pandas 설치:

  • 일반적으로 다음과 같이 pip 명령으로 설치합니다:
pip install pandas

 

Pandas의 핵심 자료 구조:

  • Pandas에서 가장 중요한 자료 구조는 Series와 DataFrame 입니다.

 

Pandas의 핵심 자료 구조 - Series:

  • 1차원 데이터를 저장하는 구조
  • 인덱스(index)와 값(value)로 구성됩니다.
  • 슬라이싱, 수학 연산 등을 쉽게 수행할 수 있음.
import pandas as pd

data = [10, 20, 30, 40]
s = pd.Series(data, index=['a', 'b', 'c', 'd'])  # 인덱스 지정 가능
print(s)

#    a    10
#    b    20
#    c    30
#    d    40
#    dtype: int64

 

Pandas의 핵심 자료 구조 - DataFrame:

  • 2차원(행과 열) 구조를 가진 데이터
  • 표 형태로 데이터가 표시되며, 인덱스와 칼럼 이름으로 데이터에 접근할 수 있습니다.
  • 엑셀 시트와 유사한 형태로 볼 수 있어 직관적입니다.
  • 열별/행별로 데이터에 접근, 슬라이싱, 추가/삭제 등을 손쉽게 수행할 수 있음.
import pandas as pd

data = {
    'name': ['Alice', 'Bob', 'Charlie'],
    'age': [25, 30, 35],
    'city': ['New York', 'Los Angeles', 'Chicago']
}
df = pd.DataFrame(data)
print(df)

#       name  age         city
# 0    Alice   25     New York
# 1      Bob   30  Los Angeles
# 2  Charlie   35      Chicago

 

데이터 불러오기와 저장하기 - CSV 파일에서 데이터 불러오기:

# 실무에서 가장 많이 사용하는 기능 중 하나입니다.
df = pd.read_csv('data.csv')

# 인코딩, 구분자 등의 옵션을 통해 다양한 파일 형식을 읽어올 수 있습니다.
df = pd.read_csv('data.csv', encoding='utf-8', sep=',')

# Excel 파일(.xlsx, .xls 등)을 다루는 경우:
df = pd.read_excel('data.xlsx')

# DataFrame을 파일로 저장하는 방법도 아주 간단합니다.
df.to_csv('output.csv', index=False)  # index=False를 주면 인덱스 컬럼 제외
df.to_excel('output.xlsx', index=False)

 

DataFrame 기본 조작:

# 열 접근
## 단일 열
df['age']  # Series 형태로 age 컬럼만 반환
## 여러 열
df[['name', 'city']]

# 행(Row)에 접근:
## 인덱스 기반으로 특정 행 가져오기
df.loc[0]      # label 기반(행 인덱스)
df.iloc[0]     # 정수 기반(0번째 행)

# 부분 데이터 가져오기:
df.loc[0:1, 'name':'age']  # 0~1행, name~age 열 범위
df.iloc[0:2, 0:2]          # 0~1행, 0~1열

# 데이터 추가/수정/삭제
df['country'] = ['USA', 'USA', 'USA']  # 새로운 열 추가


# 행 추가:
## 행 추가는 주로 concat 또는 append(판다스 2.0부터 deprecated) 사용
new_row = pd.DataFrame([['David', 40, 'Boston', 'USA']], 
                       columns=['name', 'age', 'city', 'country'])
df = pd.concat([df, new_row], ignore_index=True)

# 열 삭제:
df.drop(columns=['country'], inplace=True)  # country 열 삭제

# 행 삭제:
df.drop(index=[0], inplace=True)  # 인덱스가 0인 행 삭제

 

Dataframe 기본 함수 및 속성:

  • DataFrame 기본 정보 확인
    • df.info() : 데이터셋의 전체적인 정보(행수, 열수, 각 열의 데이터 타입)를 보여줍니다.
    • df.head(n) : 상위 n개 행을 미리 보기.
    • df.tail(n) : 하위 n개 행을 미리 보기.
    • df.describe() : 수치형 칼럼에 대한 요약 통계량(평균, 표준편차, 최소/최대값 등)을 보여줍니다.
    • df.shape : (행 수, 열 수)를 튜플 형태로 반환.
# 결측치(Null, NaN) 처리
## 결측치 확인: df.isnull(), df.isna(), df.isnull().sum()
print(df.isnull().sum())  # 각 열에 결측치가 몇 개인지 확인

# 결측치 제거:
df.dropna()           # NaN 포함된 행 삭제
df.dropna(axis=1)     # NaN 포함된 열 삭제

# 결측치 대체:
df['age'].fillna(df['age'].mean(), inplace=True)  # 평균값으로 채우기
df['city'].fillna('Unknown', inplace=True)        # 문자열로 채우기

 

 

정렬, 필터링, 그룹화, 합치기

정렬 - sort_values() 메서드로 특정 열을 기준으로 오름차순/내림차순 정렬 가능:

import pandas as pd

data = {
    'name': ['Alice', 'Bob', 'Charlie', 'David'],
    'age': [25, 30, 35, 25],
    'score': [88, 92, 95, 78]
}
df = pd.DataFrame(data)

# age 기준 오름차순 정렬
df_sorted_by_age = df.sort_values(by='age')  

# score 기준 내림차순 정렬
df_sorted_by_score_desc = df.sort_values(by='score', ascending=False)

# 여러 열 기준으로 정렬할 수도 있습니다.
df_multiple_sort = df.sort_values(by=['age', 'score'], ascending=[True, False])

 

필터링(Filtering):

# 조건을 사용해 원하는 데이터만 추출 가능
# age가 30 이상인 데이터만 추출
df_age_over_30 = df[df['age'] >= 30]

# 문자열 비교: name이 "Alice"인 행만
df_alice = df[df['name'] == 'Alice']

# 여러 조건을 결합할 땐 &(AND), |(OR), ~(NOT) 연산자 사용 (단, 괄호 필요)
# age가 25 이상이고 score가 90 이상인 행
df_filtered = df[(df['age'] >= 25) & (df['score'] >= 90)]

# age가 30 미만이거나 score가 80 미만인 행
df_filtered_or = df[(df['age'] < 30) | (df['score'] < 80)]

 

그룹화(GroupBy)와 집계(Aggregation):

# 비슷한 범주(카테고리)로 묶어서 요약 통계를 낼 때 유용
data = {
    'department': ['Sales', 'Sales', 'IT', 'IT', 'HR'],
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
    'salary': [5000, 6000, 7000, 6500, 4000]
}
df = pd.DataFrame(data)

# department별 평균 salary 계산
grouped = df.groupby('department')
mean_salary_by_dept = grouped['salary'].mean()
print(mean_salary_by_dept)

# department
# HR       4000.0
# IT       6750.0
# Sales    5500.0
# Name: salary, dtype: float64

 

여러 통계량 동시에 구하기:

# agg() 또는 aggregate() 함수를 사용해 동시에 여러 값을 얻을 수 있습니다.

# department별 salary의 평균과 최대값, 최소값
stats = df.groupby('department')['salary'].agg(['mean', 'max', 'min'])
print(stats)

#              mean   max   min
# department                  
# HR         4000.0  4000  4000
# IT         6750.0  7000  6500
S# ales      5500.0  6000  5000

 

여러 DataFrame 합치기: Merge / Join / Concat - Concat:

df1 = pd.DataFrame({'A': [1, 2], 'B': [3, 4]})
df2 = pd.DataFrame({'A': [5, 6], 'B': [7, 8]})

# 행 방향으로 이어 붙이기
df_concat = pd.concat([df1, df2], ignore_index=True)
print(df_concat)

#    A  B
# 0  1  3
# 1  2  4
# 2  5  7
# 3  6  8

 

여러 DataFrame 합치기: Merge / Join / Concat - Merge:

left = pd.DataFrame({
    'key': ['K0', 'K1', 'K2'],
    'A': ['A0', 'A1', 'A2']
})
right = pd.DataFrame({
    'key': ['K0', 'K1', 'K3'],
    'B': ['B0', 'B1', 'B3']
})

merged_inner = pd.merge(left, right, how='inner', on='key')
print(merged_inner)

#    key   A   B
# 0  K0  A0  B0
# 1  K1  A1  B1

 

여러 DataFrame 합치기: Merge / Join / Concat - Join:

  • 인덱스를 기준으로 병합할 때 주로 사용 (DataFrame에서 index를 key처럼 사용할 경우)
left = left.set_index('key')
right = right.set_index('key')

joined = left.join(right, how='inner')
print(joined)

 

문자열 처리와 apply 함수

문자열 처리 (String Methods):

  • Pandas에는 문자열 처리 메서드가 풍부하게 포함되어 있습니다. 주로 Series.str 접두사를 통해 사용합니다
df = pd.DataFrame({'city': ['New York', 'LOS ANGELES', 'chicago', None]})

# 소문자로 변환
df['city_lower'] = df['city'].str.lower()

# 문자열 길이
df['city_len'] = df['city'].str.len()

# 문자열 치환
df['city_replaced'] = df['city'].str.replace(' ', '_')

# 결측치가 있으면 NaN이 그대로 반환됨
print(df)

 

apply 함수:

  • apply() 함수는 Series 또는 DataFrame의 행/열을 순회하면서 임의의 함수를 적용할 수 있게 해줍니다.
import numpy as np

# 예: 각 행마다 "age * 2"를 계산
def double_age(row):
    return row['age'] * 2

df['age_double'] = df.apply(double_age, axis=1)  # axis=1이면 '행' 단위로 함수 적용

 

map 함수:

  • map()은 Series 객체에 일대일 함수 매핑을 할 때 주로 사용 (특히 딕셔너리 매핑).
df['salary_level'] = df['salary'].map(lambda x: 'high' if x > 6000 else 'normal')

 

주의: apply(), map(), applymap()은 편리하지만, 데이터가 아주 커지면 성능 문제가 생길 수 있습니다(파이썬-level for loop가 내부에서 실행). 가능하다면 벡터화(vectorized) 연산을 우선 고려하는 것이 좋습니다.

 

고성능/최적화 팁

벡터화 연산(Vectorized Operations) 활용:

  • 파이썬의 for-loop로 각 행을 순회하는 대신, NumPy 배열 기반의 벡터화 연산을 쓰면 대량 데이터를 훨씬 빠르게 처리할 수 있습니다.
  • 이런 방식은 내부적으로 C로 구현되어 있어 빠르고 메모리 사용이 효율적입니다.
# 예: 모든 salary 값에 세율 10%를 적용하고 싶을 때
df['salary_after_tax'] = df['salary'] * 0.9

 

dtype 최적화:

  • 불필요하게 float64(또는 int64)를 사용하는 대신, 값 범위에 따라 float32나 int32, 혹은 category dtype을 사용하면 메모리를 절약할 수 있습니다.
df['column'] = df['column'].astype('int32')
df['category_col'] = df['category_col'].astype('category')

 

Chunk 단위로 파일 읽어오기:

  • 매우 큰 파일(수백 MB~수 GB)을 한 번에 읽으면 메모리가 부족해질 수 있습니다. 이럴 때는 Chunk 단위로 나누어 읽는 방법을 쓸 수 있습니다.
chunk_size = 100000
chunks = pd.read_csv('big_data.csv', chunksize=chunk_size)

for i, chunk in enumerate(chunks):
    # chunk에 대해 필요한 연산 수행
    process(chunk)

 

query()와 eval() 활용:

  • df.query()와 df.eval()은 Pandas의 내장 “미니 언어”로, 속도 향상에 도움이 될 때가 있습니다.
    • df.query("age > 30 and salary < 5000")
    • df.eval("new_col = salary * 1.1 + 100")

+ Recent posts