## x,y값샘플제작

손든횟수=[1,2,3,1,1,3,1,3]
성적=[90,100,80,100,88,77,55,85]

import pandas as pd
import numpy as np

df=pd.DataFrame(dict({'손든횟수':손든횟수, '성적':성적}))
df
손든횟수 성적
0 1 90
1 2 100
2 3 80
3 1 100
4 1 88
5 3 77
6 1 55
7 3 85

Part1. [단순선형회귀-최소제곱법계산식]

image.png

x=df['손든횟수'].values
y=df['성적'].values
x,y
(array([1, 2, 3, 1, 1, 3, 1, 3], dtype=int64),
 array([ 90, 100,  80, 100,  88,  77,  55,  85], dtype=int64))
xData=np.reshape(x,(-1,1))
xData
array([[1],
       [2],
       [3],
       [1],
       [1],
       [3],
       [1],
       [3]], dtype=int64)
from sklearn.linear_model import LinearRegression
import numpy as np

model=LinearRegression()
model.fit(xData,y)
model.coef_, model.intercept_
(array([-0.96363636]), 86.18181818181819)

– [작업1] model.coef_와 model.intercept_값을 아래 수식으로 직접 계산해봅니다. –

image.png

# 공식을 이용하여 구한 회귀 | 위의 값과 비교해보면 같다.
coef=np.sum((x-np.mean(x))*(y-np.mean(y))) / (np.sum((x-np.mean(x))**2))
coef
-0.9636363636363636
np.mean(y)-(np.mean(x)*coef)
86.18181818181819

상수화 추가

$ y = w_1x_1 + b $ 는

내적곱 $ y = w_1x_1 + w_0x_0 $ 와 같다고 볼 수있다. 이때 $w_0$는 1, $x_0$는 b이다.

이렇게 연산하는 이유는 b, w를 한 개의 행렬로 계산할 수 있게 되기 때문이다.

– [작업2] model.coef_와 model.intercept_값을 아래 수식으로 직접 계산해봅니다. –

image.png

newX=np.c_[np.ones(len(x)),x] # 상수화 추가 | 1로된 배열 생성 | c_는 concat함수로 좌우로 연결함 
newX
array([[1., 1.],
       [1., 2.],
       [1., 3.],
       [1., 1.],
       [1., 1.],
       [1., 3.],
       [1., 1.],
       [1., 3.]])
# newX의전치행렬*newX
계산1=np.dot(newX.T,newX) # T는 배열의 전치를 수행하는 함수 | dot은 행열 내적곱을 하는 함수
계산1
array([[ 8., 15.],
       [15., 35.]])
#계산1의 역행렬
# np.random.seed(0)
계산1역행렬= np.linalg.inv(계산1)  # 역행열
계산1역행렬
array([[ 0.63636364, -0.27272727],
       [-0.27272727,  0.14545455]])

역행렬

원래 행렬 A와 곱셈을 통해 항등행렬(대각선이 1인 행렬)을 얻을 수 있는 행렬

전치

행렬 A의 행과 열을 열과 행으로 바꾼 행렬

# 최종결과
계산2=np.dot(계산1역행렬,newX.T)
계산3=np.dot(계산2,y)
계산3   ##첫번째값은 b, 두번째는 x1, 세번째는 x2
array([86.18181818, -0.96363636])

Part2. [다중선형회귀_최소제곱법계산식]

  • 위의 작업2방법으로 계산합니다.
df['공지확인횟수']=[1,2,20,7,8,1,2,3]
df
손든횟수 성적 공지확인횟수
0 1 90 1
1 2 100 2
2 3 80 20
3 1 100 7
4 1 88 8
5 3 77 1
6 1 55 2
7 3 85 3
x=df[['손든횟수','공지확인횟수']].values
y=df['성적'].values

display(x)
array([[ 1,  1],
       [ 2,  2],
       [ 3, 20],
       [ 1,  7],
       [ 1,  8],
       [ 3,  1],
       [ 1,  2],
       [ 3,  3]], dtype=int64)
# 사이킷런 모델에서 계산된값
from sklearn.linear_model import LinearRegression
import numpy as np

