AICE (에이스) 시험 노트개념 정리14 MIN
이상치 처리와 불필요한 열 정리
AICE Associate 전처리 과목에서 표를 실제로 깎는 자리입니다. IQR 경계로 clip 하기, 조건으로 행 지우기, drop으로 버릴 열 고르기, replace와 to_numeric으로 자료형 되돌리기, drop_duplicates를 연습 문제 7개로 짚습니다.
빈 칸을 메웠으면 남은 것은 셋입니다 — 튀는 값, 쓸모없는 열, 겹친 행입니다. 셋 다 앞 과목에서 이미 찾아 두었고 여기서는 실제로 손봅니다. AICE Associate는 「이상치를 IQR 기준으로 처리하시오」·「모델링에 불필요한 열을 제거하시오」처럼 한 문항에 한 걸음씩 냅니다.
손보는 일에는 되돌릴 수 없다는 성질이 있습니다. 행을 지우면 그 행의 다른 열 값까지 함께 사라지고, 열을 지우면 뒤에 가서 필요해져도 다시 만들 수 없습니다. 그래서 원본을 따로 두고 사본을 고치는 습관이 이 과목의 바탕입니다.
data = df.copy()
copy() 없이 data = df라고 적으면 같은 표에 이름만 둘 붙는 것이라, 한쪽을 고치면 다른 쪽도 바뀝니다.
이상치 처리의 세 갈래
오류인가 극단값인가
먼저 가르는 것은 그 값이 있을 수 없는 값인가입니다. 나이 -3이나 999, 주문 금액 0은 들어올 수 없는 값이라 오류이고, 소득 5억은 드물지만 실제로 있을 수 있는 극단값입니다.
오류는 잘못 들어온 것이니 지우거나 결측으로 바꿔 다시 채웁니다. 극단값은 사실이므로 함부로 지우면 그 사실을 버리는 것이 됩니다.
세 갈래
처리 방법은 셋이고 잃는 것이 각각 다릅니다.
| 방법 | 하는 일 | 잃는 것 |
|---|---|---|
| 남기기 | 그대로 둔다 | 모델이 그 값에 끌려간다 |
| 자르기 | 경계값으로 바꾼다 | 원래 크기의 정보 |
| 지우기 | 그 행을 뺀다 | 그 행의 다른 열 값 전부 |
행 수가 넉넉하고 값이 명백한 오류면 지우고, 행 수가 아까우면 자릅니다. 별말이 없으면 IQR 경계로 자르는 쪽이 무난합니다 — 행이 안 줄고 극단값의 영향만 눌리기 때문입니다.
clip
경계로 자르기
clip은 지정한 구간 밖의 값을 구간의 끝값으로 바꿉니다. 아래 경계보다 작으면 아래 경계로, 위 경계보다 크면 위 경계로 바뀌고 안쪽 값은 그대로입니다.
Q1 = data['Age'].quantile(0.25)
Q3 = data['Age'].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
data['Age'] = data['Age'].clip(lower, upper)
경계를 구하는 네 줄은 앞 글의 IQR 계산 그대로입니다. 달라진 것은 찾기만 하던 것을 값에 실제로 적용한다는 점뿐입니다.
자르고 나서 describe()를 다시 찍으면 min과 max가 경계값에 딱 붙어 있는 것이 보입니다. 그 값이 여럿 생기는 것이 정상입니다 — 경계 밖에 있던 값이 전부 같은 값으로 모이기 때문입니다.
한쪽만
위쪽만 자르고 싶으면 아래 자리에 None을 줍니다.
data['Income'] = data['Income'].clip(None, upper)
data['Age'] = data['Age'].clip(0, None) # 음수만 0으로
나이나 금액처럼 음수가 있을 수 없는 열은 아래를 0으로 막아 두는 것만으로도 오류가 정리됩니다.
조건으로 행 지우기
남길 조건으로 적기
행을 지우는 가장 짧은 방법은 지울 조건이 아니라 남길 조건을 불리언 인덱싱으로 적는 것입니다.
data = data[(data['Age'] >= lower) & (data['Age'] <= upper)]
data = data[data['Age'] > 0]
앞 과목의 조건 필터와 완전히 같은 문법입니다. 조건마다 괄호를 씌우고 &로 잇는 것도 같습니다.
지운 뒤에는 인덱스에 구멍이 납니다. 원래 인덱스를 유지하기 때문인데, 뒤에서 순서로 접근할 일이 있으면 다시 매깁니다.
data = data.reset_index(drop=True)
drop=True를 빼면 옛 인덱스가 index라는 열로 표에 남습니다.
drop으로 지우기
인덱스를 직접 지정해 지울 수도 있습니다. 이쪽은 지울 행이 몇 개로 특정될 때 씁니다.
outlier_idx = data[data['Age'] > upper].index
data = data.drop(outlier_idx)
drop은 행에 쓸 때 axis=0이 기본값이라 따로 적지 않아도 됩니다.
열 정리
drop
열은 axis=1을 주어 지웁니다.
data = data.drop(['ID', 'Name'], axis=1)
data = data.drop(columns=['ID', 'Name'])
두 줄은 같은 일을 합니다. columns=를 쓰면 axis를 안 적어도 되고 무엇을 지우는지가 읽기에 분명합니다.
버릴 열
모델링에 도움이 안 되는 열은 대개 넷입니다.
- 식별자 — 고객 번호·주문 번호처럼 행마다 다른 값. 예측에 쓸 규칙이 없습니다.
- 상수 열 — 모든 행의 값이 같은 열.
data['열'].nunique()가 1이면 그렇습니다. - 중복 열 — 다른 열과 뜻이 같은 열. 생년과 나이가 함께 있는 경우입니다.
- 타깃이 새는 열 — 결과가 나온 뒤에야 알 수 있는 값. 해지 여부를 맞히는데 「해지 사유」가 들어 있으면 그 열 하나로 답이 정해집니다.
마지막 것이 가장 놓치기 쉽습니다. 점수가 이상하게 높게 나오면 이런 열이 섞여 있는지부터 봅니다.
자료형 되돌리기
replace
숫자여야 할 열이 object로 들어온 원인은 값에 섞인 글자였습니다. 그 글자를 먼저 걷어냅니다.
data['Income'] = data['Income'].str.replace(',', '')
data['Grade'] = data['Grade'].replace('-', np.nan)
data['Gender'] = data['Gender'].replace({'남': 'M', '여': 'F'})
.str.replace는 문자열 안의 일부를 바꾸고, .replace는 칸의 값 전체가 일치할 때 바꿉니다. 천 단위 쉼표는 값의 일부라 앞쪽을, 빈 값 표시로 쓰인 '-'는 칸 전체라 뒤쪽을 씁니다. 딕셔너리를 주면 여러 값을 한 번에 바꿉니다.
to_numeric
글자를 걷어냈으면 자료형을 숫자로 바꿉니다.
data['Income'] = pd.to_numeric(data['Income'], errors='coerce')
errors='coerce'가 핵심입니다. 숫자로 못 바꾸는 값을 만나면 오류를 내는 대신 NaN으로 바꿔 줍니다. 그래서 이 줄을 돌린 뒤에는 결측이 늘어날 수 있고, 앞 글의 fillna로 한 번 더 메워야 합니다.
순서가 정해져 있습니다 — 글자를 걷어내고, 숫자로 바꾸고, 그때 생긴 빈 칸을 채웁니다. 순서를 바꾸면 to_numeric이 쉼표 때문에 멀쩡한 값까지 NaN으로 만듭니다.
drop_duplicates
keep
drop_duplicates()는 겹친 행을 지웁니다. 인자는 앞 과목에서 세는 데 쓴 duplicated()와 같습니다.
data = data.drop_duplicates()
data = data.drop_duplicates(keep='last')
keep='first'가 기본값이라 처음 나온 행을 남깁니다. 같은 고객의 기록 중 최신 것을 남기려면 시각 순으로 정렬한 뒤 keep='last'를 줍니다.
keep=False는 주의해서 씁니다. 겹친 행을 원본까지 전부 지우므로 두 번 나온 값이 표에서 통째로 사라집니다. 중복을 없애려다 그 값 자체를 없애는 셈이라, 의도한 것이 아니면 쓰지 않습니다.
subset과 인덱스
기준 열을 좁히는 것도 duplicated()와 같습니다.
data = data.drop_duplicates(subset=['CustomerID'], keep='last')
data = data.drop_duplicates(ignore_index=True)
고객 번호만 기준으로 삼으면 접속 시각이나 금액이 달라도 같은 행으로 봅니다. 전체 열로는 하나도 안 지워지는데 subset으로는 수십 개가 지워지는 일이 흔하므로, 무엇을 기준으로 삼는지를 문항에서 먼저 읽습니다.
ignore_index=True를 주면 지운 뒤 인덱스를 0부터 다시 매겨 줍니다. reset_index(drop=True)를 따로 부르는 것과 같습니다.
이 영역의 출제 형태는 대개 셋입니다. IQR 경계를 주고 clip 한 줄을 채우게 하거나, 열 목록을 주고 버릴 열을 고르게 하거나, object로 들어온 열을 숫자로 되돌리는 순서를 묻습니다.
연습 문제
Age열의 이 25, 이 41이다. IQR 경계를 구하고, 값 88이clip을 거친 뒤 얼마가 되는지 쓰시오.이고 아래 경계는 , 위 경계는 입니다. 88은 위 경계를 넘으므로 65로 바뀝니다.다음 중
clip과 조건 필터로 행을 지우는 방법의 차이로 옳은 것은?
①clip은 행 수가 줄고 조건 필터는 그대로다
②clip은 행 수가 그대로고 조건 필터는 줄어든다
③ 둘 다 행 수가 줄어든다
④ 둘 다 행 수가 그대로다②.clip은 값만 경계값으로 바꾸므로 행이 그대로 남고, 조건 필터는 조건에 안 맞는 행을 빼므로 표가 줄어듭니다. 대신clip은 원래 크기의 정보를 잃습니다.Income열의 값이"1,200"·"3,500"처럼 쉼표가 섞인 문자열이다. 숫자로 바꾸는 코드를 순서대로 쓰시오.쉼표를 먼저 걷어내고 숫자로 바꿉니다.data['Income'] = data['Income'].str.replace(',', '') data['Income'] = pd.to_numeric(data['Income'], errors='coerce')순서를 바꾸면 쉼표 때문에 모든 값이
NaN이 됩니다.errors='coerce'는 못 바꾸는 값을NaN으로 만드므로, 뒤에isnull().sum()으로 몇 개가 생겼는지 확인합니다.고객 표에서 모델링에 쓰지 않을 열로 가장 알맞은 것은?
① 나이
② 고객 번호
③ 월 사용량
④ 가입 지역②. 고객 번호는 행마다 다른 식별자라 예측에 쓸 규칙이 없습니다. 나머지 셋은 값이 갈래나 크기를 가져 타깃과 관계를 맺을 수 있습니다.data = df뒤에data의 열을 지웠더니df에서도 그 열이 사라졌다. 원인과 해결 방법은?data = df는 같은 표에 이름을 하나 더 붙인 것이라 둘이 같은 표를 가리킵니다.data = df.copy()로 내용을 복사해 둬야 원본이 보존됩니다.고객 번호가 같은 접속 기록이 여러 개 쌓여 있다. 고객마다 시각이 가장 늦은 기록 하나만 남기시오.
시각 순으로 정렬한 뒤 고객 번호를 기준으로 마지막 것을 남깁니다.data = data.sort_values('AccessTime') data = data.drop_duplicates(subset=['CustomerID'], keep='last')정렬을 먼저 하지 않으면
keep='last'가 남기는 것은 시각이 가장 늦은 행이 아니라 표에 늦게 들어온 행입니다. 두 순서가 같다는 보장이 없습니다.해지 여부를 예측하는 모델의 정확도가 0.99로 나왔다. 피처 목록에
해지사유열이 있다면 무엇이 문제인가?해지 사유는 해지가 일어난 뒤에야 알 수 있는 값이라, 예측 시점에는 존재하지 않는 정보가 피처로 들어간 것입니다. 그 열 하나로 답이 정해지므로 점수만 높고 실제로는 쓸 수 없는 모델이 됩니다.drop으로 빼고 다시 학습합니다.
여기서 손이 굳어야 하는 것은 네 줄입니다 — clip(lower, upper), drop(columns=[...]), pd.to_numeric(..., errors='coerce'), drop_duplicates()입니다. 표를 깎는 일까지 됐으면 남은 것은 모델이 읽을 수 있는 모양으로 바꾸는 일이고, 다음은 글자로 된 갈래 열을 숫자로 옮기는 자리입니다.

