mmings_pring_day

[머신러닝] California Housing dataset (week03- pracitce) 본문

2024-2학기/머신러닝

[머신러닝] California Housing dataset (week03- pracitce)

mming_10 2024. 9. 19. 12:56

# 목적
-> 캘리포니아 인구조사 데이터를 사용해 캘리포니아 주택 가격 모델 만들기 
-> 학습시킨 모델에 다른 측정 데이터가 주어졌을 때 구역의 중간 주택 가격을 예측해야 함 -> 지도학습 중 '회귀'

 

1단계: 문제 정의

[회귀분석의 종류]- 단변량 회귀: 하나의 특성을 기반으로 예측- 다변량 회귀: 여러 개의 특성을 기반으로 예측 (이 데이터셋은 '구역의 인구, 중간 소득 등 feature가 많으므로 다변량 회귀)

 

2단계: 성능 측정 지표 선택

-> 회귀 분석의 대표적인 성능 측정 지표는 '평균 제곱근 오차 (RMSE; Root Mean Square Error)'

: 시스템이 하나의 샘플 데이터 x^(i) 를 받으면 예측 함수에 대입되어 예측 값이 출력됨. RMSE 가 회귀 문제에서 주로 선호되는 성능 측정 지표이지만 경우에 따라 이상치가 많은 구역이 있다면 평균 절대 오차 (Mean Absolute Error)를 고려할 수 있음


1. 데이터 불러오기

import numpy as np
import pandas as pd
df = pd.read_csv('data/housing/housing.csv')

- housing_median_age= 주택 지어진 기간 (중앙값)/ ocean_proximity (바다 근접도)

1) df.info( )

(1) 마지막 행인 'ocean_proximity' 만 obect 형이고, 나머지는 모두 float 형

(2) total_bedrooms 행이 결측치를 가지고 있음 

 

2) 각 변수들의 속성에 맞게 데이터 확인 (범주형 변수, 수치형 변수)

- 'ocean_proximity' 속성의 value 값 확인 

df['ocean_proximity'].value_counts()

- 수치형 변수들에 대해서 데이터 요약 정보 확인 = df.describe( )

df.describe()

 

2. 데이터 시각화 (히스토그램으로 데이터 형태 파악하기- 수치형 변수)

df.hist(bins=50, figsize=(20,15))

[이 시각화를 통해서 알 수 있는 정보]

- 중간 소득 (median_income)이 달러 단위가 아님

- 'median-income' 에 대해서, 실제 income 이 아님 (개인정보 문제 때문에) -> 인지해야 함!

데이터 취합시 normalize과정을 수행함 (최소값(0.5), 최대값(15)을 기준으로 실제 데이터를 변환))

 

- 중간 주택 연도 ('housing median age')와 중간 주택 가격 ('median house value')의 그래프의 오른쪽이  심하게 높아진 것으로 보아, 마지막 값으로 한정시킨 것 같음 (= 모델의 예측 값이 500000을 넘어가지 않도록 했음) 

 -> 중간 주택 가격 ('median house value')의 특성은 예측하는데 레이블로 사용되기 때문에 '데이터 처리'를 해야 함!!

만약 정확한 가격을 예측할 필요가 있다면, 다음 중 하나의 조치를 취해야 함
- 정확한 y값을 얻을 것
- 학습(train), 평가(test) 데이터셋에서 중간 주택 가격이 500000이상의 데이터는 제거할 것 (일반적)

 

- 대부분 변수가 'skew'되어 있음 (정규분포 X _머신러닝 알고리즘에서 패턴을 찾기 어려움-> 전처리 필요성)

3. 데이터 간 상관성 살펴보기

[지리적 데이터 시각화]

- x를 경도(longitude), y를 위도(latitude)로 해서 scatterplot 그리기

df.plot(kind='scatter', x='longitude', y='latitude', figsize=(7,7))

- alpha 옵션에 값을 0.1로 줘서 데이터 포인트가 밀집된 영역을 더 잘보이도록 하기

df.plot(kind='scatter', x='longitude', y='latitude', alpha=0.1, figsize=(7,7))

- 원의 크기로 인구 수를, 색상으로 주택 가격대를 나타내는 scatter plot 그리기

df.plot(kind='scatter', x='longitude', y='latitude', alpha=o,4,
		s= df['population']/100, label='population', figsize=(10,7),
        c= 'median_house_value', cmap=plt.get_cmap('jet'),colorbar=True,
        sharex= False)

(원의 반지름은 인구를 나타내고, 색깔은 가격을 나타냄/ 자세히 보면 바다 근처가 집 가격이 더 크다는 것을 알아낼 수 있음

* 물론 바다 근처인데도 불구하고 가격이 높지 않은 지역이 있어서 정보 사용이 간단하지는 않아 보임 *)

 

