1. 개요
1.1 중규모 데이터 처리 담당
- 처리 규모 — 중규모 데이터에 적합, 최대 수 GB까지 처리 가능
- 메모리 방식 — 단일 머신 메모리에 데이터를 한 번에 로드
1.2 pandas란
- 공식 정의 — fast, powerful, flexible and easy to use open source data analysis and manipulation tool
- 포지션 — Python Data Analysis Library
- R과의 대응 — R ↔ pandas(분석) + sklearn(ML) + statsmodels(통계) + matplotlib(시각화)
- 기반 — numpy를 근간으로 데이터 처리 수행
데이터를 사용하는 주체에 따라 전처리 방향이 달라짐 — 데이터 분석용(정제)이냐, 모델 학습용(피처 엔지니어링 → Gold Layer)이냐.
1.3 자료구조
- Series — 1차원(Vector). 데이터(ndarray) + 인덱스(행)
- DataFrame — 2차원(Matrix). 데이터(ndarray) + 인덱스(행) + 컬럼(열)
1.4 주요 작업
- 데이터 로드 — 다양한 리소스로부터 Raw Data 적재
- 데이터 전처리 — 정제, 피처 엔지니어링
- 추출 — 열/행 단위 추출
- 결합 — 데이터 병합
- 집계/피벗 — 인사이트 도출, Gold Layer 형태로 변환
import numpy as np
import pandas as pd
2. Series 생성
1차원 자료구조로 ndarray(1차원) + index로 구성됨.
2.1 기본 생성
# np.nan : 결측치
data = [-1, 3, 5, np.nan, 7, 8]
vec = pd.Series(data)
vec = pd.Series(data, name='점수')
# 속성 변경 가능 (객체이므로)
vec.name = '더미데이터'
- pd.Series(data) — 리스트로부터 Series 생성
- name — Series에 이름 부여, 생성 후에도 변경 가능
- 인덱스 — 미지정 시 0부터 자동 부여
- dtype — 데이터 보고 자동 결정, 결측치 포함 시 float64로 처리
2.2 메타 정보 파악
vec.shape, vec.dtype, vec.ndim # 형태, 타입, 차원
vec.sample(2) # 무작위 n개 추출
vec.describe() # 기초 통계
vec.info() # 결측 개수, 인덱스 구성, 타입, 메모리
- shape — 형태/볼륨
- sample(n) — 무작위 표본 확인
- describe() — 기초 통계 제공
- info() — 결측 개수, 엔트리 수, 타입, 메모리 사용량 통합 확인
2.3 메모리 측정
import sys
sys.getsizeof(vec) # 객체 주소 + 내용 (실제 데이터 사용량 부정확)
vec.memory_usage(deep=True) # 실제 자료구조 메모리 총량 (벡터 단위)
- sys.getsizeof — 부정확. pandas는 내부적으로 BlockManager 구조를 사용해 메타 정보가 부가됨
- memory_usage(deep=True) — 실제 메모리 사용량 정확 측정. DataFrame은 sum()까지 호출해야 총량 산출
2.4 성분 추출
vec.values # ndarray 부분
vec.index # 인덱스 부분
3. DataFrame 생성
동일한 크기의 Series를 n개 모은 2차원 자료구조. shape = (n, m).
3.1 구성 요소
| 요소 | 역할 |
| 인덱스 | 행 식별자 |
| 컬럼 | 열 식별자 |
| 데이터 | ndarray (실제 값) |
DataFrame 인덱싱 → Series 인덱싱 → 스칼라 값. 차원 축소 흐름을 항상 기억할 것.
3.2 기본 생성
# 비어있는 DF (버퍼 용도)
pd.DataFrame()
# 데이터만으로 구성 (인덱스/컬럼 자동 부여)
values = np.random.rand(7, 4)
df = pd.DataFrame(values)
- 암묵적 인덱스/컬럼 — 0, 1, 2, ... 자동 부여, iloc에서 활용
- 빈 DataFrame — 추후 데이터를 채우는 버퍼로 사용 가능
3.3 인덱스/컬럼 지정 생성
cols = list('ABCD')
indexs = pd.date_range(start='20260225', periods=7)
df = pd.DataFrame(values, indexs, cols)
# 키워드 방식
df = pd.DataFrame(
data=np.random.rand(100, 2),
index=pd.date_range(start='20260513', periods=100),
columns=list('AB')
)
- pd.date_range — 시작일과 기간으로 날짜 인덱스 자동 생성
- 위치 인자 순서 — data, index, columns
- 키워드 사용 시 — 인자명 정확히 맞춰야 함
3.4 기본 점검 메서드
| 메서드 | 용도 |
| shape | (데이터 개수, 컬럼 수) — 형태/볼륨 파악 |
| head() / tail() / sample() | 상위/하위/무작위 표본 확인 |
| dtypes | 컬럼별 타입 확인 |
| describe() | 기초 통계 (수치형 컬럼만 대상, 문자열 배제) |
| info() | 인덱스, 결측치, 엔트리 수, 타입, 메모리 통합 정보 |
| columns | 컬럼명 확인 (필요 시 변경/재배치 검토) |
| index | 인덱스 값 확인 |
# 컬럼명 전체 변경
df.columns = ['C', 'D']
- 전체 변경 — df.columns = [...]로 일괄 교체
- 타겟 변경 — rename() 사용
4. 기본 인덱싱 (열 중심)
4.1 단일 컬럼 추출
df = pd.read_csv('customer_master.csv')
df.age # 멤버 변수 접근 (공백 없는 컬럼명만)
df['age'] # 기본형, Series 반환
df[['age']] # 차원 유지, DataFrame 반환
- df.컬럼명 — 가장 짧은 접근. 공백 포함 컬럼명에는 사용 불가
- df['컬럼명'] — 가장 기본형. Series 반환 (차원 축소)
- df[['컬럼명']] — 리스트 감싸면 DataFrame 반환 (차원 유지)
4.2 다중 컬럼 추출
df[['customer_id', 'gender', 'age', 'registration_date']]
- 순서 지정 가능 — 리스트 순서대로 컬럼 재배치
- 컬럼명 기반만 가능 — df[1] 같은 위치 기반은 KeyError (iloc에서만 가능)
- 체이닝 — df['customer_id'][0] 형태로 인덱싱 연속 가능
5. 기본 슬라이싱 (행 중심)
df[시작 : 끝 : step] 형태. 차원 유지.
df[:] # 원본 카피
df[:3] # 0 <= index < 3
df[1:10:2] # 1 <= index < 10, step 2
- 행 단위 동작 — 슬라이싱은 행 방향으로 작동
- 차원 유지 — 결과는 항상 DataFrame
- 암묵적 인덱스 사용 — 인덱스 값을 명시적으로 교체해도, 슬라이싱은 위치 기반(암묵적 인덱스)으로 작동
df.index = pd.date_range(start='20120513', periods=len(df.index))
df[:3] # 처음 3행 추출 (날짜값과 무관, 암묵적 위치 기반)
인덱스 값 자체로 추출하려면 loc을 사용해야 함.
6. loc — 라벨 기반 인덱싱
데이터의 location 정보(인덱스값, 컬럼명)를 좌표로 사용.
6.1 단일 행 추출
df.loc['2012-12-02'] # Series 반환
df.loc[['2012-12-02']] # DataFrame 반환 (차원 유지)
- 단일 라벨 — Series 반환
- 리스트 감싸기 — DataFrame 반환
6.2 좌표 지정
df.loc['2012-12-02', 'customer_name'] # 스칼라
df.loc['2012-12-02', ['customer_name']] # Series
df.loc[['2012-12-02'], 'customer_name'] # Series
df.loc[['2012-12-02'], ['customer_name']] # DataFrame (양쪽 차원 유지)
- 첫 번째 인자 — 행(인덱스) 위치
- 두 번째 인자 — 열(컬럼) 위치
- 대괄호 유무 — 차원 유지 여부 결정
6.3 범위 슬라이싱
df.loc['2012-12-02':'2012-12-05', ['customer_name']]
df.loc['2012-12-02':'2012-12-05', 'customer_name':'age']
- loc 슬라이싱 특징 — 오른쪽 경계값까지 포함 (<=)
- 다른 인덱싱과의 차이점 — 일반 슬라이싱/iloc은 오른쪽 경계 미포함 (<)
- 컬럼 순서 주의 — 실제 컬럼 배치 순서에 어긋나는 범위는 빈 결과
6.4 불리언 조건 결합
df.loc[df.age >= 40].shape # 40세 이상 추출
df.loc[:, df.columns != 'email'] # email 컬럼 제외
- 불리언 인덱싱과 결합 — 조건식을 그대로 위치에 사용
- 컬럼 필터링 — df.columns != 'X' 형태로 특정 컬럼 제외 (drop() 대안)
7. iloc — 위치 기반 인덱싱
암묵적 인덱스(0, 1, 2, ...) 활용.
df.iloc[0] # 첫 행
df.iloc[:].shape # 전체 복사
df.iloc[:, :].shape # 전체 복사 (양쪽 차원 명시)
df.iloc[1:-1, 1:-1].shape # 앞뒤 1개씩 제거
# 팬시 인덱싱
df.iloc[[1, 10, 4], [4, 2, 6]]
- 위치 기반 — 라벨이 아닌 정수 위치
- 팬시 인덱싱 — 행/열 순서까지 자유롭게 지정
- 오른쪽 경계 미포함 — 일반 슬라이싱과 동일
좌표 기반 추출 시 loc | iloc [행 표현, 열 표현] 패턴이면 무난.
8. 불리언 인덱싱
조건식의 불리언 결과를 필터로 활용.
8.1 기본 패턴
cus = pd.read_csv('customer_newer.csv')
cus.is_deleted.value_counts() # 고유값 별 카운트
조건식 = cus.is_deleted == 1
delete_user = cus.loc[조건식]
delete_user = cus[조건식] # loc 생략 가능 (권장은 loc 유지)
# cus.iloc[조건식] # 에러 - iloc은 불리언 인덱싱 불가
- value_counts() — 범주형 컬럼의 값별 빈도
- loc + 조건식 — 권장 패턴
- iloc + 조건식 — 불가능
- 층화 고려 — ML 분류 작업 시 타겟/피처 값 비율 점검 필요
8.2 부정과 다중 조건
cus.loc[~조건식] # 부정
cus.gender.unique() # 고유값 추출
df.loc[(df.gender == 'M') & (df.age >= 40)] # 다중 조건
- 부정 연산자 — ~, !
- AND — &, OR — |
- 괄호 필수 — 각 조건을 ()로 감싸지 않으면 우선순위 오류
피처 요약표 작성 시 컬럼별 고유값(unique()) + 개수/비중 확인이 기본 작업.
9. 데이터 삭제
9.1 drop()
cus.drop(['end_date'], axis=1) # 반환만 (원본 유지)
cus.drop(['end_date'], axis=1, inplace=True) # 원본 반영
- axis=1 — 컬럼 방향 삭제
- axis=0 — 행 방향 삭제 (기본값)
- inplace=True — 원본 변경, 반환 없음
9.2 del
del cus['name'] # 1회만 실행 가능, 영구 삭제
- 위험성 — 한 번 실행되면 컬럼이 즉시 사라짐. 재실행 시 KeyError
- 권장 — try-except로 감싸 사용
9.3 drop_duplicates()
df.drop_duplicates() # 중복 행 제거
- 활용 시점 — merge 후 중복 데이터가 발생했을 때 정제 단계에서 사용
- subset — 특정 컬럼 기준 중복 판정 가능
- keep — 'first', 'last', False 중 선택
10. 파생 변수 (컬럼 추가)
기존 데이터를 가공해 새로운 컬럼 생성.
10.1 활용 시나리오
- 시간 데이터 분해 — yyyy-mm-dd HH:MM:ss → year, month, hour, weekday, 분기, 반기
- 상관관계 구축 — 수식 반영
- 차원 축소 — PCA, LDA 등으로 컬럼 생성
- 함수 활용 — apply(), str 접근자 등
10.2 apply() — 콜백 함수 적용
def 콜백함수(x):
return 1 if (1995 <= int(x[:4]) <= 2009) else 0
df['is_mz_gen'] = df.birth.apply(콜백함수)
# 람다 표현식
df['is_mz_gen'] = df.birth.apply(lambda x: 1 if (1995 <= int(x[:4]) <= 2009) else 0)
- apply() — 데이터를 하나씩 꺼내 함수에 전달, 결과를 모아 새 Series 생성
- 콜백 함수 — 명명 함수 또는 람다 모두 사용 가능
- 활용 예 — MZ 세대 플래그(1995~2009년생)
10.3 str 접근자
df['birth_year'] = df.birth.str[:4]
- str 슬라이싱 — apply로 문자열을 잘라내는 것과 동일한 효과
- 벡터화 연산 — apply보다 빠름
10.4 시간 타입 변환과 dt 접근자
# 문자열 슬라이싱 방식
transaction_data_mart.payment_date.str[:4] + transaction_data_mart.payment_date.str[5:7]
# 시간 타입으로 변환 후 포맷 활용
transaction_data_mart['payment_date'] = pd.to_datetime(transaction_data_mart.payment_date)
transaction_data_mart.payment_date.dt.strftime('%Y%m')
- pd.to_datetime — 문자열 → datetime 타입 변환
- dt 접근자 — .dt.year, .dt.month, .dt.strftime() 등 시간 전용 메서드
- strftime — 원하는 포맷의 문자열 추출 (%Y%m → 202605 형태)
11. 데이터 병합
11.1 concat() — 단순 결합
동일한 컬럼을 가진 n개 DataFrame을 합칠 때 유용.
pd.concat([left_df, right_df], axis=0) # 행 방향 결합
pd.concat([left_df, right_df], axis=1) # 열 방향 결합
- axis=0 — 인덱스 증가 (행 추가)
- axis=1 — 컬럼 증가 (열 추가)
- 공통 컬럼 — 1개로 처리, 비공통 컬럼은 개별 처리 (결측 발생 가능)
- ignore_index=True — 인덱스 재정렬
11.2 merge() — 조인
2개 DataFrame을 공통 컬럼 기준으로 결합. SQL의 JOIN과 대응.
pd.merge(left_df, right_df, on='key', how='inner')
pd.merge(left_df, right_df, on='key', how='outer')
pd.merge(left_df, right_df, on='key', how='left')
pd.merge(left_df, right_df, on='key', how='right')
| how | 동작 |
| inner | 양쪽 매칭되는 키만 표기, 결측 미표기 |
| outer | 양쪽 모두 표기, 매칭 안 되는 쪽은 결측 |
| left | 왼쪽 기준, 오른쪽 결측 표기 |
| right | 오른쪽 기준, 왼쪽 결측 표기 |
- on — 명시적 기준 컬럼 지정
11.3 데이터 마트 구성 — 단계적 조인
import glob
files = glob.glob('/path/datas/*.csv')
files.sort()
dataframes = [pd.read_csv(file) for file in files]
- 트랜잭션 합치기 — pd.merge(C, D, how='outer')로 트랜잭션 1, 2 통합
- 트랜잭션 디테일 합치기 — pd.merge(E, F, how='outer')로 디테일 통합
len(X['transaction_id'].unique()), X.shape[0]
# (6786, 7144) -> 상세 건수가 고유값 대비 많음 = 1주문에 다수 상품
- 고유값 vs 행 수 비교 — 1:N 관계 확인. 주문당 여러 상품 = 제품 중심으로 트랜잭션이 펼쳐진 형태
Y = pd.merge(W, X, on='transaction_id', how='inner') # 트랜잭션 + 디테일
Z = pd.merge(Y, B, how='inner', on='item_id') # 아이템 마스터 조인
final = pd.merge(Z, A, how='left') # 고객 마스터 조인
11.4 파생변수 생성 후 중복 검증
transaction_data_mart['cal_price'] = (
transaction_data_mart.item_price * transaction_data_mart.quantity
)
transaction_data_mart.cal_price.sum(), transaction_data_mart.price.sum()
# cal_price 총합이 price 총합보다 큼 -> 중복 데이터 존재
- cal_price — item_price × quantity로 주문별 총 금액 계산
- 중복 진단 — cal_price.sum() > price.sum()이면 조인 과정에서 행이 부풀려진 상태
- 후속 조치 — drop_duplicates() 또는 적절한 키 재설정 필요
11.5 실버 레이어 저장
transaction_data_mart.to_csv('transaction_data_mart_silver.csv')
- 중간 결과물 — 정제·병합 완료 후 Silver Layer로 저장
- 다음 단계 — 집계/피벗을 통해 Gold Layer로 변환
12. 집계 — groupby
범주형 컬럼(명목형/순서형)을 기준으로 수치형 컬럼을 요약.
12.1 단일 컬럼 집계
tmp = transaction_data_mart.groupby(['is_mz_gen']).count()[['age']]
tmp.columns = ['고객수']
- groupby([컬럼]) — 그룹 기준 지정
- count() — 그룹별 개수 산출
- [['age']] — 결과에서 특정 컬럼만 추출 (차원 유지)
- 컬럼명 재설정 — 의미 있는 한글 컬럼명으로 변환
12.2 다중 컬럼 집계
tmp = transaction_data_mart.groupby(['is_mz_gen', 'gender']).count()[['age']]
tmp.columns = ['고객수']
- 다중 기준 — 리스트에 컬럼 여러 개 지정
- MultiIndex 결과 — 인덱스가 계층 구조로 생성됨
12.3 agg() — 다중 통계 함수
tmp = transaction_data_mart.groupby(['gender']).agg({
'age': 'mean', # 그룹 평균 연령
'cal_price': 'max', # 건당 최고 매출액
'transaction_id': 'count' # 트랜잭션 건수
})
tmp.columns = ['평균연령', '제품당최고구매액', '그룹의속한맴버수']
- 딕셔너리 형식 — 컬럼명: 집계함수 매핑
- 집계 함수 종류 — mean, max, min, sum, count, std, var, median 등
- 함수명 문자열 또는 함수 객체 — 'mean' 또는 np.mean 모두 가능
집계는 데이터에서 의미 있는 지표를 뽑는 핵심 단계. 어떤 컬럼을 기준으로 묶을지가 Gold Layer 설계의 시작.
12.4 시간 기반 집계
# pay_month 파생 변수 생성 (예: '202605')
transaction_data_mart['payment_date'] = pd.to_datetime(transaction_data_mart.payment_date)
transaction_data_mart['pay_month'] = transaction_data_mart.payment_date.dt.strftime('%Y%m')
# 월별 + 제품별 집계
tmp = transaction_data_mart.groupby(['pay_month', 'item_id']).agg({
'cal_price': 'sum',
'item_id': 'count',
'quantity': 'sum',
'item_price': 'mean'
})
- 시간 → 범주형 변환 — datetime을 문자열 포맷으로 명목화하면 그룹 키로 사용 가능
- 활용 예 — 월별 매출 추이, 제품별 판매량 분석
13. 피벗 테이블
데이터 재배치를 통해 내재된 의미를 추출하는 집계 기법.
13.1 피벗 대상 컬럼 조건
- 범주형이 기본 — 명목형/순서형
- 고유값 개수 최소 — 너무 많으면 결과가 복잡해짐
13.2 기본 사용법
df_pv = pd.pivot_table(df, index=['Name'], values=['Quantity', 'Price'])
- index — 인덱스로 배치할 컬럼 (그룹 기준)
- values — 집계 대상 수치 컬럼
- 기본 집계 함수 — mean (지정하지 않으면 평균)
- 타입 변화 — 정수형 데이터도 평균 계산으로 부동소수점화
13.3 계층형 인덱스
df_pv = pd.pivot_table(df, index=['Manager', 'Rep', 'Name'], values=['Quantity', 'Price'])
- 계층 구조 — 인덱스 리스트 순서대로 상위 → 하위 구성
- 활용 예 — 영업 조직 구조 반영 (Manager → Rep → Name)
- 요약 단위 조정 — 상위 레벨만 지정하면 더 큰 집계 단위
13.4 다중 집계 함수
df_pv = df.pivot_table(
index=['Manager', 'Rep', 'Name'],
values=['Price'],
aggfunc=['mean', len, 'sum']
)
- aggfunc — 적용할 집계 함수 리스트
- 혼합 표기 — 문자열('mean', 'sum')과 내장 함수(len) 모두 가능
- 활용 예 — 평균/건수/총액을 동시에 보여주는 인사 평가 지표
13.5 columns로 펼치기
df_pv = df.pivot_table(
index=['Manager', 'Rep', 'Name'],
values=['Price'],
columns=['Product'],
aggfunc=['mean', len, 'sum']
)
- columns — 컬럼 방향으로 펼칠 범주형 컬럼
- 결측 발생 — 모든 영업사원이 모든 제품을 팔지 않을 때 빈 칸 생성
- 분석 가치 — 주력 품목/약점 품목 식별, 영업 성향 파악
13.6 결측 처리
# 옵션 1: 피벗 시점에 채우기
df_pv = df.pivot_table(..., fill_value=0)
# 옵션 2: 사후 처리
df_pv.fillna(0, inplace=True)
- fill_value — 피벗 결과의 결측을 즉시 치환
- fillna(inplace=True) — 생성된 피벗 결과를 후처리로 채움
- 0 외 다른 값 — 평균값, 'N/A' 등 도메인에 맞게 선택
groupby와 pivot_table은 결국 같은 집계 작업이지만, 피벗은 컬럼 방향 펼치기까지 한 번에 처리할 수 있어 Gold Layer 분석 보고서에 더 적합함.
요약
| 개념 | 핵심 |
| Series / DataFrame | 1차원/2차원 자료구조. numpy ndarray + 인덱스(+컬럼) |
| 기본 점검 | shape, dtypes, describe, info — 메모리 로드 직후 필수 확인 |
| 열 인덱싱 | df['컬럼'] (Series), df[['컬럼']] (DataFrame 유지) |
| 행 슬라이싱 | df[start:end:step] — 암묵적 위치 기반, 차원 유지 |
| loc | 라벨 기반. 오른쪽 경계 포함 |
| iloc | 위치 기반. 팬시 인덱싱 지원 |
| 불리언 인덱싱 | loc + 조건식. &, ` |
| 파생 변수 | apply / str / dt — 콜백, 벡터화, 시간 처리 |
| concat / merge | 단순 결합 / 키 기반 조인 |
| 중복 검증 | 합계 비교, drop_duplicates로 정제 |
| groupby | 범주형 기준 수치 집계. agg로 다중 함수 |
| pivot_table | 인덱스 + 컬럼 펼치기. Gold Layer 분석에 적합 |
pandas는 numpy 기반의 표준 데이터 분석 도구로, 로드 → 인덱싱/필터링 → 파생변수 → 병합 → 집계/피벗의 흐름을 통해 Raw 데이터를 Silver/Gold Layer로 정제하는 전 과정을 단일 인터페이스로 책임짐.
'SK플래닛 ai활용 데이터엔지니어 과정 2기 > 데이터 처리' 카테고리의 다른 글
| 데이터 처리 6 - EMR + Spark (0) | 2026.05.19 |
|---|---|
| 데이터 처리 5 - Docker Spark (0) | 2026.05.19 |
| 데이터 처리 4 - Apache Spark (0) | 2026.05.18 |
| 데이터 처리 3 - polars (1) | 2026.05.14 |
| 데이터 처리 1 - Numpy (0) | 2026.05.13 |