안녕하세요
장철원님이 저자이신 <선형대수와 통계학으로 배우는 머신러닝 with 파이썬>책을
실습과 이론 부분으로 나누어서 매일 따라하고 있는데요
실습 부분인 챕터 6을 따라하던 중 6-2-2부분인 클래스 라벨링 부분이 다르게 되어 있더라구요.
그래서 이 부분을 헤매시는 분들께 도움을 드리고자 포스팅을 하게 되었습니다.
모든 내용은 앞서 언급한 책을 보며 따라한 코드이며, 오직 학습 목적으로 포스팅함을 알려드립니다.
일단 데이터는 이렇게 생겼습니다.
# 데이터 생성
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 |
이후 데이터 이상치/ 결측치를 확인하고 이상치를 Nan으로 대체 후, 결측치를 제거하는 여러 방법이 소개됩니다.
# 데이터 이상치/결측치 확인
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 |
df이름.isnull().sum()
# 결측치 개수 확인
df.isnull().sum()
age 2
gender 2
month_birth 1
hobby 1
target 1
dtype: int64
데이터프레임이름.dropna(파라미터들)
아래에는 dropna에서 변경할 수 있는 옵션인, 파라미터들에 대한 설명과 예시입니다.
df이름.dropna(axis = 0)
df2 = df.dropna(axis=0)
df2
age | gender | month_birth | hobby | target | |
---|---|---|---|---|---|
0 | 42.0 | male | 12.0 | reading | class2 |
df이름.dropna(axis = 1)
df3 = df.dropna(axis=1)
df3
0 |
---|
1 |
2 |
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 |
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 |
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 |
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 |
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 |
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 |
이 다음 단계에서 저자는 다음과 같이 코딩하셨어요
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로 만들기 위해 정렬하시려던 것 같았습니다.
그래서 저는 이렇게 코딩했습니다.
데이터를 변경시키는 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 |
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 |
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 |
이렇게 이상치/결측치 확인과 처리, 그리고 라벨링 및 정렬이 끝났습니다.
감사합니다!