model=LinearRegression()
model.fit(x,y)
model.coef_, model.intercept_
(array([-1.17727639,  0.12771958]), 85.87993553585818)
# 직접 계산한 최소제곱법
newX=np.c_[np.ones(len(x)),x]
계산1=np.dot(newX.T,newX)
np.random.seed(0)
계산1역행렬= np.linalg.inv(계산1) 
계산2=np.dot(계산1역행렬,newX.T)
계산3=np.dot(계산2,y)
계산3   ##첫번째값은 b, 두번째는 x1, 세번째는 x2

# 이값으로 해석하면 손든횟수가 많을수록 성적은 -1배 감소한다. | x1의 인터셉트(weight)가 -1이기 때문에
# 이값으로 해석하면 공지확인횟수가 많을수혹 성적은 0.1배 증가한다. | x2의 인터셉트(weight)가 0.1
array([85.87993554, -1.17727639,  0.12771958])
### 위의 작업을 함수로 작업해보기

x=df[['손든횟수','공지확인횟수']].values
y=df['성적'].values

def linear(x,y):
    
    newX=np.c_[np.ones(len(x)),x] # 상수항 추가로 인해 reshape를 하지 않아도 된다.
    계산1=np.dot(newX.T,newX)
    np.random.seed(0)
    계산1역행렬= np.linalg.inv(계산1) 
    계산2=np.dot(계산1역행렬,newX.T)
    계산3=np.dot(계산2,y)

    return 계산3

linear(x,y)
    
array([85.87993554, -1.17727639,  0.12771958])

GridSearch

그리드서치는 머신러닝 모델에서 최적의 하이퍼파라미터를 찾기 위해 사용되는 탐색 방법 중 하나입니다.

그리드 서치는 가능한 모든 하이퍼파라미터 조합을 다중 for문을 시도하여 최적의 조합을 찾는 방법입니다.

예를 들어, 하이퍼파라미터 A에 [0.1,0.2,0.3]의 값들과 하이퍼파라미터 B에 [1,2,3]의 값을 지정한 경우,

그리드 서치는 총 9개의 조합을 만들어 각각에 대해 모델을 학습하고 평가해 최적의 조합을 찾아냅니다.

CV(Cross-Validation)

CV는 일반적으로 모델의 성능 평가와 하이퍼파라미터 튜닝에 널리 사용되는 검증 기법입니다.

CV는 다음과 같은 단계로 진행됩니다:

  1. 데이터 분할: 주어진 데이터를 학습 데이터와 테스트 데이터로 나눕니다. 전통적인 CV 방법은 데이터를 K개의 서로 다른 부분 집합으로 나누는데, 이를 K-Fold Cross-Validation이라고 합니다. 일반적으로 K 값은 5 또는 10이 많이 사용되지만, 다른 값도 선택할 수 있습니다.

  2. 모델 학습 및 평가: K 개의 부분 집합 중 하나를 선택하여 학습 데이터로 사용하고, 나머지 K-1 개의 부분 집합을 테스트 데이터로 사용하여 모델을 학습하고 평가합니다. 이 과정을 K 번 반복하여 K 개의 모델을 학습하고 평가합니다.

  3. 성능 평가: K 번의 평가 결과를 종합하여 모델의 평균 성능을 계산합니다. 일반적으로 평가 지표로는 정확도(accuracy), 정밀도(precision), 재현율(recall), F1 스코어(F1 score) 등이 사용됩니다.

하이퍼파라미터(alpha)

alpha는 릿지 회귀(Ridge Regression) 또는 라쏘 회귀(Lasso Regression)와 같은 정규화(regularization) 기법에서 사용되는 하이퍼파라미터입니다. 정규화는 모델의 복잡도를 제어하고 과적합을 방지하기 위해 사용되는 기법으로, alpha는 이 정규화 강도를 조절하는 매개변수입니다.

alpha 값은 일반적으로 0에서 큰 양의 값 사이에 있으며, 일반적으로 로그 스케일로 설정됩니다. alpha 값이 0에 가까울수록 정규화의 효과가 약화되며, 0보다 큰 값일수록 정규화의 강도가 강해집니다. 따라서, 더 큰 alpha 값은 더 강한 제약을 모델에 부과하고, 가중치를 작게 만듭니다.

