[책] 선형대수와 통계학으로 배우는 머신러닝 with 파이썬 - 챕터 6.2

ossap·2021년 12월 28일
0

📚 책

목록 보기
1/1

안녕하세요

장철원님이 저자이신 <선형대수와 통계학으로 배우는 머신러닝 with 파이썬>책을
실습과 이론 부분으로 나누어서 매일 따라하고 있는데요

실습 부분인 챕터 6을 따라하던 중 6-2-2부분인 클래스 라벨링 부분이 다르게 되어 있더라구요.
그래서 이 부분을 헤매시는 분들께 도움을 드리고자 포스팅을 하게 되었습니다.

모든 내용은 앞서 언급한 책을 보며 따라한 코드이며, 오직 학습 목적으로 포스팅함을 알려드립니다.

1. 데이터 확인

일단 데이터는 이렇게 생겼습니다.

# 데이터 생성
df = pd.DataFrame([
    [42, 'male', 12, 'reading', 'class2'],
    [35, 'unknown', 3, 'cooking', 'class1'],
    [1000, 'female', 7, 'cycling', 'class3'],
    [1000, 'unknown', 21, 'unknown', 'unknown']
])
df.columns = ['age', 'gender', 'month_birth', 'hobby', 'target']
# 데이터 확인
df
age gender month_birth hobby target
0 42 male 12 reading class2
1 35 unknown 3 cooking class1
2 1000 female 7 cycling class3
3 1000 unknown 21 unknown unknown

2. 이상치/결측치 확인 및 처리

이후 데이터 이상치/ 결측치를 확인하고 이상치를 Nan으로 대체 후, 결측치를 제거하는 여러 방법이 소개됩니다.

1) 이상치/결측치 확인

# 데이터 이상치/결측치 확인
df['age'].unique()
#    array([  42,   35, 1000]
df['gender'].unique()
#    array(['male', 'unknown', 'female'], dtype=object)
df['hobby'].unique()
#    array(['reading', 'cooking', 'cycling', 'unknown'], dtype=object)
df['target'].unique()
#    array(['class2', 'class1', 'class3', 'unknown'], dtype=object)

# 이상치 nan으로 대체
df.loc[df['age']>150, ['age']] = np.nan
df.loc[df['gender']=='unknown', ['gender']] = np.nan
df.loc[df['month_birth']>12, ['month_birth']] = np.nan
df.loc[df['hobby']=='unknown', ['hobby']] = np.nan
df.loc[df['target']=='unknown', ['target']] = np.nan

df
age gender month_birth hobby target
0 42.0 male 12.0 reading class2
1 35.0 NaN 3.0 cooking class1
2 NaN female 7.0 cycling class3
3 NaN NaN NaN NaN NaN


2) 결측치 개수 확인

  • df이름.isnull().sum()
# 결측치 개수 확인
df.isnull().sum()
age            2
gender         2
month_birth    1
hobby          1
target         1
dtype: int64


3) 결측치 제거

  • 데이터프레임이름.dropna(파라미터들)

아래에는 dropna에서 변경할 수 있는 옵션인, 파라미터들에 대한 설명과 예시입니다.

(1) 결측치 포함한 행 삭제

  • df이름.dropna(axis = 0)
df2 = df.dropna(axis=0)
df2
age gender month_birth hobby target
0 42.0 male 12.0 reading class2

(2) 결측치 포함한 열 삭제

  • df이름.dropna(axis = 1)
df3 = df.dropna(axis=1)
df3
0
1
2
3

(3) 모든 값이 결측치인 행만 삭제

  • df이름.dropna(how = 'all')
df4 = df.dropna(how='all')
df4
age gender month_birth hobby target
0 42.0 male 12.0 reading class2
1 35.0 NaN 3.0 cooking class1
2 NaN female 7.0 cycling class3

(4) 결측치를 제외한 값이 n개보다 작을 경우, 해당 행 삭제

  • df이름.dropna(thresh = n)
df5 = df.dropna(thresh=2)
df5
age gender month_birth hobby target
0 42.0 male 12.0 reading class2
1 35.0 NaN 3.0 cooking class1
2 NaN female 7.0 cycling class3

(5) 특정 열에 결측치가 있는 경우 행 삭제

  • df이름.dropna(subset = ['열이름'])
df6 = df.dropna(subset=['gender'])
df6
age gender month_birth hobby target
0 42.0 male 12.0 reading class2
2 NaN female 7.0 cycling class3


4) 결측치 대체

  • df이름.fillna(values = 변경할 values가 담긴 dict)
# 결측치 대체하기
alter_values = {'age' :0,
                'gender' : 'U',
                'month_birth' : 0,
                'hobby' : 'U',
                'target': 'class4'}
df7 = df.fillna(value = alter_values)
df7
age gender month_birth hobby target
0 42.0 male 12.0 reading class2
1 35.0 U 3.0 cooking class1
2 0.0 female 7.0 cycling class3
3 0.0 U 0.0 U class4

3. 클래스 라벨링

1) 사이킷런을 활용한 클래스 라벨링

(1) 데이터 라벨링하기

  • LabelEncoder().fit_transform(라벨링할 데이터)
from sklearn.preprocessing import LabelEncoder
df8 = df7
class_label = LabelEncoder()
data_value = df8['target'].values
y_new = class_label.fit_transform(data_value)
y_new
#    array([1, 0, 2, 3])


