import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
import statsmodels.api as sm
import random
import time
import os
import datetime
import xgboost
from xgboost import XGBRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import explained_variance_score
from sklearn.model_selection import GridSearchCV, RandomizedSearchCV
from statsmodels.stats.outliers_influence import variance_inflation_factor
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
import warnings
warnings.filterwarnings('ignore')
plt.rcParams['font.family'] = 'NanumGothic'
plt.rcParams['font.size'] = 13
def seed_everything(seed):
random.seed(seed)
os.environ['PYTHONHASHSEED'] = str(seed)
np.random.seed(seed)
seed_everything(42) # Seed 고정
train = pd.read_csv('data/train.csv')
test = pd.read_csv('data/test.csv')
train.head(5)
ID | 월 | 일 | 측정 시간대 | 섭씨 온도(°C) | 절대 온도(K) | 이슬점 온도(°C) | 상대 습도 (%) | 대기압(mbar) | 포화 증기압(mbar) | 실제 증기압(mbar) | 증기압 부족량(mbar) | 수증기 함량 (g/kg) | 공기 밀도 (g/m**3) | 풍향 (deg) | 풍속 (m/s) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | TRAIN_00000 | 7 | 2 | 저녁 | 13.97 | 287.78 | 9.84 | 76.1 | 992.08 | 15.98 | 12.16 | 3.82 | 7.66 | 1198.06 | 155.60 | 1.61 |
1 | TRAIN_00001 | 8 | 21 | 오전 | 16.94 | 290.85 | 12.14 | 73.3 | 991.07 | 19.33 | 14.17 | 5.16 | 8.94 | 1183.67 | 177.00 | 1.68 |
2 | TRAIN_00002 | 11 | 1 | 저녁 | 9.76 | 283.84 | 5.40 | 74.2 | 988.71 | 12.10 | 8.98 | 3.12 | 5.67 | 1213.22 | 146.20 | 0.73 |
3 | TRAIN_00003 | 12 | 28 | 오전 | 5.27 | 277.30 | 2.71 | 83.5 | 1014.25 | 8.89 | 7.43 | 1.47 | 4.57 | 1265.48 | 264.50 | 2.71 |
4 | TRAIN_00004 | 9 | 26 | 오후 | 17.35 | 290.86 | 12.68 | 74.0 | 995.77 | 19.84 | 14.68 | 5.16 | 9.22 | 1187.40 | 19.34 | 1.00 |
test.head(5)
ID | 월 | 일 | 측정 시간대 | 섭씨 온도(°C) | 절대 온도(K) | 이슬점 온도(°C) | 상대 습도 (%) | 대기압(mbar) | 포화 증기압(mbar) | 실제 증기압(mbar) | 증기압 부족량(mbar) | 수증기 함량 (g/kg) | 공기 밀도 (g/m**3) | 풍향 (deg) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | TEST_00000 | 3 | 24 | 새벽 | 4.28 | 278.68 | -0.21 | 72.5 | 984.48 | 8.30 | 6.02 | 2.28 | 3.81 | 1233.29 | 251.80 |
1 | TEST_00001 | 9 | 24 | 저녁 | 13.40 | 286.81 | 10.36 | 81.8 | 996.98 | 15.40 | 12.59 | 2.80 | 7.89 | 1206.20 | 225.60 |
2 | TEST_00002 | 5 | 28 | 저녁 | 19.89 | 294.33 | 14.95 | 73.2 | 984.83 | 23.26 | 17.03 | 6.23 | 10.82 | 1163.06 | 10.39 |
3 | TEST_00003 | 1 | 17 | 저녁 | -2.88 | 270.44 | -4.47 | 88.7 | 998.02 | 4.94 | 4.38 | 0.56 | 2.73 | 1284.19 | 260.20 |
4 | TEST_00004 | 10 | 22 | 오후 | 6.97 | 281.18 | 4.36 | 83.4 | 987.00 | 10.01 | 8.35 | 1.66 | 5.28 | 1223.47 | 262.50 |
Dataset Info.
57920개의 데이터
ID : 샘플 별 고유 id
월: 데이터가 기록된 달을 나타냅니다.
일: 데이터가 기록된 날짜를 나타냅니다.
측정 시간대: 데이터가 측정된 시간대를 나타냅니다. 오전, 오후, 저녁, 새벽으로 구분되어 있습니다.
섭씨 온도 (° C)
절대 온도 (K)
이슬점 온도 (° C)
상대 습도 (%)
대기압 (mbar)
포화 증기압 (mbar)
실제 증기압 (mbar)
증기압 부족량 (mbar)
수증기 함량 (g/kg): 공기 1 kg당 수증기의 질량을 그램(g) 단위로 나타냅니다.
공기 밀도 (g/m**3): 1 m³의 부피에 들어있는 공기의 질량을 그램(g) 단위로 나타냅니다.
풍향 (deg): 바람의 향하는 방향을 각도(degree)로 나타냅니다.
풍속 (m/s)
train 데이터프레임은 총 26129개의 행(RangeIndex)과 16개의 열(Data columns)로 구성되어 있습니다.
수치형 데이터가 12개(float64)와 2개(int64)의 컬럼으로 구성되어 있으며, object 타입의 컬럼도 2개 있습니다. 따라서 'ID'와 '측정 시간대' 컬럼이 문자열 데이터라는 것을 알 수 있습니다.
train.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 36581 entries, 0 to 36580 Data columns (total 16 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 ID 36581 non-null object 1 월 36581 non-null int64 2 일 36581 non-null int64 3 측정 시간대 36581 non-null object 4 섭씨 온도(°C) 36581 non-null float64 5 절대 온도(K) 36581 non-null float64 6 이슬점 온도(°C) 36581 non-null float64 7 상대 습도 (%) 36581 non-null float64 8 대기압(mbar) 36581 non-null float64 9 포화 증기압(mbar) 36581 non-null float64 10 실제 증기압(mbar) 36581 non-null float64 11 증기압 부족량(mbar) 36581 non-null float64 12 수증기 함량 (g/kg) 36581 non-null float64 13 공기 밀도 (g/m**3) 36581 non-null float64 14 풍향 (deg) 36581 non-null float64 15 풍속 (m/s) 36581 non-null float64 dtypes: float64(12), int64(2), object(2) memory usage: 4.5+ MB
train.describe()
월 | 일 | 섭씨 온도(°C) | 절대 온도(K) | 이슬점 온도(°C) | 상대 습도 (%) | 대기압(mbar) | 포화 증기압(mbar) | 실제 증기압(mbar) | 증기압 부족량(mbar) | 수증기 함량 (g/kg) | 공기 밀도 (g/m**3) | 풍향 (deg) | 풍속 (m/s) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 | 36581.00000 | 36581.000000 |
mean | 6.511468 | 15.648561 | 9.992694 | 283.971095 | 5.453064 | 75.827418 | 990.032953 | 14.037167 | 9.786895 | 4.250224 | 6.178010 | 1214.580682 | 176.58240 | 2.036446 |
std | 3.458678 | 8.803467 | 8.237177 | 8.354235 | 6.327009 | 16.089188 | 8.824477 | 8.075680 | 4.185701 | 5.191128 | 2.655054 | 39.528520 | 84.38269 | 1.544146 |
min | 1.000000 | 1.000000 | -13.930000 | 258.870000 | -15.560000 | 23.640000 | 956.890000 | 2.070000 | 1.810000 | 0.000000 | 1.120000 | 1110.650000 | 0.01000 | 0.100000 |
25% | 4.000000 | 8.000000 | 3.650000 | 277.700000 | 0.450000 | 65.360000 | 985.100000 | 7.940000 | 6.310000 | 0.950000 | 3.980000 | 1184.030000 | 129.40000 | 0.920000 |
50% | 7.000000 | 16.000000 | 9.130000 | 283.170000 | 5.070000 | 79.000000 | 990.250000 | 11.590000 | 8.770000 | 2.250000 | 5.520000 | 1214.650000 | 199.50000 | 1.630000 |
75% | 9.000000 | 23.000000 | 16.350000 | 290.370000 | 10.950000 | 89.200000 | 995.700000 | 18.620000 | 13.100000 | 5.410000 | 8.270000 | 1241.600000 | 233.90000 | 2.700000 |
max | 12.000000 | 31.000000 | 34.340000 | 308.620000 | 20.150000 | 100.000000 | 1015.300000 | 54.270000 | 23.640000 | 41.230000 | 14.980000 | 1349.460000 | 360.00000 | 11.890000 |
대기압 (mbar): 기압은 대기의 무게로 인해 발생하므로, 대기압은 기압에 직접적인 영향을 미칩니다.
이슬점 온도 (° C): 이슬점 온도는 공기가 포화 상태로 수증기를 포함하는데 필요한 온도를 의미하며, 공기가 포화되면 대기압에 영향을 줄 수 있습니다.
상대 습도 (%): 상대 습도는 공기가 포화 상태에 얼마나 가까운지를 나타내는데, 수증기가 포화되면 기압에 영향을 미칠 수 있습니다.
수증기 함량 (g/kg): 공기 내의 수증기의 양은 대기압과 관련하여 영향을 미칠 수 있습니다.
섭씨 온도 (° C) 또는 절대 온도 (K): 온도의 변화는 대기의 열적 팽창과 함께 기압에 영향을 미칠 수 있습니다.
증기압 부족량 (mbar): 공기 중의 수증기가 포화 상태에 미치지 못하는 압력 차이로, 기압에 영향을 줄 수 있습니다.
공기 밀도 (g/m**3): 공기의 밀도는 공기의 질량과 부피에 영향을 미치며, 이는 일부 경우에 기압에 영향을 미칠 수 있습니다.
측정 시간대: 측정 시간대는 기압에 직접적인 영향은 없지만, 기상 조건과 관련하여 다른 변수들과 함께 영향을 미칠 수 있습니다.
풍향 (deg)은 바람의 방향을 나타내기 때문에 기압에 직접적인 영향을 미치지 않지만, 바람은 기압의 차이로 인해 발생하고 이동하므로 간접적으로 기압과 연관이 있습니다. 따라서 바람은 기압 변화와 관련하여 중요한 요소입니다. 하지만 바람의 방향 자체가 기압에 직접적인 영향을 주지는 않습니다.
섭씨 온도 (° C) 또는 절대 온도 (K): 풍속은 주로 기압 차이에 의해 발생하고, 온도는 풍속에 직접적인 영향을 주지 않습니다. 따라서 풍속을 결정하는데 온도는 크게 영향을 미치지 않습니다.
증기압 부족량 (mbar): 증기압 부족량은 공기 중의 수증기가 포화 상태에 미치지 못하는 압력 차이를 나타냅니다. 풍속을 결정하는데 이 변수는 직접적인 영향을 주지 않습니다.
공기 밀도 (g/m**3): 풍속은 대기의 움직임과 기압 차이에 의해 발생하고, 공기의 밀도는 풍속에 큰 영향을 미치지 않습니다.
측정 시간대: 풍속은 대기의 움직임과 기압 차이에 따라 형성되므로, 측정 시간대는 풍속에 직접적인 영향을 주지 않습니다.
위 변수들은 풍속에 직접적인 영향을 미치지 않는다는 것을 기억하는 것이 중요합니다. 풍속은 주로 기압 차이에 의해 발생하며, 바람은 기압이 낮은 곳에서 기압이 높은 곳으로 흐르는 경향을 가지기 때문에 기압과 관련된 변수들이 풍속에 큰 영향을 미칩니다. 따라서 풍속을 예측하거나 이해하기 위해서는 주로 기압과 관련된 변수들이 중요하며, 기온, 증기압, 공기 밀도, 측정 시간대는 풍속과의 직접적인 관련성이 낮습니다.
모델을 학습하기 위해서는 독립변수(X)와 종속변수(y)로 나누어야 합니다.
독립변수는 Feature, 종속변수는 Target이라고 불리기도 합니다.
Feature : 예측을 위해 활용되는 데이터
Target : 예측하고자 하는 대상
(이 때, 분석에 활용하지 않는 데이터인 'id'는 제거하겠습니다.)
# train_x는 독립변수이므로 종속변수(풍속 (m/s))를 제거합니다.
# 또한 target 이외의 분석에 활용하지 않는 데이터(id)를 제거합니다.
train_x = train.drop(columns=['ID', '풍속 (m/s)'], axis = 1)
# train_y는 종속변수로 값을 설정합니다.
train_y = train['풍속 (m/s)']
# train에서와 마찬가지로 분석에 활용하지 않는 데이터(id)를 제거합니다.
test_x = test.drop(columns=['ID'])
from sklearn.preprocessing import LabelEncoder
le = LabelEncoder()
le = le.fit(train_x['측정 시간대'])
train_x['측정 시간대'] = le.transform(train_x['측정 시간대'])
test_x['측정 시간대'] = le.transform(test_x['측정 시간대'])
df = train_x.copy()
## 전진 단계별 선택법
variables = df.columns.tolist() ## 설명 변수 리스트
y = train_y ## 반응 변수
selected_variables = [] ## 선택된 변수들
sl_enter = 0.05
sl_remove = 0.05
sv_per_step = [] ## 각 스텝별로 선택된 변수들
adjusted_r_squared = [] ## 각 스텝별 수정된 결정계수
steps = [] ## 스텝
step = 0
while len(variables) > 0:
remainder = list(set(variables) - set(selected_variables))
pval = pd.Series(index=remainder) ## 변수의 p-value
## 기존에 포함된 변수와 새로운 변수 하나씩 돌아가면서
## 선형 모형을 적합한다.
for col in remainder:
X = df[selected_variables+[col]]
X = sm.add_constant(X)
model = sm.OLS(y,X).fit()
pval[col] = model.pvalues[col]
min_pval = pval.min()
if min_pval < sl_enter: ## 최소 p-value 값이 기준 값보다 작으면 포함
selected_variables.append(pval.idxmin())
## 선택된 변수들에대해서
## 어떤 변수를 제거할지 고른다.
while len(selected_variables) > 0:
selected_X = df[selected_variables]
selected_X = sm.add_constant(selected_X)
selected_pval = sm.OLS(y,selected_X).fit().pvalues[1:] ## 절편항의 p-value는 뺀다
max_pval = selected_pval.max()
if max_pval >= sl_remove: ## 최대 p-value값이 기준값보다 크거나 같으면 제외
remove_variable = selected_pval.idxmax()
selected_variables.remove(remove_variable)
else:
break
step += 1
steps.append(step)
adj_r_squared = sm.OLS(y,sm.add_constant(df[selected_variables])).fit().rsquared_adj
adjusted_r_squared.append(adj_r_squared)
sv_per_step.append(selected_variables.copy())
else:
break
# 원래 컬럼
variables
['월', '일', '측정 시간대', '섭씨 온도(°\u2063C)', '절대 온도(K)', '이슬점 온도(°C)', '상대 습도 (%)', '대기압(mbar)', '포화 증기압(mbar)', '실제 증기압(mbar)', '증기압 부족량(mbar)', '수증기 함량 (g/kg)', '공기 밀도 (g/m**3)', '풍향 (deg)']
# 단계별 선택법으로 선택된 컬럼
selected_variables
['증기압 부족량(mbar)', '수증기 함량 (g/kg)', '상대 습도 (%)', '풍향 (deg)', '실제 증기압(mbar)', '일', '이슬점 온도(°C)', '월', '섭씨 온도(°\u2063C)', '대기압(mbar)', '절대 온도(K)', '공기 밀도 (g/m**3)']
# 선택되지 않은 변수
list(set(variables)-set(selected_variables))
['포화 증기압(mbar)', '측정 시간대']
fig = plt.figure(figsize=(10,10))
fig.set_facecolor('white')
font_size = 15
plt.xticks(steps,[f'step {s}\n'+'\n'.join(sv_per_step[i]) for i,s in enumerate(steps)], fontsize=12)
plt.plot(steps,adjusted_r_squared, marker='o')
plt.ylabel('Adjusted R Squared',fontsize=font_size)
plt.grid(True)
plt.show()
중요도순
풍향, 대기압, 일, 섭씨 온도, 이슬점 온도, 상대 습도, 월, 공기 밀도, 증기압 부족량, 측정 시간대, 절대 온도, 수증기 함량, 실제 증기압, 포화 증기압
# train_x = train_x.drop(['실제 증기압(mbar)', '포화 증기압(mbar)', '이슬점 온도(°C)', '절대 온도(K)'], axis=1)
train_x = train_x.drop(['증기압 부족량(mbar)', '측정 시간대'], axis=1)
# test_x = test_x.drop(['실제 증기압(mbar)', '포화 증기압(mbar)', '이슬점 온도(°C)', '절대 온도(K)'], axis=1)
test_x = test_x.drop(['증기압 부족량(mbar)', '측정 시간대'], axis=1)
len(train_x.columns), len(test_x.columns)
(12, 12)
train_x.isnull().sum()
월 0 일 0 섭씨 온도(°C) 0 절대 온도(K) 0 이슬점 온도(°C) 0 상대 습도 (%) 0 대기압(mbar) 0 포화 증기압(mbar) 0 실제 증기압(mbar) 0 수증기 함량 (g/kg) 0 공기 밀도 (g/m**3) 0 풍향 (deg) 0 dtype: int64
train_x
월 | 일 | 섭씨 온도(°C) | 절대 온도(K) | 이슬점 온도(°C) | 상대 습도 (%) | 대기압(mbar) | 포화 증기압(mbar) | 실제 증기압(mbar) | 수증기 함량 (g/kg) | 공기 밀도 (g/m**3) | 풍향 (deg) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 7 | 2 | 13.97 | 287.78 | 9.84 | 76.10 | 992.08 | 15.98 | 12.16 | 7.66 | 1198.06 | 155.60 |
1 | 8 | 21 | 16.94 | 290.85 | 12.14 | 73.30 | 991.07 | 19.33 | 14.17 | 8.94 | 1183.67 | 177.00 |
2 | 11 | 1 | 9.76 | 283.84 | 5.40 | 74.20 | 988.71 | 12.10 | 8.98 | 5.67 | 1213.22 | 146.20 |
3 | 12 | 28 | 5.27 | 277.30 | 2.71 | 83.50 | 1014.25 | 8.89 | 7.43 | 4.57 | 1265.48 | 264.50 |
4 | 9 | 26 | 17.35 | 290.86 | 12.68 | 74.00 | 995.77 | 19.84 | 14.68 | 9.22 | 1187.40 | 19.34 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
36576 | 8 | 10 | 14.10 | 287.79 | 4.99 | 54.15 | 993.53 | 16.11 | 8.73 | 5.48 | 1200.85 | 292.90 |
36577 | 2 | 20 | 3.80 | 277.91 | -1.01 | 70.70 | 988.03 | 8.02 | 5.67 | 3.58 | 1240.06 | 210.50 |
36578 | 5 | 29 | 20.06 | 295.10 | 16.70 | 81.00 | 977.90 | 23.51 | 19.04 | 12.20 | 1153.24 | 19.22 |
36579 | 9 | 10 | 25.65 | 299.81 | 15.30 | 52.81 | 988.39 | 32.98 | 17.41 | 11.03 | 1144.61 | 225.40 |
36580 | 3 | 11 | 3.14 | 276.30 | 1.88 | 91.40 | 1000.01 | 7.66 | 7.00 | 4.37 | 1257.47 | 30.67 |
36581 rows × 12 columns
모두 측정값의 숫자 분포라 특별히 이상치라고 할 만한 게 없다. 말이 안되게 튀는 숫자는 없는 것 같다.
풍속이 월에 따라 바뀔 수는 있어도 일에 따라 바뀌긴 어려울 것 같다. 계절을 주기로 돌아갈 순 있어도 한 달 안에서 날짜 주기로 돌진 않을테니까.
** 일 컬럼 drop 시 성능이 낮아지는 것을 확인함
** 계절로 1, 2, 3, 4 인코딩 시 성능이 낮아지는 것을 확인함
풍향은 degree, 각도이기 때문에 값의 크고 작음이 의미가 없다. 사인 함수를 활용해서 변환해본다.
train_x
월 | 일 | 섭씨 온도(°C) | 절대 온도(K) | 이슬점 온도(°C) | 상대 습도 (%) | 대기압(mbar) | 포화 증기압(mbar) | 실제 증기압(mbar) | 수증기 함량 (g/kg) | 공기 밀도 (g/m**3) | 풍향 (deg) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 7 | 2 | 13.97 | 287.78 | 9.84 | 76.10 | 992.08 | 15.98 | 12.16 | 7.66 | 1198.06 | 155.60 |
1 | 8 | 21 | 16.94 | 290.85 | 12.14 | 73.30 | 991.07 | 19.33 | 14.17 | 8.94 | 1183.67 | 177.00 |
2 | 11 | 1 | 9.76 | 283.84 | 5.40 | 74.20 | 988.71 | 12.10 | 8.98 | 5.67 | 1213.22 | 146.20 |
3 | 12 | 28 | 5.27 | 277.30 | 2.71 | 83.50 | 1014.25 | 8.89 | 7.43 | 4.57 | 1265.48 | 264.50 |
4 | 9 | 26 | 17.35 | 290.86 | 12.68 | 74.00 | 995.77 | 19.84 | 14.68 | 9.22 | 1187.40 | 19.34 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
36576 | 8 | 10 | 14.10 | 287.79 | 4.99 | 54.15 | 993.53 | 16.11 | 8.73 | 5.48 | 1200.85 | 292.90 |
36577 | 2 | 20 | 3.80 | 277.91 | -1.01 | 70.70 | 988.03 | 8.02 | 5.67 | 3.58 | 1240.06 | 210.50 |
36578 | 5 | 29 | 20.06 | 295.10 | 16.70 | 81.00 | 977.90 | 23.51 | 19.04 | 12.20 | 1153.24 | 19.22 |
36579 | 9 | 10 | 25.65 | 299.81 | 15.30 | 52.81 | 988.39 | 32.98 | 17.41 | 11.03 | 1144.61 | 225.40 |
36580 | 3 | 11 | 3.14 | 276.30 | 1.88 | 91.40 | 1000.01 | 7.66 | 7.00 | 4.37 | 1257.47 | 30.67 |
36581 rows × 12 columns
# 각도를 라디안으로 변환하여 사인 함수 적용 후 새로운 컬럼에 저장
train_x['풍향 (sin)'] = np.sin(np.deg2rad(train_x['풍향 (deg)']))
train_x
월 | 일 | 섭씨 온도(°C) | 절대 온도(K) | 이슬점 온도(°C) | 상대 습도 (%) | 대기압(mbar) | 포화 증기압(mbar) | 실제 증기압(mbar) | 수증기 함량 (g/kg) | 공기 밀도 (g/m**3) | 풍향 (deg) | 풍향 (sin) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 7 | 2 | 13.97 | 287.78 | 9.84 | 76.10 | 992.08 | 15.98 | 12.16 | 7.66 | 1198.06 | 155.60 | 0.413104 |
1 | 8 | 21 | 16.94 | 290.85 | 12.14 | 73.30 | 991.07 | 19.33 | 14.17 | 8.94 | 1183.67 | 177.00 | 0.052336 |
2 | 11 | 1 | 9.76 | 283.84 | 5.40 | 74.20 | 988.71 | 12.10 | 8.98 | 5.67 | 1213.22 | 146.20 | 0.556296 |
3 | 12 | 28 | 5.27 | 277.30 | 2.71 | 83.50 | 1014.25 | 8.89 | 7.43 | 4.57 | 1265.48 | 264.50 | -0.995396 |
4 | 9 | 26 | 17.35 | 290.86 | 12.68 | 74.00 | 995.77 | 19.84 | 14.68 | 9.22 | 1187.40 | 19.34 | 0.331173 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
36576 | 8 | 10 | 14.10 | 287.79 | 4.99 | 54.15 | 993.53 | 16.11 | 8.73 | 5.48 | 1200.85 | 292.90 | -0.921185 |
36577 | 2 | 20 | 3.80 | 277.91 | -1.01 | 70.70 | 988.03 | 8.02 | 5.67 | 3.58 | 1240.06 | 210.50 | -0.507538 |
36578 | 5 | 29 | 20.06 | 295.10 | 16.70 | 81.00 | 977.90 | 23.51 | 19.04 | 12.20 | 1153.24 | 19.22 | 0.329196 |
36579 | 9 | 10 | 25.65 | 299.81 | 15.30 | 52.81 | 988.39 | 32.98 | 17.41 | 11.03 | 1144.61 | 225.40 | -0.712026 |
36580 | 3 | 11 | 3.14 | 276.30 | 1.88 | 91.40 | 1000.01 | 7.66 | 7.00 | 4.37 | 1257.47 | 30.67 | 0.510093 |
36581 rows × 13 columns
test_x['풍향 (sin)'] = np.sin(np.deg2rad(test_x['풍향 (deg)']))
test_x
월 | 일 | 섭씨 온도(°C) | 절대 온도(K) | 이슬점 온도(°C) | 상대 습도 (%) | 대기압(mbar) | 포화 증기압(mbar) | 실제 증기압(mbar) | 수증기 함량 (g/kg) | 공기 밀도 (g/m**3) | 풍향 (deg) | 풍향 (sin) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 3 | 24 | 4.28 | 278.68 | -0.21 | 72.5 | 984.48 | 8.30 | 6.02 | 3.81 | 1233.29 | 251.80 | -0.949972 |
1 | 9 | 24 | 13.40 | 286.81 | 10.36 | 81.8 | 996.98 | 15.40 | 12.59 | 7.89 | 1206.20 | 225.60 | -0.714473 |
2 | 5 | 28 | 19.89 | 294.33 | 14.95 | 73.2 | 984.83 | 23.26 | 17.03 | 10.82 | 1163.06 | 10.39 | 0.180347 |
3 | 1 | 17 | -2.88 | 270.44 | -4.47 | 88.7 | 998.02 | 4.94 | 4.38 | 2.73 | 1284.19 | 260.20 | -0.985408 |
4 | 10 | 22 | 6.97 | 281.18 | 4.36 | 83.4 | 987.00 | 10.01 | 8.35 | 5.28 | 1223.47 | 262.50 | -0.991445 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
15673 | 3 | 16 | 1.61 | 274.67 | -0.49 | 85.9 | 1001.26 | 6.86 | 5.89 | 3.67 | 1266.62 | 56.18 | 0.830790 |
15674 | 9 | 5 | 18.27 | 292.19 | 13.35 | 73.0 | 990.98 | 21.03 | 15.35 | 9.69 | 1177.63 | 265.30 | -0.996637 |
15675 | 8 | 8 | 16.08 | 289.66 | 12.25 | 78.0 | 994.97 | 18.30 | 14.28 | 8.97 | 1191.84 | 189.80 | -0.170209 |
15676 | 2 | 4 | 3.07 | 276.68 | -0.95 | 74.8 | 994.31 | 7.62 | 5.70 | 3.57 | 1251.24 | 269.70 | -0.999986 |
15677 | 2 | 1 | 9.01 | 283.38 | 8.36 | 95.7 | 985.13 | 11.50 | 11.01 | 6.98 | 1211.08 | 196.20 | -0.278991 |
15678 rows × 13 columns
train_x = train_x.drop('풍향 (deg)', axis=1)
test_x = test_x.drop('풍향 (deg)', axis=1)
# 상관관계 분석
correlation_matrix = train_x.corr()
plt.figure(figsize=(8, 6))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', fmt='.2f', linewidths=0.5)
plt.title('Correlation Heatmap', fontsize=16)
plt.show()
# VIF 계산
vif = pd.DataFrame()
vif["Variables"] = train_x.columns
vif["VIF"] = [variance_inflation_factor(train_x.values, i) for i in range(train_x.shape[1])]
vif
# 일반적으로 VIF 값이 5 이상인 변수는 다중공선성의 문제가 있을 수 있으며, VIF 값이 10 이상이면 심각한 다중공선성이 발생하는 것으로 간주
Variables | VIF | |
---|---|---|
0 | 월 | 6.419815e+00 |
1 | 일 | 4.397569e+00 |
2 | 섭씨 온도(°C) | 4.712025e+04 |
3 | 절대 온도(K) | 7.384321e+04 |
4 | 이슬점 온도(°C) | 9.470483e+02 |
5 | 상대 습도 (%) | 1.687427e+03 |
6 | 대기압(mbar) | 2.183091e+07 |
7 | 포화 증기압(mbar) | 1.012713e+03 |
8 | 실제 증기압(mbar) | 2.328108e+05 |
9 | 수증기 함량 (g/kg) | 2.218115e+05 |
10 | 공기 밀도 (g/m**3) | 1.932552e+07 |
11 | 풍향 (sin) | 1.113599e+00 |
# 상관관계가 1인 변수들의 분산 확인
# 두 개 중 분산이 작은 것을 삭제
train_x.describe()
월 | 일 | 섭씨 온도(°C) | 절대 온도(K) | 이슬점 온도(°C) | 상대 습도 (%) | 대기압(mbar) | 포화 증기압(mbar) | 실제 증기압(mbar) | 수증기 함량 (g/kg) | 공기 밀도 (g/m**3) | 풍향 (sin) | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 | 36581.000000 |
mean | 6.511468 | 15.648561 | 9.992694 | 283.971095 | 5.453064 | 75.827418 | 990.032953 | 14.037167 | 9.786895 | 6.178010 | 1214.580682 | -0.166860 |
std | 3.458678 | 8.803467 | 8.237177 | 8.354235 | 6.327009 | 16.089188 | 8.824477 | 8.075680 | 4.185701 | 2.655054 | 39.528520 | 0.633524 |
min | 1.000000 | 1.000000 | -13.930000 | 258.870000 | -15.560000 | 23.640000 | 956.890000 | 2.070000 | 1.810000 | 1.120000 | 1110.650000 | -1.000000 |
25% | 4.000000 | 8.000000 | 3.650000 | 277.700000 | 0.450000 | 65.360000 | 985.100000 | 7.940000 | 6.310000 | 3.980000 | 1184.030000 | -0.739631 |
50% | 7.000000 | 16.000000 | 9.130000 | 283.170000 | 5.070000 | 79.000000 | 990.250000 | 11.590000 | 8.770000 | 5.520000 | 1214.650000 | -0.294040 |
75% | 9.000000 | 23.000000 | 16.350000 | 290.370000 | 10.950000 | 89.200000 | 995.700000 | 18.620000 | 13.100000 | 8.270000 | 1241.600000 | 0.420719 |
max | 12.000000 | 31.000000 | 34.340000 | 308.620000 | 20.150000 | 100.000000 | 1015.300000 | 54.270000 | 23.640000 | 14.980000 | 1349.460000 | 1.000000 |
절대 온도와 섭씨 온도는 상관관계가 1이므로 절대 온도 Drop
수증기 함량과 실제 증기압 중 분산이 작은 수증기 함량 삭제
다중공선성이 높은 이슬점 온도 Drop
train_x = train_x.drop(['절대 온도(K)', '이슬점 온도(°C)', '공기 밀도 (g/m**3)', '포화 증기압(mbar)', '수증기 함량 (g/kg)'], axis=1)
test_x = test_x.drop(['절대 온도(K)', '이슬점 온도(°C)', '공기 밀도 (g/m**3)', '포화 증기압(mbar)', '수증기 함량 (g/kg)'], axis=1)
# 컬럼 삭제 후 상관관계 재확인
correlation_matrix = train_x.corr()
plt.figure(figsize=(8, 6))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', fmt='.2f', linewidths=0.5)
plt.title('Correlation Heatmap', fontsize=16)
plt.show()
sns.pairplot(train_x)
plt.title("Pair Plot")
plt.show()
# 학습 데이터와 테스트 데이터로 분리
X_train, X_test, y_train, y_test = train_test_split(train_x, train_y, test_size=0.2, shuffle=True, random_state=42)
# 학습 데이터로 StandardScaler 객체 생성 및 적용
scaler = StandardScaler()
scaler.fit(X_train)
# 학습 데이터와 테스트 데이터를 각각 표준화
X_train_scaled = scaler.transform(X_train)
X_test_scaled = scaler.transform(X_test)
# # 'col1', 'col2', 'col3' 컬럼만 선택하여 스케일링할 경우
# selected_cols = ['col1', 'col2', 'col3']
# # StandardScaler 객체 생성
# scaler = StandardScaler()
# # 'col1', 'col2', 'col3' 컬럼만 스케일링하기 위해 해당 컬럼들을 추출하여 스케일링
# X_train_scaled_selected = X_train[selected_cols].copy()
# X_train_scaled_selected = scaler.fit_transform(X_train_scaled_selected)
# # 스케일링된 컬럼들을 다시 원래 X_train에 삽입
# X_train[selected_cols] = X_train_scaled_selected
# # 나머지 컬럼들은 스케일링하지 않으므로 그대로 사용
# # ...
# # 위와 동일한 방식으로 X_test에 대해서도 스케일링 수행
# X_test_scaled_selected = X_test[selected_cols].copy()
# X_test_scaled_selected = scaler.transform(X_test_scaled_selected)
# X_test[selected_cols] = X_test_scaled_selected
# # 나머지 컬럼들은 스케일링하지 않으므로 그대로 사용
# # ...
from hyperopt import fmin, tpe, hp, Trials
# 데이터를 준비하고 X_train, y_train, X_test, y_test로 나누는 등의 전처리 작업을 수행합니다.
# Objective 함수 정의 (하이퍼파라미터 튜닝 대상)
def objective(params):
model = XGBRegressor(
n_estimators=int(params['n_estimators']),
max_depth=int(params['max_depth']),
learning_rate=params['learning_rate'],
reg_lambda=params['reg_lambda'],
colsample_bytree=params['colsample_bytree'],
max_leaves=int(params['max_leaves']),
subsample=params['subsample'],
gamma=params['gamma'],
random_state=42,
n_jobs=-1,
# tree_method='gpu_hist', gpu_id=0
)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
return mae
# 하이퍼파라미터 공간 정의
space = {
'n_estimators': hp.quniform('n_estimators', 150, 250, 1),
'max_depth': hp.quniform('max_depth', 10, 20, 1),
'learning_rate': hp.loguniform('learning_rate', np.log(0.001), np.log(0.1)),
'reg_lambda': hp.loguniform('reg_lambda', 0.01, 5.0),
'colsample_bytree': hp.uniform('colsample_bytree', 0.1, 1.0),
'max_leaves': hp.quniform('max_leaves', 20, 40, 1),
'subsample': hp.uniform('subsample', 0.1, 1.0),
'gamma': hp.uniform('gamma', 0.0, 0.5)
}
# Hyperopt를 사용하여 하이퍼파라미터 튜닝
trials = Trials()
best = fmin(fn=objective,
space=space,
algo=tpe.suggest,
max_evals=200, # 시도할 횟수
trials=trials,
verbose=1)
100%|██████████| 200/200 [1:22:43<00:00, 24.82s/trial, best loss: 0.4284746343314493]
# 최적의 하이퍼파라미터 출력
print("Best Hyperparameters:")
print(best)
Best Hyperparameters: {'colsample_bytree': 0.8732693318423104, 'gamma': 0.010459570332818009, 'learning_rate': 0.025559040901251454, 'max_depth': 20.0, 'max_leaves': 33.0, 'n_estimators': 239.0, 'reg_lambda': 2.5562770225550766, 'subsample': 0.692511632552637}
# 최적의 하이퍼파라미터로 모델 생성
best_model = XGBRegressor(
n_estimators=int(best['n_estimators']),
max_depth=int(best['max_depth']),
learning_rate=best['learning_rate'],
reg_lambda=best['reg_lambda'],
colsample_bytree=best['colsample_bytree'],
max_leaves=int(best['max_leaves']),
subsample=best['subsample'],
gamma=best['gamma'],
random_state=42,
n_jobs=-1,
# tree_method='gpu_hist', gpu_id=0
)
# 최적의 하이퍼파라미터로 모델 훈련
best_model.fit(X_train, y_train)
XGBRegressor(base_score=None, booster=None, callbacks=None, colsample_bylevel=None, colsample_bynode=None, colsample_bytree=0.8732693318423104, early_stopping_rounds=None, enable_categorical=False, eval_metric=None, feature_types=None, gamma=0.010459570332818009, gpu_id=None, grow_policy=None, importance_type=None, interaction_constraints=None, learning_rate=0.025559040901251454, max_bin=None, max_cat_threshold=None, max_cat_to_onehot=None, max_delta_step=None, max_depth=20, max_leaves=33, min_child_weight=None, missing=nan, monotone_constraints=None, n_estimators=239, n_jobs=-1, num_parallel_tree=None, predictor=None, random_state=42, ...)
# 예측 결과 출력
y_pred = best_model.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
print("MAE: {:.4f}".format(mae))
# 0.4252 {'colsample_bytree': 0.9, 'learning_rate': 0.03, 'max_depth': 19, 'max_leaves': 29, 'n_estimators': 195, 'subsample': 0.7}
MAE: 0.4285
xgboost.plot_importance(best_model)
<AxesSubplot:title={'center':'Feature importance'}, xlabel='F score', ylabel='Features'>
# dt_pred = dt_clf.predict(test_x)
# 테스트 데이터로 예측
xgb_pred = best_model.predict(test_x)
submission = pd.read_csv('./data/sample_submission.csv')
submission['풍속 (m/s)'] = xgb_pred
submission.head()
ID | 풍속 (m/s) | |
---|---|---|
0 | TEST_00000 | 2.317696 |
1 | TEST_00001 | 1.054162 |
2 | TEST_00002 | 2.042070 |
3 | TEST_00003 | 1.057729 |
4 | TEST_00004 | 1.240995 |
submission.to_csv('submission/submission_'+datetime.datetime.now().strftime("%Y%m%d%H%M")+'.csv', index= False)
//끝//