alpha 값을 조정함으로써 모델의 복잡도와 일반화 간의 트레이드오프를 조절할 수 있습니다. 작은 alpha 값을 사용하면 모델은 더 복잡해지고 학습 데이터에 더 잘 적합할 수 있지만, 테스트 데이터에 대한 일반화 성능이 저하될 수 있습니다. 큰 alpha 값을 사용하면 모델은 더 단순화되고 일반화 성능이 향상될 수 있지만, 학습 데이터에 대한 적합도가 줄어들 수 있습니다.

alpha 값의 최적값을 찾기 위해서는 일반적으로 그리드 서치(Grid Search)나 다른 하이퍼파라미터 튜닝 방법을 사용하여 여러 가지 alpha 값에 대해 모델을 평가하고 비교해야 합니다. 최적의 alpha 값은 주어진 데이터셋과 모델에 따라 달라질 수 있으므로, 실험과 검증을 통해 선택해야 합니다.

# 그리드서치 실습

import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import Lasso
from sklearn.model_selection import GridSearchCV
import pandas as pd

##########데이터 로드

x_data = np.array([
[2,1],
[3,2],
[3,4],
[5,5],
[7,5],
[2,5],
[8,9],
[9,2],
[6,10],
[7,12]
])
y_data = np.array([3,5,7,10,12,7,13,13,12,12])

##########데이터 분석

##########데이터 전처리

x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size=0.3)

##########모델 훈련

model = Lasso()
param_grid = {'alpha':[0.01,0.1,1,10,100]} # alpha= 하이퍼파라미터 | 그리드서치를 통해 여러 가지 alpha값에 대해 모델을 평가하고 비교해야 함
grid = GridSearchCV(model, param_grid=param_grid) 

grid.fit(x_train, y_train)
print(f'그리드서치가 찾아낸 최적의 하이퍼파라미터 조합: {grid.best_params_}')
df = pd.DataFrame(grid.cv_results_) # 그리드 서치를 수행한 결과 | 각 하이퍼파라미터 조합의 정보를 포함함.
print(df.sort_values(by=['param_alpha'])[['params','mean_test_score']])

##########모델 검증

print(grid.score(x_test, y_test)) #

##########모델 사용

print(grid.predict([[2,1]])[0]) # 최적의 모델을 사용하여 데이터 예측을 수행
그리드서치가 찾아낸 최적의 하이퍼파라미터 조합: {'alpha': 0.01}
            params  mean_test_score
0  {'alpha': 0.01}              NaN
1   {'alpha': 0.1}              NaN
2     {'alpha': 1}              NaN
3    {'alpha': 10}              NaN
4   {'alpha': 100}              NaN
0.8779231555293396
4.3847133947153925
c:\devtools\Miniconda3\envs\meta\Lib\site-packages\sklearn\metrics\_regression.py:918: UndefinedMetricWarning: R^2 score is not well-defined with less than two samples.
  warnings.warn(msg, UndefinedMetricWarning)
c:\devtools\Miniconda3\envs\meta\Lib\site-packages\sklearn\metrics\_regression.py:918: UndefinedMetricWarning: R^2 score is not well-defined with less than two samples.
  warnings.warn(msg, UndefinedMetricWarning)
c:\devtools\Miniconda3\envs\meta\Lib\site-packages\sklearn\metrics\_regression.py:918: UndefinedMetricWarning: R^2 score is not well-defined with less than two samples.
  warnings.warn(msg, UndefinedMetricWarning)
c:\devtools\Miniconda3\envs\meta\Lib\site-packages\sklearn\metrics\_regression.py:918: UndefinedMetricWarning: R^2 score is not well-defined with less than two samples.
  warnings.warn(msg, UndefinedMetricWarning)
c:\devtools\Miniconda3\envs\meta\Lib\site-packages\sklearn\metrics\_regression.py:918: UndefinedMetricWarning: R^2 score is not well-defined with less than two samples.
  warnings.warn(msg, UndefinedMetricWarning)
c:\devtools\Miniconda3\envs\meta\Lib\site-packages\sklearn\metrics\_regression.py:918: UndefinedMetricWarning: R^2 score is not well-defined with less than two samples.
  warnings.warn(msg, UndefinedMetricWarning)
