AICE (에이스)

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로 들어온 열을 숫자로 되돌리는 순서를 묻습니다.

연습 문제

  1. Age 열의 Q1Q_1이 25, Q3Q_3이 41이다. IQR 경계를 구하고, 값 88이 clip을 거친 뒤 얼마가 되는지 쓰시오.
    IQR=41−25=16IQR = 41 - 25 = 16이고 아래 경계는 25−1.5×16=125 - 1.5 \times 16 = 1, 위 경계는 41+1.5×16=6541 + 1.5 \times 16 = 65입니다. 88은 위 경계를 넘으므로 65로 바뀝니다.
  2. 다음 중 clip과 조건 필터로 행을 지우는 방법의 차이로 옳은 것은?
    ① clip은 행 수가 줄고 조건 필터는 그대로다
    ② clip은 행 수가 그대로고 조건 필터는 줄어든다
    ③ 둘 다 행 수가 줄어든다
    ④ 둘 다 행 수가 그대로다
    ②. clip은 값만 경계값으로 바꾸므로 행이 그대로 남고, 조건 필터는 조건에 안 맞는 행을 빼므로 표가 줄어듭니다. 대신 clip은 원래 크기의 정보를 잃습니다.
  3. 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()으로 몇 개가 생겼는지 확인합니다.

  4. 고객 표에서 모델링에 쓰지 않을 열로 가장 알맞은 것은?
    ① 나이
    ② 고객 번호
    ③ 월 사용량
    ④ 가입 지역
    ②. 고객 번호는 행마다 다른 식별자라 예측에 쓸 규칙이 없습니다. 나머지 셋은 값이 갈래나 크기를 가져 타깃과 관계를 맺을 수 있습니다.
  5. data = df 뒤에 data의 열을 지웠더니 df에서도 그 열이 사라졌다. 원인과 해결 방법은?
    data = df는 같은 표에 이름을 하나 더 붙인 것이라 둘이 같은 표를 가리킵니다. data = df.copy()로 내용을 복사해 둬야 원본이 보존됩니다.
  6. 고객 번호가 같은 접속 기록이 여러 개 쌓여 있다. 고객마다 시각이 가장 늦은 기록 하나만 남기시오.
    시각 순으로 정렬한 뒤 고객 번호를 기준으로 마지막 것을 남깁니다.
    data = data.sort_values('AccessTime')
    data = data.drop_duplicates(subset=['CustomerID'], keep='last')

    정렬을 먼저 하지 않으면 keep='last'가 남기는 것은 시각이 가장 늦은 행이 아니라 표에 늦게 들어온 행입니다. 두 순서가 같다는 보장이 없습니다.

  7. 해지 여부를 예측하는 모델의 정확도가 0.99로 나왔다. 피처 목록에 해지사유 열이 있다면 무엇이 문제인가?
    해지 사유는 해지가 일어난 뒤에야 알 수 있는 값이라, 예측 시점에는 존재하지 않는 정보가 피처로 들어간 것입니다. 그 열 하나로 답이 정해지므로 점수만 높고 실제로는 쓸 수 없는 모델이 됩니다. drop으로 빼고 다시 학습합니다.

여기서 손이 굳어야 하는 것은 네 줄입니다 — clip(lower, upper), drop(columns=[...]), pd.to_numeric(..., errors='coerce'), drop_duplicates()입니다. 표를 깎는 일까지 됐으면 남은 것은 모델이 읽을 수 있는 모양으로 바꾸는 일이고, 다음은 글자로 된 갈래 열을 숫자로 옮기는 자리입니다.

AICE (에이스) 시험 노트 전체 보기