본문으로 건너뛰기

pandas 100제: 판다스 연습문제와 풀이 · 7장

051–060 결측치와 중복

isna, any, dropna, subset, fillna, 평균·그룹 평균으로 채우기, ffill, duplicated, drop_duplicates 연습문제

8분 읽기

이 장의 목차 13개

빈 값(NaN)과 중복 행을 다루는 전처리 문제입니다. 준비 코드의 표에는 빈 값과 중복 행이 섞여 있습니다.

결측치 칸과 중복 행을 색으로 표시한 준비 데이터
결측치 칸과 중복 행을 색으로 표시한 준비 데이터

준비 데이터에는 빈 값(붉은 칸)과 완전히 같은 행(갈색)이 섞여 있습니다. 숫자 열의 빈 값은 NaN, 문자열 열의 빈 값은 pandas 2에서 None, pandas 3에서 NaN으로 보이지만 둘 다 isna()가 결측치로 셉니다. 빈 값을 지울지, 0으로 채울지, 평균으로 채울지는 문법이 아니라 데이터의 의미로 정해야 한다는 점을 기억하세요.

준비 코드와 데이터

노트북 첫 셀에 아래 준비 코드를 붙여 넣어 실행한 뒤 문제를 푸세요.

Pythonimport pandas as pd
import numpy as np

df = pd.DataFrame({
    'name': ['김민준', '이서연', '박지호', '최수아', '정민서', '최수아'],
    'class': ['A', 'B', 'A', 'C', 'B', 'C'],
    'score': [85, np.nan, 58, 77, np.nan, 77],
    'memo': ['우수', None, None, '성실', '결석', '성실'],
})

이 장 실습 노트북 내려받기 (.ipynb) — 준비 코드와 문제가 들어 있고 답 칸은 비어 있습니다. 코랩에서는 파일 → 노트 업로드로 엽니다.

준비 코드를 실행하면 df는 다음 표가 됩니다(6행 × 4열, 맨 왼쪽 숫자는 인덱스).

결과     name class  score  memo
0  김민준     A   85.0  우수
1  이서연     B    NaN   NaN
2  박지호     A   58.0   NaN
3  최수아     C   77.0  성실
4  정민서     B    NaN  결석
5  최수아     C   77.0  성실

장 안의 문제는 번호 순서대로 이어집니다. 앞 문제에서 만든 열이나 변수를 다음 문제가 쓸 수 있으니, 중간부터 풀 때는 준비 코드와 앞의 필요한 문제를 먼저 실행하세요.

051. 열별 결측치 개수

열마다 빈 값이 몇 개인지 구하세요.

힌트 보기

isna()는 칸마다 True/False를 돌려줍니다. 열마다 더하면 결측치 수가 됩니다.

정답 보기
Pythondf.isna().sum()
결과name     0
class    0
score    2
memo     2
dtype: int64

풀이: isna가 만든 불리언 표를 열별로 더해 빠진 값 수를 셉니다. 빈 문자열은 NaN과 다릅니다.

052. 결측치가 있는 행

빈 값이 하나라도 있는 행을 고르세요.

힌트 보기

isna().any(axis=1) — 행마다 하나라도 True인지 봅니다.

정답 보기
Pythondf[df.isna().any(axis=1)]
결과     name class  score  memo
1  이서연     B    NaN   NaN
2  박지호     A   58.0   NaN
4  정민서     B    NaN  결석

풀이: any(axis=1)는 각 행에서 하나라도 결측치인지 확인합니다.

pandas 2.x에서는 값은 같고 표시만 다음과 같습니다.

결과     name class  score  memo
1  이서연     B    NaN  None
2  박지호     A   58.0  None
4  정민서     B    NaN  결석

053. 결측치 행 지우기

빈 값이 있는 행을 모두 지우세요.

힌트 보기

dropna()를 인자 없이 쓰면 결측치가 하나라도 있는 행을 지웁니다.

정답 보기
Pythondf.dropna()
결과     name class  score  memo
0  김민준     A   85.0  우수
3  최수아     C   77.0  성실
5  최수아     C   77.0  성실

풀이: dropna 기본 동작은 결측치가 하나라도 있는 행 제거입니다. 남길 관측치가 과도하게 줄지 확인합니다.

054. 특정 열 기준으로 지우기

score가 빈 행만 지우세요.

힌트 보기

dropna(subset=["열"]).

정답 보기
Pythondf.dropna(subset=['score'])
결과     name class  score  memo
0  김민준     A   85.0  우수
2  박지호     A   58.0   NaN
3  최수아     C   77.0  성실
5  최수아     C   77.0  성실

풀이: subset으로 삭제 판단에 사용할 열을 제한하므로 다른 열의 결측치 때문에 행을 버리지 않습니다.

memo만 빈 행은 남습니다.

pandas 2.x에서는 값은 같고 표시만 다음과 같습니다.

결과     name class  score  memo
0  김민준     A   85.0  우수
2  박지호     A   58.0  None
3  최수아     C   77.0  성실
5  최수아     C   77.0  성실