-> 주택 가격은 바다와의 인접성과 인구 수에 밀접한 관계가 있어 보임

 

3-2. 데이터 간 상관성 살펴보기 (변수 별 상관 관계도)

1) 상관관계 조사 (수치형 변수만을 가지고)

corr_matrix= df1.corr()
corr_matrix['median_house_value'].sort_values(ascending= False)

: 상관관계를 보면, 중간 주택 가격(median_house_value)은 중간 소득 (median_income)과 양의 상관관계를 가지며 중간 소득이 올라갈 때 증가함. 위도(latitude)와는 음의 상관관계로 위도가 올라갈 수록 가격이 서서히 내려간다고 볼 수 있음 (*0에 가까운 것은 관계가 없다는 뜻 *)

-> 상관계수는 선형적인 관계만 측정 가능함/ 비선형은 측정 불가

-> 상관계수는 기울기와 관계 없음

 

2) 중간 주택 가격과 중간 소득을 산점도 (scatter)로 나타내기

from pandas.plotting import scatter_matrix
attrs = ['median_house_value', 'median_income', 'total_rooms', 'housing_median_age']
scatter_matrix(df[attrs], figsize=(12,10))

 

['median house value'와 다른 변수 간의 상관성/ 관계성]

df.plot(kind='scatter', x='median_income', y='median_house_value', alpha= 0.1)

 

-  주택 가격 (median_house_value)는 중위 소득 (median_income)과 선형적인 관계가 존재!

(이 산점도를 통해 '갈수록 증가하는 상향관계'임을 알 수 있음_ 퍼져있지 않고 몰려있어서 아주 좋은 특성을 가지고 있음.

하지만 $500,000 에서 제한값으로 수평선으로 보이고, $350,000 과 $280,000, $230,000 등 수평선이 보임 -> 이러한 데이터는 좋지 않으므로 삭제 필요)

 

4. 데이터 전처리

[전처리 방법]

1) 비정상 데이터 제거

- 비정상적인 '중간 주택 가격 (median_house_value)' > = 500000, '중간 주택 연식 (housing_median_age)' >= 50 인 부분 제거

df= df[(df['housing_median_age'] < 50) &
	   (df['median_house_value'] < 500000)].reset_inces(drop=True)

2) train-test split

- 머신러닝 모델을 구축할 때, 학습(train)과 평가(test)를 위한 dataset 을 반드시 나눠야 함

df.info()

from sklearn.model_selection import train_test_split
#df_train) 학습 데이터
#df_test) 평가 데이터
df_train, df_test= train_test_split(df, test_size=0.2) #학습용 80%, 평가용 20%로 분리
#train_test_split(*arrays,test_size=None,train_size=None,random_state=None,)
print(df_train.shape, df_test.shape, df.shape)

- 본격적인 전처리 전에 특성에 따라 column 들을 다음과 같이 분리하겠음 (column 을 특성에 따라 구분하기)

all_cols= [x for x in df.columns] #모든 형태를 리스트화

geo_cols= ['longitude', 'latitude'] #지리적 특성 column
y_cols= ['median_house_value'] #종속 변수(y)_주택 가격 column
cat_cols= ['ocean_proximity'] #범주형 독립 변수(x) column
num_cols= [x for in all_cols if x not in geo_cols + y_cols + cat_cols] #수치형 독립 변수(x) column
print(all_cols)
print(geo_cols)
print(y_cols)
print(cat_cols)
print(num_cols)

 

[결측치 처리 방법 - total_bedrooms 변수에 대해서]

- Feature 삭제하기: drop( )

- 다른 값으로 변경하기 (0 or 평균 or 중간값): fillna( )

df_train.info()

df_test.info()

 

1) 결측치 제거하기

df_train_removed= df_train[df_train['total_bedrooms'].isnull() == False].reset_index(drop=True)
df_test_removed= df_test[df_test['total_bedrooms'].isnull() == False].reset_index(drop=True)

#결측치 제거 전 데이터 수
print(f"결측치 제거 전:train data 수={df_train.shape[0]}, test data 수={df_test.shape[0]}")
# 결측치 제거 후 데이터 수
print(f"결측치 제거 후: train data 수={df_train_removed.shape[0]}, test data 수={df_test_removed.shape[0]}")

2) 결측치 대체하기

- 데이터를 대체하기 위해선 중간값(median)으로 채워 넣기

  -> 범주형 데이터에 대해선 ('ocean_proximity') 중간값이라는 개념이 없음

  -> 수치형(numerical), 범주형 데이터로 나누고, 수치형 데이터만 채워넣도록 하겠음

all_cols = df_trn.columns