c:\devtools\Miniconda3\envs\meta\Lib\site-packages\sklearn\metrics\_regression.py:918: UndefinedMetricWarning: R^2 score is not well-defined with less than two samples.
  warnings.warn(msg, UndefinedMetricWarning)
c:\devtools\Miniconda3\envs\meta\Lib\site-packages\sklearn\metrics\_regression.py:918: UndefinedMetricWarning: R^2 score is not well-defined with less than two samples.
  warnings.warn(msg, UndefinedMetricWarning)
c:\devtools\Miniconda3\envs\meta\Lib\site-packages\sklearn\metrics\_regression.py:918: UndefinedMetricWarning: R^2 score is not well-defined with less than two samples.
  warnings.warn(msg, UndefinedMetricWarning)
c:\devtools\Miniconda3\envs\meta\Lib\site-packages\sklearn\metrics\_regression.py:918: UndefinedMetricWarning: R^2 score is not well-defined with less than two samples.
  warnings.warn(msg, UndefinedMetricWarning)
c:\devtools\Miniconda3\envs\meta\Lib\site-packages\sklearn\metrics\_regression.py:918: UndefinedMetricWarning: R^2 score is not well-defined with less than two samples.
  warnings.warn(msg, UndefinedMetricWarning)
c:\devtools\Miniconda3\envs\meta\Lib\site-packages\sklearn\metrics\_regression.py:918: UndefinedMetricWarning: R^2 score is not well-defined with less than two samples.
  warnings.warn(msg, UndefinedMetricWarning)
c:\devtools\Miniconda3\envs\meta\Lib\site-packages\sklearn\metrics\_regression.py:918: UndefinedMetricWarning: R^2 score is not well-defined with less than two samples.
  warnings.warn(msg, UndefinedMetricWarning)
c:\devtools\Miniconda3\envs\meta\Lib\site-packages\sklearn\metrics\_regression.py:918: UndefinedMetricWarning: R^2 score is not well-defined with less than two samples.
  warnings.warn(msg, UndefinedMetricWarning)
c:\devtools\Miniconda3\envs\meta\Lib\site-packages\sklearn\metrics\_regression.py:918: UndefinedMetricWarning: R^2 score is not well-defined with less than two samples.
  warnings.warn(msg, UndefinedMetricWarning)
