[Python 100일 챌린지] Day 65 - Pandas 데이터 조작
데이터를 자유자재로 다뤄봅시다! 🔧
필터링, 정렬, 열 추가/삭제… 실무에서 매일 쓰는 데이터 조작 기법을 배워요! 이것만 알면 웬만한 데이터 전처리는 가능합니다! 💪
(30-40분 완독 ⭐⭐)
🎯 오늘의 학습 목표
📚 사전 지식
- Day 63: Pandas 기초 - DataFrame 기본
- Day 64: Pandas 데이터 로딩 - 파일 읽기/쓰기
🎯 학습 목표 1: 데이터 필터링하기
💡 필터링이란?
비유: 도서관에서 “파이썬 책만 찾아줘”라고 하는 것과 같아요! 📚
전체 데이터에서 원하는 조건에 맞는 데이터만 골라내는 거예요.
1.1 조건으로 필터링
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import pandas as pd
df = pd.DataFrame({
'이름': ['철수', '영희', '민수', '지영', '현수'],
'나이': [20, 22, 21, 23, 20],
'점수': [85, 90, 78, 92, 88],
'학과': ['컴퓨터', '경영', '컴퓨터', '경영', '전자']
})
# 단일 조건
high_score = df[df['점수'] >= 85]
print(high_score)
# 여러 조건 (AND)
result = df[(df['점수'] >= 85) & (df['나이'] >= 21)]
print(result)
# 여러 조건 (OR)
result = df[(df['학과'] == '컴퓨터') | (df['학과'] == '경영')]
print(result)
💡 조건 연결 기호 정리
기호 의미 예시 설명 &AND (그리고) 조건1 & 조건2둘 다 만족해야 함 \|OR (또는) 조건1 \| 조건2하나만 만족해도 됨 ~NOT (부정) ~조건조건의 반대 ⚠️ 주의: 여러 조건을 쓸 때는 각 조건을 괄호
()로 감싸야 해요!
- ✅
df[(조건1) & (조건2)]- ❌
df[조건1 & 조건2]← 오류 발생!
1
2
3
# 여러 조건 예제 (참고용)
# NOT 연산: 점수가 80점 이상이 "아닌" 학생
low_score = df[~(df['점수'] >= 80)] # 80점 미만
1.2 isin()으로 필터링
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pandas as pd
df = pd.DataFrame({
'이름': ['철수', '영희', '민수', '지영', '현수'],
'학과': ['컴퓨터', '경영', '컴퓨터', '경영', '전자']
})
# 특정 값들 중 하나와 일치하는 행
result = df[df['학과'].isin(['컴퓨터', '전자'])]
print(result)
# 포함되지 않는 행
result = df[~df['학과'].isin(['경영'])]
print(result)
1.3 문자열 조건
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pandas as pd
df = pd.DataFrame({
'이름': ['김철수', '이영희', '박민수', '김지영', '최현수'],
'이메일': ['kim@a.com', 'lee@b.com', 'park@a.com', 'kim2@c.com', 'choi@b.com']
})
# 특정 문자 포함
result = df[df['이름'].str.contains('김')]
print(result)
# 특정 문자로 시작
result = df[df['이메일'].str.startswith('kim')]
print(result)
# 특정 문자로 끝남
result = df[df['이메일'].str.endswith('.com')]
print(result)
1.4 query() 메서드
💡 query()가 편한 이유?
방식 코드 특징 기본 방식 df[(df['점수'] >= 85) & (df['나이'] >= 21)]길고 복잡함 query() df.query('점수 >= 85 and 나이 >= 21')SQL처럼 직관적! 조건이 많을수록
query()가 훨씬 읽기 쉬워요!
1
2
3
4
5
6
7
8
9
10
11
12
13
import pandas as pd
df = pd.DataFrame({
'이름': ['철수', '영희', '민수'],
'나이': [20, 22, 21],
'점수': [85, 90, 78]
})
# SQL 스타일 쿼리
result = df.query('점수 >= 85')
result = df.query('나이 >= 21 and 점수 >= 80')
result = df.query('이름 == "철수"')
print(result)
💡 query() 안에서는
and,or사용!
- 기본 필터링:
&,|(기호)- query():
and,or(영어 단어)
🎯 학습 목표 2: 데이터 정렬하기
💡 정렬 용어 정리
용어 영어 순서 예시 오름차순 ascending 작은 → 큰 1, 2, 3 또는 A, B, C 내림차순 descending 큰 → 작은 3, 2, 1 또는 C, B, A 기본값은 오름차순이에요!
2.1 단일 열 정렬
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pandas as pd
df = pd.DataFrame({
'이름': ['철수', '영희', '민수', '지영'],
'점수': [85, 90, 78, 92]
})
# 오름차순 정렬
sorted_df = df.sort_values('점수')
print(sorted_df)
# 내림차순 정렬
sorted_df = df.sort_values('점수', ascending=False)
print(sorted_df)
2.2 여러 열 정렬
1
2
3
4
5
6
7
8
9
10
11
import pandas as pd
df = pd.DataFrame({
'학과': ['컴퓨터', '경영', '컴퓨터', '경영'],
'이름': ['철수', '영희', '민수', '지영'],
'점수': [85, 90, 78, 92]
})
# 학과로 먼저 정렬, 같으면 점수로 정렬
sorted_df = df.sort_values(['학과', '점수'], ascending=[True, False])
print(sorted_df)
2.3 인덱스 정렬
1
2
3
4
5
6
7
8
9
import pandas as pd
df = pd.DataFrame({
'A': [3, 1, 2]
}, index=['c', 'a', 'b'])
# 인덱스 기준 정렬
sorted_df = df.sort_index()
print(sorted_df)
🎯 학습 목표 3: 열 추가/수정/삭제하기
3.1 열 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import pandas as pd
df = pd.DataFrame({
'이름': ['철수', '영희', '민수'],
'국어': [85, 90, 78],
'영어': [90, 85, 92]
})
# 새 열 추가 (계산)
df['총점'] = df['국어'] + df['영어']
df['평균'] = df['총점'] / 2
# 조건에 따른 값
df['합격'] = df['평균'] >= 80
print(df)
3.2 열 수정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import pandas as pd
df = pd.DataFrame({
'이름': ['철수', '영희', '민수'],
'점수': [85, 90, 78]
})
# 전체 열 수정
df['점수'] = df['점수'] + 5 # 전체 5점 추가
# 조건부 수정 (loc 사용 권장!)
df.loc[df['점수'] < 80, '점수'] = 80 # 80점 미만은 80점으로
print(df)
⚠️ 조건부 수정 시
loc사용하기:df[조건]['열'] = 값대신df.loc[조건, '열'] = 값을 사용하세요! 전자는 경고(SettingWithCopyWarning)가 발생할 수 있어요.
3.3 열 삭제
💡 axis란?
Pandas에서 방향을 나타내는 값이에요:
axis 의미 시각적 방향 axis=0행 (row) ↓ 세로 방향 axis=1열 (column) → 가로 방향 열을 삭제하려면
axis=1!
1
2
3
4
5
6
7
8
9
10
11
12
13
import pandas as pd
# 예제 1: 단일 열 삭제
df1 = pd.DataFrame({
'이름': ['철수', '영희'],
'나이': [20, 22],
'임시': [1, 2]
})
df1 = df1.drop('임시', axis=1)
# 또는 (더 직관적인 방법)
# df1 = df1.drop(columns=['임시'])
print(df1)
1
2
3
4
5
6
7
8
9
10
11
12
import pandas as pd
# 예제 2: 여러 열 삭제
df2 = pd.DataFrame({
'이름': ['철수', '영희'],
'나이': [20, 22],
'점수': [85, 90],
'임시': [1, 2]
})
df2 = df2.drop(columns=['나이', '임시'])
print(df2) # 이름, 점수만 남음
3.4 열 이름 변경
1
2
3
4
5
6
7
8
9
10
11
12
13
import pandas as pd
df = pd.DataFrame({
'name': ['철수', '영희'],
'age': [20, 22]
})
# 이름 변경
df = df.rename(columns={'name': '이름', 'age': '나이'})
print(df)
# 전체 열 이름 변경
df.columns = ['이름', '나이']
🎯 학습 목표 4: 결측값 처리하기
💡 결측값(Missing Value)이란?
비유: 설문조사에서 “응답 없음”과 같아요! 📋
데이터에 값이 없는 것을 말해요. Pandas에서는
NaN(Not a Number) 또는None으로 표시돼요.
1 2 3 예: 학생 명단에서 전화번호를 안 적은 학생 이름: 철수, 전화번호: 010-1234-5678 이름: 영희, 전화번호: NaN ← 결측값!결측값은 분석 결과를 왜곡할 수 있어서, 적절히 처리해야 해요!
4.1 결측값 확인
1
2
3
4
5
6
7
8
9
10
11
12
13
import pandas as pd
import numpy as np
df = pd.DataFrame({
'이름': ['철수', '영희', '민수', None],
'나이': [20, np.nan, 21, 22],
'점수': [85, 90, np.nan, 92]
})
# 결측값 확인
print(df.isnull()) # True/False 표시
print(df.isnull().sum()) # 열별 결측값 개수
print(df.isnull().sum().sum()) # 전체 결측값 개수
4.2 결측값 삭제
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import pandas as pd
import numpy as np
df = pd.DataFrame({
'이름': ['철수', '영희', '민수', None],
'나이': [20, np.nan, 21, 22],
'점수': [85, 90, np.nan, 92]
})
# 결측값이 있는 행 삭제
df_cleaned = df.dropna()
print(df_cleaned)
# 특정 열에 결측값이 있는 행만 삭제
df_cleaned = df.dropna(subset=['이름'])
# 모든 값이 결측인 행만 삭제
df_cleaned = df.dropna(how='all')
4.3 결측값 채우기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import pandas as pd
import numpy as np
df = pd.DataFrame({
'이름': ['철수', '영희', '민수'],
'점수': [85, np.nan, 78]
})
# 특정 값으로 채우기
df_filled = df.fillna(0)
# 평균으로 채우기
df_filled = df.fillna(df['점수'].mean())
# 앞의 값으로 채우기 (forward fill)
df_filled = df.ffill() # Pandas 2.0+ 권장 방식
# 뒤의 값으로 채우기 (backward fill)
df_filled = df.bfill() # Pandas 2.0+ 권장 방식
print(df_filled)
💡 Pandas 2.0+ 참고:
fillna(method='ffill')은 deprecated 되었어요. 대신df.ffill(),df.bfill()을 사용하세요!
💡 결측값 처리 방법 선택 가이드
상황 권장 방법 이유 결측값이 적을 때 dropna()데이터 손실이 적음 결측값이 많을 때 fillna(평균/중앙값)데이터 보존 시계열 데이터 ffill()/bfill()연속성 유지 카테고리 데이터 fillna('없음')명시적 표시
💡 실전 팁
✅ 체이닝 (연속 작업)
💡 체이닝이란?
비유: 세탁기 → 건조기 → 옷장 순서로 한 번에 처리하는 것! 🧺
여러 작업을 점(.)으로 연결해서 한 줄에 처리하는 방식이에요.
- 장점: 코드가 깔끔하고 중간 변수가 필요 없어요
- 단점: 너무 길면 읽기 어려울 수 있어요
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import pandas as pd
df = pd.DataFrame({
'이름': ['철수', '영희', '민수', '지영'],
'점수': [85, 90, 78, 92],
'학과': ['컴퓨터', '경영', '컴퓨터', '경영']
})
# 메서드 체이닝으로 깔끔하게
result = (df
.query('점수 >= 80') # 1) 80점 이상만
.sort_values('점수', ascending=False) # 2) 점수 내림차순
.reset_index(drop=True) # 3) 인덱스 새로 매기기
)
print(result)
💡 reset_index(drop=True)란?
필터링/정렬 후 인덱스가 뒤죽박죽이 돼요:
1 2 원래: 0, 1, 2, 3, 4 필터 후: 0, 1, 3, 4 ← 2가 빠짐!
reset_index(drop=True)로 0, 1, 2, 3처럼 깔끔하게 다시 매겨요!
drop=True: 원래 인덱스는 버림drop=False: 원래 인덱스를 새 열로 추가
🧪 연습 문제
문제: 데이터 전처리 실습
주어진 데이터에서 1) 점수가 80점 이상인 학생만 필터링, 2) 점수 기준 내림차순 정렬, 3) ‘등급’ 열 추가(90점 이상 A, 80점 이상 B)
1
2
3
4
df = pd.DataFrame({
'이름': ['철수', '영희', '민수', '지영', '현수'],
'점수': [85, 90, 78, 92, 88]
})
💡 힌트 보기
단계별 접근법:
- 80점 이상 필터링
df[df['점수'] >= 80]형태로 조건 필터링
- 내림차순 정렬
sort_values('점수', ascending=False)사용
- 등급 추가하기
- 방법 1:
np.where()사용 -np.where(조건, 참일때값, 거짓일때값) - 방법 2:
apply()+ 람다 함수 사용
- 방법 1:
핵심 코드 구조:
1
2
3
# 필터링 → 정렬 → 새 열 추가
result = df[조건].sort_values(...).copy()
result['등급'] = np.where(...)
✅ 정답 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import pandas as pd
import numpy as np
df = pd.DataFrame({
'이름': ['철수', '영희', '민수', '지영', '현수'],
'점수': [85, 90, 78, 92, 88]
})
# 1. 80점 이상 필터링
result = df[df['점수'] >= 80]
# 2. 점수 내림차순 정렬
result = result.sort_values('점수', ascending=False)
# 3. 등급 추가
result['등급'] = np.where(result['점수'] >= 90, 'A', 'B')
# 인덱스 리셋
result = result.reset_index(drop=True)
print(result)
📝 오늘 배운 내용 정리
- 필터링:
df[조건],df.query(),isin() - 정렬:
sort_values(),sort_index() - 열 조작: 추가(
df['새열']=값), 삭제(drop()), 이름변경(rename()) - 결측값:
isnull(),dropna(),fillna()
🔗 관련 자료
📚 이전 학습
Day 64: Pandas 데이터 로딩 ⭐⭐
어제는 CSV, Excel 파일 읽기/쓰기를 배웠어요!
📚 다음 학습
Day 66: Pandas 그룹화와 집계 ⭐⭐
“늦었다고 생각할 때가 가장 빠른 때입니다. 오늘도 한 걸음 성장했어요!” 🚀
Day 65/100 Phase 7: 데이터 분석 기초 #100DaysOfPython