df_trn_num = df_trn[num_cols] # 학습 데이터 중, 수치형 변수만
df_trn_cat = df_trn[cat_cols] # 학습 데이터 중, 범주형 변수만
df_trn_geo = df_trn[geo_cols] # 학습 데이터 중, 지리적 특성 관련 변수만
df_trn_y = df_trn[y_cols] # 학습 데이터 중, 종속 변수만

df_tst_num = df_tst[num_cols] # 시험 데이터 중, 수치형 변수만
df_tst_cat = df_tst[cat_cols] # 시험 데이터 중, 범주형 변수만
df_tst_geo = df_tst[geo_cols] # 시험 데이터 중, 지리적 특성 관련 변수만
df_tst_y = df_tst[y_cols] # 시험 데이터 중, 종속 변수만

 

- train data 에서 각 column 별로 어떤 값으로 결측치를 채워 넣을지 결정

from sklearn.impute import SimpleImputer #SimpleImputer= 결측치 채워넣기 가능
imputer = SimpleImputer(strategy='median')
imputer.fit(df_train_num) #각각의 변수들의 결측치에 어떤 값으로 채워넣을 지 계산됨
print(imputer.statistics_) # 각 column마다 이 값으로 결측치가 채워짐. 3번째에 해당하는 값이 total_bedrooms column의 빈칸 채우는 값

(채워넣을 준비가 됨)

- 어떤 값으로 채워지지? 각 열마다 어떤 값으로 채워지는 지 계산 -> total_bedrooms 의 경우 채워지는 값은 '443'

 

- train data 를 impute 한 결과 산출

# df_trn_num의 결측치를 impute한 결과(단, numpy array 형태)
X_trn_num = imputer.transform(df_train_num)
X_trn_num #array가 반환됨

- 변환된 결과물이 pandas 형태가 되도록

imputer = SimpleImputer(strategy='median').set_output(transform='pandas')
imputer.fit(df_train_num)
print(imputer.statistics_) # 각 column마다 이 값으로 결측치가 채워짐. 5번째 값이 total_bedrooms column의 빈칸 채우는 값

X_trn_num = imputer.transform(df_train_num)
X_trn_num.head()

X_tst_num = imputer.transform(df_test_num)
X_tst_num.head()

* test data 에 채워넣는 값과 train data에서 채워넣는 값은 동일! / 하지만, test data에 값을 채워넣는 과정에 fit( )은 적용하면 안 됨!1 *

imputer = SimpleImputer(strategy='median')
imputer.fit(df_train_num) #각각의 변수들의 결측치에 어떤 값으로 채워넣을 지 계산됨 (X)
print(imputer.statistics_) # 각 column마다 이 값으로 결측치가 채워짐. 3번째에 해당하는 값이 total_bedrooms column의 빈칸 채우는 값 

 

3) 새로운 변수 생성하기

- 주택 가격은 전체 방 개수, 침실 개수보다 '침실 비율 = (침실 개수) / (전체 방 개수)' 에 더 많은 영향이 있음 

  -> 새로운 유도 변수를 생성 (* 새로운 변수를 유도할 때 사용한 변수는 제거해야 함 *)

X_trn_num.head()

- '결측치 처리'가 완료된 데이터를 활용하여 'bedrooms_per_room' 변수 생성

X_trn_num['bedrooms_per_room']= X_trn_num['total_bedrooms']/ X_trn_num['total_rooms']
X_tst_num['bedrooms_per_room']= X_tst_num['total_bedrooms']/ X_tst_num['total_rooms']

  -> 'bedrooms_per_room' 을 산출하는데 사용한 'total_bedrooms', 'total_rooms' 는 (기존 것) 제거!!

X_trn_num= X_trn_num.drop(['total_bedrooms', 'total_rooms'], axis=1)
X_tst_num= X_tst_num.drop(['total_bedrooms', 'total_rooms'], axis=1)
X_trn_num.head()

 

4) 수치형 변수 스케일링

- 표준화 혹은 정규화 사용하기 (정규분포로 만들기 위해서)

  -> MinMaxScaler = 최소, 최대 맞추기/ StandardScaler= z값으로 정규화 ? (둘 중 하나 사용)

  -> scikit-learn 에서는 train, test set 에서 같은 스케일로 데이터를 변환하도록 하는 기능을 제공함.

from sklearn.preprocessing import MinMaxScaler
from sklearn.preprocessing import StandardScaler

- 각 column 별로 standardize(normalize)하는 방식:  MinMaxScaler 사용하기 (변환!)

# scaler = StandardScaler().set_output(transform='pandas')
scaler = MinMaxScaler().set_output(transform='pandas')
scaler.fit(X_trn_num)

 - train data를 normalize (스케일링)한 결과 산출

X_trn_num_scaled = scaler.transform(X_trn_num)

스케일링 후 값이 달라짐!