055. 0으로 채우기

score의 빈 값을 0으로 채운 Series를 구하세요(원본은 그대로 둡니다).

힌트 보기

fillna(0).

정답 보기
Pythondf['score'].fillna(0)
결과0    85.0
1     0.0
2    58.0
3    77.0
4     0.0
5    77.0
Name: score, dtype: float64

풀이: 0을 넣으면 평균 등 후속 계산이 달라집니다. 결측치가 실제 0을 의미하는지 먼저 확인합니다.

056. 평균으로 채우기

score의 빈 값을 평균으로 채운 Series를 구하세요.

힌트 보기

fillna() 괄호 안에 df["score"].mean()을 넣습니다.

정답 보기
Pythondf['score'].fillna(df['score'].mean())
결과0    85.00
1    74.25
2    58.00
3    77.00
4    74.25
5    77.00
Name: score, dtype: float64

풀이: mean은 결측치를 제외한 값으로 계산합니다. 중복 행도 포함되므로 전처리 순서를 확인합니다.

057. 그룹 평균으로 채우기

score의 빈 값을 같은 반의 평균으로 채우세요. 같은 반에 값이 없으면 빈 값으로 남습니다.

힌트 보기

groupby("class")["score"].transform("mean")은 행마다 그 반의 평균을 돌려줍니다. 이것을 fillna에 넣으세요.

정답 보기
Pythondf['score'].fillna(df.groupby('class')['score'].transform('mean'))
결과0    85.0
1     NaN
2    58.0
3    77.0
4     NaN
5    77.0
Name: score, dtype: float64

풀이: transform 결과는 원래 인덱스에 맞춰집니다. 그룹 안 유효 점수가 없으면 평균도 NaN입니다.

transform은 그룹 결과를 원래 행 수에 맞춰 돌려줍니다. B반은 점수가 모두 비어 있어 채워지지 않습니다.

058. 앞의 값으로 채우기

memo의 빈 값을 바로 위 행의 값으로 채우세요.

힌트 보기

바로 위 값으로 채우는 함수는 ffill()입니다.

정답 보기
Pythondf['memo'].ffill()
결과0    우수
1    우수
2    우수
3    성실
4    결석
5    성실
Name: memo, dtype: str

풀이: ffill은 현재 행 순서에 의존합니다. 이 예제에서는 문법을 연습하며 실제 서로 다른 사람의 메모를 임의로 이월하면 안 됩니다.

pandas 2.x에서는 마지막 줄이 dtype: object로 보입니다. 값은 같습니다.

059. 중복 행 찾기

앞에서 이미 나온 행과 완전히 같은 중복 행이 몇 개인지 세고, 그 행을 보이세요.

힌트 보기

duplicated()는 행마다 "앞에 같은 행이 있었나"를 True/False로 돌려줍니다.

정답 보기
Pythonprint(df.duplicated().sum())
df[df.duplicated()]
결과1
     name class  score  memo
5  최수아     C   77.0  성실

풀이: duplicated는 기본적으로 첫 등장은 남기고 뒤의 완전 중복을 표시합니다. 바로 앞 행과 같아야 하는 것은 아닙니다.

060. 기준 열로 중복 지우기

name이 같은 행은 마지막 행만 남기세요.

힌트 보기

drop_duplicates(subset="name", keep="last").

정답 보기
Pythondf.drop_duplicates(subset='name', keep='last')
결과     name class  score  memo
0  김민준     A   85.0  우수
1  이서연     B    NaN   NaN
2  박지호     A   58.0   NaN
4  정민서     B    NaN  결석
5  최수아     C   77.0  성실

풀이: name만 중복 기준으로 보고 현재 순서의 마지막 행을 남깁니다. 최신 날짜를 의미하려면 먼저 날짜로 정렬해야 합니다.

pandas 2.x에서는 값은 같고 표시만 다음과 같습니다.

결과     name class  score  memo
0  김민준     A   85.0  우수
1  이서연     B    NaN  None
2  박지호     A   58.0  None
4  정민서     B    NaN  결석
5  최수아     C   77.0  성실

이 장 요약

문법하는 일문제
df.isna().sum()열별 결측치 수051
df[df.isna().any(axis=1)]결측치가 있는 행052
df.dropna() / dropna(subset=[열])결측 행 지우기(전체 / 특정 열 기준)053–054
s.fillna(값)빈 값 채우기055–056
s.fillna(df.groupby(g)[열].transform("mean"))그룹 평균으로 채우기057
s.ffill()위 값으로 채우기058
df.duplicated()앞에 같은 행이 있었나059
df.drop_duplicates(subset=, keep=)중복 지우기060

이 장의 점검

빈 값을 0으로 바꾸기·평균으로 채우기·행 삭제가 분석에 주는 차이를 설명하세요. 중복과 결측치 처리에는 실제 기록 의미가 필요합니다.

참고: pandas 공식 문서 · 문법이 기억나지 않으면 부록 치트시트를 찾아보세요.

전체 메뉴