c:\devtools\Miniconda3\envs\meta\Lib\site-packages\sklearn\model_selection\_search.py:952: UserWarning: One or more of the test scores are non-finite: [nan nan nan nan nan]
  warnings.warn(

피클

객체를 저장하고 읽어오는 모듈로 학습한 모델을 저장하고 불러올 수 있다.

import pandas as pd
from sklearn.linear_model import LinearRegression
import pickle
import numpy as np
import os

##########데이터 로드

train_df = pd.read_excel('https://github.com/cranberryai/todak_todak_python/blob/master/machine_learning/regression/%E1%84%8B%E1%85%A1%E1%84%87%E1%85%A5%E1%84%8C%E1%85%B5%E1%84%8B%E1%85%A1%E1%84%83%E1%85%B3%E1%86%AF%E1%84%8F%E1%85%B5.xlsx?raw=true', sheet_name='train')
test_df = pd.read_excel('https://github.com/cranberryai/todak_todak_python/blob/master/machine_learning/regression/%E1%84%8B%E1%85%A1%E1%84%87%E1%85%A5%E1%84%8C%E1%85%B5%E1%84%8B%E1%85%A1%E1%84%83%E1%85%B3%E1%86%AF%E1%84%8F%E1%85%B5.xlsx?raw=true', sheet_name='test')

##########데이터 분석

##########데이터 전처리

x_train = train_df.drop(['Son'], axis=1)
x_test = test_df.drop(['Son'], axis=1)
y_train = train_df['Son']
y_test = test_df['Son']

print(x_train.head())
'''
    Father
0  160.782
1  166.116
2  165.608
3  169.672
4  176.530
'''

x_train = x_train.to_numpy()
x_test = x_test.to_numpy()

##########모델 생성

model = LinearRegression()

##########모델 학습

model.fit(x_train, y_train)

##########모델 검증

print(model.score(x_train, y_train)) #

print(model.score(x_test, y_test)) #0.251997790584662

if not os.path.exists('models/son_height_regression_model'):
    os.makedirs('models/son_height_regression_model')

with open('models/son_height_regression_model/model.pkl', 'wb') as f:
    pickle.dump(model, f)

##########모델 예측

x_test = np.array([
    [164.338]
])

y_predict = model.predict(x_test)

print(y_predict[0]) #169.66660924268297
    Father
0  165.100
1  165.100
2  167.132
3  155.194
4  160.020
0.24967004992776776
0.2519977905846619
170.46931035654347

Tree 구조

node로 표현하는 tree 구조는 빠르다는 장점이 있지만 많은 용량을 할당해야하는 단점이 있습니다.

image.png

  • 불순도(impurity) : 해당 범주안에 서로 다른 데이터가 얼마나 섞여 있는지를 뜻 한 종류의 데이터가 쏠려 있을 수록 순도( 불순도)가 높다.
  • 엔트로피(entropy) : 엔프로피가 1이면 불순도 최대(한 범주안에 데이터가 반반씩 있음) 0이면 불순도 최소(한 범주안에 같은 종류의 데이터만 있음)
  • 정보획득(information gain) : gain 지수라고도 함. 노드 분기 이전의 엔트로피에서 분기 이후의 엔트로피를 뺀 수치 이 지수가 크면 클수록 좋은 분기.

가지치기

트리 구조의 오버피팅을 막기위한 전략. 트리에 노드가 너무 많다면 오버피팅으로 볼 수 있습니다.

최대 깊이나 터미널 노드의 최대 개수, 혹은 한 노드가 분할하기 위한 최소 데이터수를 제한할 수 있습니다.


실습 - 자동으로 모은 중고 자동차 데이터 분석

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

df = pd.read_csv('csv\\vehicles.csv')

EDA 및 데이터 기초 통계 분석

불필요한 데이터 제거하기

df.head(3)
Unnamed: 0 id url region region_url price year manufacturer model condition ... drive size type paint_color image_url description state lat long posting_date
0 0 7240372487 https://auburn.craigslist.org/ctd/d/auburn-uni... auburn https://auburn.craigslist.org 35990 2010.0 chevrolet corvette grand sport good ... rwd NaN other NaN https://images.craigslist.org/00N0N_ipkbHVZYf4... Carvana is the safer way to buy a car During t... al 32.590000 -85.480000 2020-12-02T08:11:30-0600
1 1 7240309422 https://auburn.craigslist.org/cto/d/auburn-201... auburn https://auburn.craigslist.org 7500 2014.0 hyundai sonata excellent ... fwd NaN sedan NaN https://images.craigslist.org/00s0s_gBHYmJ5o7y... I'll move to another city and try to sell my c... al 32.547500 -85.468200 2020-12-02T02:11:50-0600
2 2 7240224296 https://auburn.craigslist.org/cto/d/auburn-200... auburn https://auburn.craigslist.org 4900 2006.0 bmw x3 3.0i good ... NaN NaN SUV blue https://images.craigslist.org/00B0B_5zgEGWPOrt... Clean 2006 BMW X3 3.0I. Beautiful and rare Bl... al 32.616807 -85.464149 2020-12-01T19:50:41-0600

3 rows × 26 columns

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 458213 entries, 0 to 458212
Data columns (total 26 columns):
 #   Column        Non-Null Count   Dtype  
---  ------        --------------   -----  
 0   Unnamed: 0    458213 non-null  int64  
 1   id            458213 non-null  int64  
 2   url           458213 non-null  object 
 3   region        458213 non-null  object 
 4   region_url    458213 non-null  object 
 5   price         458213 non-null  int64  
 6   year          457163 non-null  float64
 7   manufacturer  439993 non-null  object 
 8   model         453367 non-null  object 
 9   condition     265273 non-null  object 
 10  cylinders     287073 non-null  object 
 11  fuel          454976 non-null  object 
 12  odometer      402910 non-null  float64
 13  title_status  455636 non-null  object 
 14  transmission  455771 non-null  object 
 15  VIN           270664 non-null  object 
 16  drive         324025 non-null  object 
 17  size          136865 non-null  object 
 18  type          345475 non-null  object 
 19  paint_color   317370 non-null  object 
 20  image_url     458185 non-null  object 
 21  description   458143 non-null  object 
 22  state         458213 non-null  object 
 23  lat           450765 non-null  float64
 24  long          450765 non-null  float64
 25  posting_date  458185 non-null  object 
dtypes: float64(4), int64(3), object(19)
memory usage: 90.9+ MB
df.isna().sum() # 결측치 확인
Unnamed: 0           0
id                   0
url                  0
region               0
region_url           0
price                0
year              1050
manufacturer     18220
model             4846
condition       192940
cylinders       171140
fuel              3237
odometer         55303
title_status      2577
transmission      2442
VIN             187549
drive           134188
size            321348
type            112738
paint_color     140843
image_url           28
description         70
state                0
lat               7448
long              7448
posting_date        28
dtype: int64
df.describe()
Unnamed: 0 id price year odometer lat long
count 458213.000000 4.582130e+05 4.582130e+05 457163.000000 4.029100e+05 450765.000000 450765.000000
mean 229106.000000 7.235233e+09 4.042093e+04 2010.746067 1.016698e+05 38.531925 -94.375824
std 132274.843786 4.594362e+06 8.194599e+06 8.868136 3.228623e+06 5.857378 18.076225
min 0.000000 7.208550e+09 0.000000e+00 1900.000000 0.000000e+00 -82.607549 -164.091797
25% 114553.000000 7.231953e+09 4.900000e+03 2008.000000 4.087700e+04 34.600000 -110.890427
50% 229106.000000 7.236409e+09 1.099500e+04 2013.000000 8.764100e+04 39.244500 -88.314889
75% 343659.000000 7.239321e+09 2.149500e+04 2016.000000 1.340000e+05 42.484503 -81.015022
max 458212.000000 7.241019e+09 3.615215e+09 2021.000000 2.043756e+09 82.049255 150.898969
df.columns
Index(['Unnamed: 0', 'id', 'url', 'region', 'region_url', 'price', 'year',
       'manufacturer', 'model', 'condition', 'cylinders', 'fuel', 'odometer',
       'title_status', 'transmission', 'VIN', 'drive', 'size', 'type',
       'paint_color', 'image_url', 'description', 'state', 'lat', 'long',
       'posting_date'],
      dtype='object')

데이터 소개

- 이번 주제는 Used Cars Dataset을 사용합니다.

- 파일은 한 개이며, 각각의 컬럼은 아래와 같습니다.



- vehicles.csv

id : 중고차 거래의 아이디

url : 중고차 거래 페이지

region : 해당 거래의 관리 지점

region_url : 거래 관리 지점의 홈페이지

price : 기입된 자동차의 거래가

year : 거래가 기입된 년도

manufacturer : 자동차를 생산한 회사

model : 자동차 모델명

condition : 자동차의 상태

cylinders : 자동차의 기통 수

fuel : 자동차의 연료 타입

odometer : 자동차의 운행 마일 수

title_status : 자동차의 타이틀 상태 (소유주 등록 상태)

transmission : 자동차의 트랜스미션 종류

vin : 자동차의 식별 번호 (vehicle identification number)

drive : 자동차의 구동 타입

size : 자동차 크기

type : 자동차의 일반 타입 (세단, ...)

paint_color : 자동차 색상

image_url : 자동차 이미지

description : 세부 설명

county : 실수로 생성된 미사용 컬럼

state : 거래가 업로드된 미 주

lat : 거래가 업로드된 곳의 위도

long : 거래가 업로드된 곳의 경도
df.drop(['Unnamed: 0', 'id', 'url', 'region_url', 'VIN',
         'image_url', 'description', 'state', 'lat', 
         'long', 'posting_date'], axis=1, inplace=True)
df['age'] = 2023 - df['year'] # age 컬럼을 새로 생성
df.drop('year', axis=1, inplace=True)

# drop은 특정열을 제거하는 함수 | 분석할 필요없는 컬럼을 제거함
# axis는 제거할 축을 지정하는 옵션 (0은 행 제거 | 1은 열 제거)
# inplace는 원본 데이터 프레임을 변경할지 여부. False라면 새로운 데이터프레임을 리턴함.
df.head(3)
region price manufacturer model condition cylinders fuel odometer title_status transmission drive size type paint_color age
0 auburn 35990 chevrolet corvette grand sport good 8 cylinders gas 32742.0 clean other rwd NaN other NaN 13.0
1 auburn 7500 hyundai sonata excellent 4 cylinders gas 93600.0 clean automatic fwd NaN sedan NaN 9.0
2 auburn 4900 bmw x3 3.0i good 6 cylinders gas 87046.0 clean automatic NaN NaN SUV blue 17.0
df.columns
Index(['region', 'price', 'manufacturer', 'model', 'condition', 'cylinders',
       'fuel', 'odometer', 'title_status', 'transmission', 'drive', 'size',
       'type', 'paint_color', 'age'],
      dtype='object')

범주형(문자) 데이터 값의 범위, 기초 통계 분석하기

len(df['manufacturer'].value_counts()) # 제조사의 개수
43
df['manufacturer'].value_counts() # 제조사별 데이터 개수
# 왼쪽컬럼이 index가 된다
ford               79666
chevrolet          64977
toyota             38577
honda              25868
nissan             23654
jeep               21165
ram                17697
gmc                17267
dodge              16730
bmw                12352
hyundai            10975
mercedes-benz      10628
subaru             10510
volkswagen         10489
kia                 8854
chrysler            7499
lexus               7119
cadillac            6743
buick               6009
mazda               5931
audi                5583
acura               4008
infiniti            3714
lincoln             3338
pontiac             3037
volvo               2866
mini                2330
mitsubishi          2301
porsche             1779
rover               1662
mercury             1645
saturn              1393
tesla               1067
jaguar              1060
fiat                 955
alfa-romeo           187
harley-davidson      139
ferrari               96
datsun                63
aston-martin          35
land rover            21
morgan                 3
hennessey              1
Name: manufacturer, dtype: int64
fig = plt.figure(figsize=(8,10)) # 가로8인치, 세로10인치
sns.countplot(
    y='manufacturer',
    data=df.fillna('n.a'), # 결측치는 n/a로 표현하겠음. fillna함수가 새로운 데이터프레임을 반환하지 않기 떄문에 재사용 하지 못한다.
    order=df.fillna('n/a')['manufacturer'].value_counts().index # 제조사 개수가 높은 순으로 정렬
    )
<Axes: xlabel='count', ylabel='manufacturer'>

countplot은 주어진 데이터에서 각 카테고리별로 빈도를 시각화하는 데 사용되는 seaborn 라이브러리의 함수입니다. 주로 범주형 변수의 분포를 살펴보고자 할 때 유용하게 사용됩니다.

sns.countplot(
    y='condition',
    data=df.fillna('n/a'),
    order=df.fillna('n/a')['condition'].value_counts().index
    )
<Axes: xlabel='count', ylabel='condition'>

수치형 데이터의 통계 분석

rugplot은 단일 변수의 분포를 확인하고자 할 때 주로 사용됩니다.

fig = plt.figure(figsize=(8,2))
sns.rugplot(x='price', data=df, height=1) # label을 price로만 준것 뿐 아니라 자동으로 price컬럼을 적용함
<Axes: xlabel='price'>

sns.histplot(x='age', data=df, bins=18, kde=True)
<Axes: xlabel='age', ylabel='Count'>

데이터 클리닝 수행

범주형 데이터 클리닝

아래의 방법 중 적절히 판단하여 처리

  1. 결손 데이터가 포함된 row를 제거

  2. 혹은 결손 데이터를 others 범주로 변경

  3. 지나치게 소수인 범주를 others 범주로 변경

  4. classifier를 학습해서 결손 데이터를 추정하여 채워넣기

col = 'paint_color'
counts = df[col].fillna('others').value_counts()
# 결손데이터를 others로 처리
plt.grid()
plt.plot(range(len(counts)), counts)
# x데이터 = 각 범주의 인덱스
# y데이터 = 각 범주의 빈도수
[<matplotlib.lines.Line2D at 0x1a89c95ddd0>]

n_categorical = 7
colors = counts.index[n_categorical:]
# 7번 인덱스 이후로는 데이터 수가 적어 의미 없다고 판단
# 살릴 색상 범주를 colors 변수에 저장
df[col] = df[col].apply(lambda s: s if str(s) not in colors else 'others')
# colors안에 있는 이름의 값이 아니라면 모두 others로 변경하고 적용

df[col].value_counts()
white     82786
black     64145
silver    46722
red       33274
blue      32746
grey      30455
others    27242
Name: paint_color, dtype: int64

수치형 데이터 클리닝

quantile()를 이용하여 outlier를 제거

quantile은 주어진 데이터의 분위수를 계산하는 함수로 분위수 위치에 있는 값을 반환합니다.

분위수란 데이터를 크기별로 정렬했을 때, 특정 백분율에 해당하는 위치에 있는 값을 의미

옵션

  • series: 분위수를 계산하고자 하는 시리즈(데이터 열)입니다.

  • q: 원하는 분위수를 나타내는 값을 입력합니다. 이 값은 0과 1 사이의 실수로 지정하며, 예를 들어 0.25는 25% 분위수를 의미합니다.

  • interpolation: 분위수를 계산하는 동안 사용할 보간법(interpolation) 방법을 지정합니다. 기본값은 ‘linear’로, 선형 보간법을 사용합니다. 다른 가능한 옵션으로 ‘lower’, ‘higher’, ‘midpoint’, ‘nearest’ 등이 있습니다.

p1 = df['price'].quantile(0.99) # 99퍼 분위수에 해당하는 값
p2 = df['price'].quantile(0.1) # 10퍼 분위수에 해당하는 값
print(p1, p2)
59900.0 651.0
# 99퍼와 10퍼 분위수에 해당하는 값의 차이가 많이나므로 그 사이에 있는 값들을 추출
df = df[(p1 > df['price']) & (df['price'] > p2)]
# 해당 조건을 만족하는 기존 df의 price행만 바뀜
# Boxplot 계열로 범주형 데이터를 시각화하여 분석하기
fig = plt.figure(figsize=(10, 5))
sns.boxplot(x='paint_color', y='price', data=df)
<Axes: xlabel='paint_color', ylabel='price'>

o1 = df['odometer'].quantile(0.99)
o2 = df['odometer'].quantile(0.1)
df = df[(o1 > df['odometer']) & (df['odometer'] > o2)]

# 컬럼간의 상관계수 확인하기
sns.heatmap(df.corr(), annot=True, cmap='YlOrRd')
C:\Users\user\AppData\Local\Temp\ipykernel_17708\269042974.py:6: FutureWarning: The default value of numeric_only in DataFrame.corr is deprecated. In a future version, it will default to False. Select only valid columns or specify the value of numeric_only to silence this warning.
  sns.heatmap(df.corr(), annot=True, cmap='YlOrRd')
<Axes: >

모델 학습을 위한 데이터 전처리

standardScaler를 이용해 수치형 데이터 표준화

from sklearn.preprocessing import StandardScaler
x_num = df[['odometer', 'age']]

sclaer = StandardScaler()
sclaer.fit(x_num) # 피팅
x_scaled = sclaer.transform(x_num)
x_scaled = pd.DataFrame(x_scaled, index=x_num.index, columns=x_num.columns)

# get_dummies를 이용해 범주형 데이터를 one-hot 벡터로 변경하기
x_cat = df.drop(['price', 'odometer', 'age'], axis=1) # 숫자형 데이터를 제거
x_cat = pd.get_dummies(x_cat)

# 입출력 데이터 통합하기
x = pd.concat([x_scaled, x_cat], axis=1)
y = df['price']

x.shape
(322166, 23756)

train, test데이터 분리

from sklearn.model_selection import train_test_split

# train_test_split() 함수로 학습 데이터와 테스트 데이터 분리하기
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=1)

Regression 모델 학습

XBGoost Regression 모델 학습하기

# 여기서부터 데이터 클리닝을 못하여 뻗었다.

댓글남기기