df8['target'] = y_new
df8
age gender month_birth hobby target
0 42.0 male 12.0 reading 1
1 35.0 U 3.0 cooking 0
2 0.0 female 7.0 cycling 2
3 0.0 U 0.0 U 3

(2) 라벨링한 데이터를 되돌리고 싶을 때

  • LabelEncoder().inverse_transfrom(라벨링 완료한 데이터)
y_ori = class_label.inverse_transform(y_new)
y_ori
#    array(['class2', 'class1', 'class3', 'class4'], dtype=object)

df8['target'] = y_ori
df8
age gender month_birth hobby target
0 42.0 male 12.0 reading class2
1 35.0 U 3.0 cooking class1
2 0.0 female 7.0 cycling class3
3 0.0 U 0.0 U class4

2) 사이킷런을 활용하지 않은 라벨링

👉 책에서 조금 다른 부분이 여기부터! 👈

(1) sort()

이 다음 단계에서 저자는 다음과 같이 코딩하셨어요

y_arr = df8['target'].values
y_arr.sort()

'target'열과 y_arr를 연결시키는데,
이후 sort()를 해서 y_arr이 정렬이 되어버립니다.(데이터자체가 변경됨)
아래처럼요.

age gender month_birth hobby target
0 42.0 male 12.0 reading class1
1 35.0 U 3.0 cooking class2
2 0.0 female 7.0 cycling class3
3 0.0 U 0.0 U class4

다른 열의 데이터는 같이 정렬되지 않았는데,
target 내의 데이터만 정렬되는 것을 볼 수 있습니다.

그래서 저자의 의도를 살펴보니,
이 뒤에서 for문으로 0,1,2,3과
class1, class2, class3, class4를 연결시키거든요.

그래서 단순히 y_arr을 dict로 만들기 위해 정렬하시려던 것 같았습니다.

(1-2) sorted()

그래서 저는 이렇게 코딩했습니다.

데이터를 변경시키는 sort()가 아닌,
데이터를 변경시키지 않고 정렬만 하는 sorted()로 바꾼 후
새로운 변수(new_y_arr)에 저장했습니다. dict를 만들기 위해서요

나중에 생각해보니
어차피 dict는 순서가 없으므로, sort()만 지워주면 해결되는 문제였으나,
그 덕에 뒤의 sort_values와 reset_index 함수를 배웠으므로
지우지 않겠습니다.....하하하^^;

y_arr = df8['target'].values
new_y_arr = sorted(y_arr)
df8
age gender month_birth hobby target
0 42.0 male 12.0 reading class2
1 35.0 U 3.0 cooking class1
2 0.0 female 7.0 cycling class3
3 0.0 U 0.0 U class4

이렇게 연결된 y_arr을 sorted하면 데이터는 변하지 않음을 확인할 수 있습니다.
이후 앞서 말한것 처럼 라벨 매칭을 위해 dic_y를 만들어줍니다.

num_y = 0
dic_y = {}
for ith_y in new_y_arr:
    dic_y[ith_y] = num_y
    num_y += 1

dic_y
#    {'class1': 0, 'class2': 1, 'class3': 2, 'class4': 3}

df8
age gender month_birth hobby target
0 42.0 male 12.0 reading class2
1 35.0 U 3.0 cooking class1
2 0.0 female 7.0 cycling class3
3 0.0 U 0.0 U class4


그리고 앞서 만든 dic_y에 맞춰 replace해, 라벨링을 해줍니다.
{'class1': 0, 'class2': 1, 'class3': 2, 'class4': 3}

df8['target'] = df8['target'].replace(dic_y)
df8
age gender month_birth hobby target
0 42.0 male 12.0 reading 1
1 35.0 U 3.0 cooking 0
2 0.0 female 7.0 cycling 2
3 0.0 U 0.0 U 3

(2) 특정 컬럼 기준 정렬

  • df이름.sort_values(by=정렬할 컬럼명)

이후 sort_values라는 함수를 사용해 'target'을 기준으로 정렬해줍니다.

sort_values 역시 기존 데이터가 바뀌지 않고 보여주기만 하므로,
df8.sort_values(by='target') 만 하면 실제 df8은 바뀌지 않습니다.

df8 = df8.sort_values(by='target')
이처럼 df8에 담아서 변경까지 해주어야 합니다.

# target열을 기준으로 정렬.
df8 = df8.sort_values(by='target')
df8
age gender month_birth hobby target
1 35.0 U 3.0 cooking 0
0 42.0 male 12.0 reading 1
2 0.0 female 7.0 cycling 2
3 0.0 U 0.0 U 3

(3) 인덱스 재정렬

  • df이름.reset_index()

위의 인덱싱이 target과 맞추어 1 0 2 3 이렇게 뒤죽박죽이 되어있으므로,
인덱스를 재정렬해주겠습니다.

이 때, drop = True를 하면 기존 인덱스(1,0,2,3)은 사라지고
False를 하면 기존인덱스는 남고 새로운 인덱스 열이 추가됩니다.

# 인덱싱 재정렬
df8 = df8.reset_index(drop=True)
df8
age gender month_birth hobby target
0 35.0 U 3.0 cooking 0
1 42.0 male 12.0 reading 1
2 0.0 female 7.0 cycling 2
3 0.0 U 0.0 U 3

이렇게 이상치/결측치 확인과 처리, 그리고 라벨링 및 정렬이 끝났습니다.
감사합니다!

profile
오삽 : 오늘도 삽질

0개의 댓글