* test 데이터에서는 별도로 fit( ) 적용 X, train 데이터에서 사용한 공식은 그대로 사용하기!! *

X_tst_num_scaled = scaler.transform(X_tst_num)

 

5) 범주형 변수 처리- OneHot Encoder 를 통해 벡터로 변환

-> 범주형 변수를 그대로 사용하지 못하는 경우, 'numerical vector'형태로 형태 변화 필요 (onehot encoding 방법_가장 기본적)

 

-> 차원을 늘림 (color_red/ color_blue/ color_green)

 

- train data에서 'ocean_proximity' column 을 onehot encoding 하는 과정 

from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder(sparse_output= False).set_output(transform='pandas') #불러오기
encoder.fit(df_trn_cat) #준비

   -> OneHotEncoder(sparse_output= False)

       : onehot encoding 결과물은 CSR 형식 -> 익숙한 numpy.ndarray 형식으로 값을 반환

   -> set_output(transform= 'pandas')

        : 위의 결과물 형식이 numpy array -> pandas DataFrame 형태의 output 출력!

 

- train data 에서 'ocean_proximity' column 의 범주 값을 onehot encoding!

X_trn_cat = encoder.transform(df_trn_cat) #실행
X_trn_cat

- test data 의 onehot encoding 방식과 train data 에서 onehot encoding 방식은 반드시 동일해야 함!

  : train data 를 onehot encoding 한 방식으로 test data 도 onehot encoding 하기

X_tst_cat = encoder.transform(df_tst_cat)
X_trn_cat.head()

 

 

6) 수치형 변수, 범주형 변수 전처리한 결과 통합하기 

- 수치형 변수 전처리 결과

# train data
X_trn_num_scaled.head()

# test data
X_tst_num_scaled.head()

- 범주형 변수 전처리 결과

# train data
X_trn_cat.head()

# test data
X_tst_cat.head()

- 수치형 변수, 범주형 변수를 전처리한 결과를 기존 데이터 (df_trn_geo, df_trn_y) 와 통합

X_trn = pd.concat((df_trn_geo, df_trn_y, X_trn_num_scaled, X_trn_cat), axis=1)
X_tst = pd.concat((df_tst_geo, df_tst_y, X_tst_num_scaled, X_tst_cat), axis=1)
X_trn.head()

 

X_tst.head()

 

-


 

자료출처:

https://www.kaggle.com/datasets/camnugent/california-housing-prices

 

California Housing Prices

Median house prices for California districts derived from the 1990 census.

www.kaggle.com

https://junsik-hwang.tistory.com/34

 

캘리포니아 주택 가격 예측 모델 만들기 - (1)

# 목적 캘리포니아 인구조사 데이터를 사용해 캘리포니아 주택 가격 모델 만들기. 학습시킨 모델에 다른 측정 데이터가 주어졌을 때 구역의 중간 주택 가격을 예측해야 함. # 파라미터 설명 total_

junsik-hwang.tistory.com

https://junsik-hwang.tistory.com/36#google_vignette

 

캘리포니아 주택 가격 예측 모델 만들기 - (2) feat.특성 스케일링

이제 데이터를 만져봐야한다. 첫 번째 : total_bedrooms 에만 특성이 없는 경우들이 있었다. (total_bedrooms 만 20433 이다) housing.info() ----------------------------------------- RangeIndex: 20640 entries, 0 to 20639 Data column

junsik-hwang.tistory.com

 

https://www.kaggle.com/code/alisultanov/regression-xgboost-optuna

 

Regression | XGBoost + OPTUNA

Explore and run machine learning code with Kaggle Notebooks | Using data from California Housing Prices

www.kaggle.com

 

https://psystat.tistory.com/136

 

python 원핫인코딩은 사이킷런의 OneHotEncoder를 사용하자

pandas.get_dummies 대신 sklearn.preprocessing.OneHotEncoder를 쓰자¶ 1. pandas.get_dummies의 문제점¶ pandas.get_dummies는 train 데이터의 특성을 학습하지 않기 때문에 train 데이터에만 있고 test 데이터에는 없는 카테

psystat.tistory.com

 

https://eda-ai-lab.tistory.com/655

 

[Machine Learning Advanced] 8강. 머신러닝 강의 - 캐글에서 활용되는 알아두면 좋은 팁 (Tips)

이번 글에서는 캐글에서 활용되는 알아두면 좋은 몇가지 팁들에 대해 알아보도록 하겠습니다. 먼저, 베이스라인을 만든 이후에 고려하면 좋을 사항들에 대해서 살펴본 이후 마지막 성능을 쥐어

eda-ai-lab.tistory.com

 

 

'2024-2학기 > 머신러닝' 카테고리의 다른 글

[머신러닝]  (1) 2024.11.10
[머신러닝] 타이타닉  (0) 2024.09.16