맨하탄 월세 분석 - 다중회귀
맨하탄의 월세를 분석해 봅시다. 다중회귀를 이용할 것입니다. 늘 그렇듯이 데이터 먼저 확인해 보시죠.
데이터를 읽어보자~
import pandas as pd
import requests
import io
from IPython.display import display
pd.set_option('mode.chained_assignment', None) # <==== SettingWithCopyWarning 경고를 끈다
url = "https://raw.githubusercontent.com/Codecademy/datasets/master/streeteasy/manhattan.csv"
download = requests.get(url).content
df_raw = pd.read_csv(io.StringIO(download.decode('utf-8')))
df_raw.head()
rental_id | rent | bedrooms | bathrooms | size_sqft | min_to_subway | floor | building_age_yrs | no_fee | has_roofdeck | has_washer_dryer | has_doorman | has_elevator | has_dishwasher | has_patio | has_gym | neighborhood | borough | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1545 | 2550 | 0.0 | 1 | 480 | 9 | 2.0 | 17 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | Upper East Side | Manhattan |
1 | 2472 | 11500 | 2.0 | 2 | 2000 | 4 | 1.0 | 96 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Greenwich Village | Manhattan |
2 | 2919 | 4500 | 1.0 | 1 | 916 | 2 | 51.0 | 29 | 0 | 1 | 0 | 1 | 1 | 1 | 0 | 0 | Midtown | Manhattan |
3 | 2790 | 4795 | 1.0 | 1 | 975 | 3 | 8.0 | 31 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | Greenwich Village | Manhattan |
4 | 3946 | 17500 | 2.0 | 2 | 4800 | 3 | 4.0 | 136 | 0 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | Soho | Manhattan |
호 컬럼이 꽤 많군요. 컬럼이 뭐뭐 있나 한번 봅시다.
print(len(df_raw.columns))
df_raw.columns
18
Index(['rental_id', 'rent', 'bedrooms', 'bathrooms', 'size_sqft', 'min_to_subway', 'floor', 'building_age_yrs', 'no_fee', 'has_roofdeck', 'has_washer_dryer', 'has_doorman', 'has_elevator', 'has_dishwasher', 'has_patio', 'has_gym', 'neighborhood', 'borough'], dtype='object')
머, 대~충 봐도 어떤 condition들이 포함되는지 감이 오긴 오는군요. 이떄, 종속변수는 rent column이고, 나머지는 독립변수라는 점만 확실히 하면 이제부터는 맘편하게 할 수 있겠습니다. 그러면 데이터세트 모양새는 어떨까요~?
df_raw.info()
RangeIndex: 3539 entries, 0 to 3538 Data columns (total 18 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 rental_id 3539 non-null int64 1 rent 3539 non-null int64 2 bedrooms 3539 non-null float64 3 bathrooms 3539 non-null int64 4 size_sqft 3539 non-null int64 5 min_to_subway 3539 non-null int64 6 floor 3539 non-null float64 7 building_age_yrs 3539 non-null int64 8 no_fee 3539 non-null int64 9 has_roofdeck 3539 non-null int64 10 has_washer_dryer 3539 non-null int64 11 has_doorman 3539 non-null int64 12 has_elevator 3539 non-null int64 13 has_dishwasher 3539 non-null int64 14 has_patio 3539 non-null int64 15 has_gym 3539 non-null int64 16 neighborhood 3539 non-null object 17 borough 3539 non-null object dtypes: float64(2), int64(14), object(2) memory usage: 497.8+ KB
오 장난 없네요. 데이터 크기 무엇? 3539개나 되다니. 꽤 데이터양이 많습니다. 그리고 결측치가 없네요? 오. 뭔가 꽤나 깨끗한 데이터세트인 것 같습니다.
그르면~ 기본 통계치정도는 볼까요?
df_raw.describe()
rental_id | rent | bedrooms | bathrooms | size_sqft | min_to_subway | floor | building_age_yrs | no_fee | has_roofdeck | has_washer_dryer | has_doorman | has_elevator | has_dishwasher | has_patio | has_gym | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 3539.000000 | 3539.000000 | 3539.000000 | 3539.000000 | 3539.000000 | 3539.000000 | 3539.000000 | 3539.000000 | 3539.000000 | 3539.000000 | 3539.000000 | 3539.000000 | 3539.000000 | 3539.000000 | 3539.000000 | 3539.000000 |
mean | 5332.589997 | 5138.940379 | 1.351936 | 1.366770 | 939.727324 | 4.970896 | 11.908307 | 51.994914 | 0.403504 | 0.154846 | 0.160215 | 0.281153 | 0.294716 | 0.185646 | 0.055100 | 0.174908 |
std | 3311.552136 | 3162.824760 | 0.967595 | 0.599588 | 477.949074 | 5.513589 | 10.960893 | 39.380433 | 0.490669 | 0.361809 | 0.366857 | 0.449625 | 0.455979 | 0.388875 | 0.228208 | 0.379942 |
min | 1.000000 | 1300.000000 | 0.000000 | 0.000000 | 250.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
25% | 2443.500000 | 3150.000000 | 1.000000 | 1.000000 | 613.000000 | 2.000000 | 4.000000 | 15.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
50% | 5128.000000 | 4000.000000 | 1.000000 | 1.000000 | 800.000000 | 4.000000 | 8.000000 | 39.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 |
75% | 8149.500000 | 6000.000000 | 2.000000 | 2.000000 | 1141.000000 | 6.000000 | 17.000000 | 90.000000 | 1.000000 | 0.000000 | 0.000000 | 1.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 |
max | 11349.000000 | 20000.000000 | 5.000000 | 5.000000 | 4800.000000 | 43.000000 | 83.000000 | 180.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 |
연속형 변수와 불리언 독립변수가 섞여 있네요. 뭐, 신경쓰지 말고 일단 rent를 종속변수로 해서 곧바로 회귀를 해보죠. 머.
아 잠시, 필요없는 컬럼이 있네요. rental_id는 빼고 가시죠~
df_data = df_raw.copy()
df_data.drop("rental_id", axis=1, inplace=True)
df_data.head(2)
rent | bedrooms | bathrooms | size_sqft | min_to_subway | floor | building_age_yrs | no_fee | has_roofdeck | has_washer_dryer | has_doorman | has_elevator | has_dishwasher | has_patio | has_gym | neighborhood | borough | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2550 | 0.0 | 1 | 480 | 9 | 2.0 | 17 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 | Upper East Side | Manhattan |
1 | 11500 | 2.0 | 2 | 2000 | 4 | 1.0 | 96 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | Greenwich Village | Manhattan |
엣, 막상 회귀를 하려고 보니, neighborhood하고, borough도 쓸 수 있는 데이터인가 모르겠는데, 조금 귀찮으니까 버리죠 머~
- 사실 one hot encoding이 가능한지 한번 살펴보는 것도 좋은 시도가 되지 않을까 생각합니다만. -
df_data.drop(['neighborhood', 'borough'], axis=1, inplace=True)
print(len(df_data.columns))
df_data.head(2)
15
rent | bedrooms | bathrooms | size_sqft | min_to_subway | floor | building_age_yrs | no_fee | has_roofdeck | has_washer_dryer | has_doorman | has_elevator | has_dishwasher | has_patio | has_gym | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2550 | 0.0 | 1 | 480 | 9 | 2.0 | 17 | 1 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
1 | 11500 | 2.0 | 2 | 2000 | 4 | 1.0 | 96 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
이렇게 되면 종속 변수 rent빼고 14개의 컬럼으로 회귀를 해 보겠군요. 회귀와 머신러닝의 가장 큰 차이점은 회귀는 test가 없고, 모든 관측치를 이용해서 fitting을 한다는 점입니다. 그러니까, 모든 컬럼의 데이터를 이용해서 OLS를 해 보겠습니다. 후후.
회귀를 해 보자
from statsmodels.formula.api import ols
col_features = [col for col in df_data.columns if col != "rent"] # 컬럼 이름을 다 치기 귀찮으니까...
col_features_ = " + ".join(col_features)
eq_ols = 'rent ~ '+col_features_
model = ols(eq_ols, data=df_data).fit() # y~x는 y=... x라는 뜻
print(model.summary())
model.summary()
OLS Regression Results ============================================================================== Dep. Variable: rent R-squared: 0.779 Model: OLS Adj. R-squared: 0.778 Method: Least Squares F-statistic: 888.8 Date: Wed, 02 Nov 2022 Prob (F-statistic): 0.00 Time: 15:36:04 Log-Likelihood: -30869. No. Observations: 3539 AIC: 6.177e+04 Df Residuals: 3524 BIC: 6.186e+04 Df Model: 14 Covariance Type: nonrobust ==================================================================================== coef std err t P>|t| [0.025 0.975] ------------------------------------------------------------------------------------ Intercept -422.1606 96.095 -4.393 0.000 -610.569 -233.752 bedrooms -315.4940 42.604 -7.405 0.000 -399.025 -231.963 bathrooms 1181.0643 74.498 15.854 0.000 1035.002 1327.127 size_sqft 4.9174 0.101 48.565 0.000 4.719 5.116 min_to_subway -16.4173 4.649 -3.531 0.000 -25.533 -7.302 floor 23.3572 2.518 9.276 0.000 18.420 28.294 building_age_yrs -7.4807 0.725 -10.318 0.000 -8.902 -6.059 no_fee -130.3260 54.208 -2.404 0.016 -236.607 -24.044 has_roofdeck 31.1217 87.536 0.356 0.722 -140.505 202.748 has_washer_dryer 152.6844 79.766 1.914 0.056 -3.708 309.076 has_doorman -159.6799 85.765 -1.862 0.063 -327.834 8.474 has_elevator 87.3039 87.588 0.997 0.319 -84.425 259.033 has_dishwasher -26.7949 76.469 -0.350 0.726 -176.722 123.133 has_patio -103.0681 111.952 -0.921 0.357 -322.565 116.429 has_gym -11.8094 95.996 -0.123 0.902 -200.024 176.405 ============================================================================== Omnibus: 1005.431 Durbin-Watson: 2.076 Prob(Omnibus): 0.000 Jarque-Bera (JB): 10371.268 Skew: 1.048 Prob(JB): 0.00 Kurtosis: 11.120 Cond. No. 4.75e+03 ============================================================================== Notes: [1] Standard Errors assume that the covariance matrix of the errors is correctly specified. [2] The condition number is large, 4.75e+03. This might indicate that there are strong multicollinearity or other numerical problems.
Dep. Variable: | rent | R-squared: | 0.779 |
---|---|---|---|
Model: | OLS | Adj. R-squared: | 0.778 |
Method: | Least Squares | F-statistic: | 888.8 |
Date: | Wed, 02 Nov 2022 | Prob (F-statistic): | 0.00 |
Time: | 15:36:04 | Log-Likelihood: | -30869. |
No. Observations: | 3539 | AIC: | 6.177e+04 |
Df Residuals: | 3524 | BIC: | 6.186e+04 |
Df Model: | 14 | ||
Covariance Type: | nonrobust |
coef | std err | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
Intercept | -422.1606 | 96.095 | -4.393 | 0.000 | -610.569 | -233.752 |
bedrooms | -315.4940 | 42.604 | -7.405 | 0.000 | -399.025 | -231.963 |
bathrooms | 1181.0643 | 74.498 | 15.854 | 0.000 | 1035.002 | 1327.127 |
size_sqft | 4.9174 | 0.101 | 48.565 | 0.000 | 4.719 | 5.116 |
min_to_subway | -16.4173 | 4.649 | -3.531 | 0.000 | -25.533 | -7.302 |
floor | 23.3572 | 2.518 | 9.276 | 0.000 | 18.420 | 28.294 |
building_age_yrs | -7.4807 | 0.725 | -10.318 | 0.000 | -8.902 | -6.059 |
no_fee | -130.3260 | 54.208 | -2.404 | 0.016 | -236.607 | -24.044 |
has_roofdeck | 31.1217 | 87.536 | 0.356 | 0.722 | -140.505 | 202.748 |
has_washer_dryer | 152.6844 | 79.766 | 1.914 | 0.056 | -3.708 | 309.076 |
has_doorman | -159.6799 | 85.765 | -1.862 | 0.063 | -327.834 | 8.474 |
has_elevator | 87.3039 | 87.588 | 0.997 | 0.319 | -84.425 | 259.033 |
has_dishwasher | -26.7949 | 76.469 | -0.350 | 0.726 | -176.722 | 123.133 |
has_patio | -103.0681 | 111.952 | -0.921 | 0.357 | -322.565 | 116.429 |
has_gym | -11.8094 | 95.996 | -0.123 | 0.902 | -200.024 | 176.405 |
Omnibus: | 1005.431 | Durbin-Watson: | 2.076 |
---|---|---|---|
Prob(Omnibus): | 0.000 | Jarque-Bera (JB): | 10371.268 |
Skew: | 1.048 | Prob(JB): | 0.00 |
Kurtosis: | 11.120 | Cond. No. | 4.75e+03 |
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 4.75e+03. This might indicate that there are
strong multicollinearity or other numerical problems.
어머 이거 보세요. R_squared가 0.779입니다요? 와. 꽤 설명력이 좋은 회귀분석이 이루어졌나 봅니다. 여튼, 각 feature들의 영향력을 분석해 보겠습니다.
계수에 관련한 정보만 빼와서 한번 보시죠.
df_analysis = model.summary2().tables[1] # summary2는 Dataframe으로 데이터를 볼 수 있죠.
df_analysis
Coef. | Std.Err. | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
Intercept | -422.160580 | 96.095495 | -4.393136 | 1.150158e-05 | -610.569000 | -233.752161 |
bedrooms | -315.493951 | 42.604011 | -7.405264 | 1.629045e-13 | -399.024968 | -231.962934 |
bathrooms | 1181.064349 | 74.497518 | 15.853741 | 9.896122e-55 | 1035.001731 | 1327.126968 |
size_sqft | 4.917379 | 0.101255 | 48.564514 | 0.000000e+00 | 4.718856 | 5.115903 |
min_to_subway | -16.417272 | 4.649295 | -3.531131 | 4.190861e-04 | -25.532855 | -7.301690 |
floor | 23.357230 | 2.518140 | 9.275589 | 2.997213e-20 | 18.420071 | 28.294389 |
building_age_yrs | -7.480691 | 0.724996 | -10.318246 | 1.301044e-24 | -8.902146 | -6.059236 |
no_fee | -130.325986 | 54.207626 | -2.404200 | 1.625920e-02 | -236.607485 | -24.044487 |
has_roofdeck | 31.121729 | 87.536098 | 0.355530 | 7.222137e-01 | -140.504818 | 202.748275 |
has_washer_dryer | 152.684388 | 79.765939 | 1.914155 | 5.568123e-02 | -3.707693 | 309.076470 |
has_doorman | -159.679867 | 85.765094 | -1.861828 | 6.271050e-02 | -327.834117 | 8.474384 |
has_elevator | 87.303873 | 87.588352 | 0.996752 | 3.189533e-01 | -84.425124 | 259.032870 |
has_dishwasher | -26.794883 | 76.468776 | -0.350403 | 7.260573e-01 | -176.722424 | 123.132658 |
has_patio | -103.068067 | 111.951907 | -0.920646 | 3.572983e-01 | -322.565162 | 116.429028 |
has_gym | -11.809396 | 95.996391 | -0.123019 | 9.020989e-01 | -200.023510 | 176.404718 |
자, 여기에서 보면 지금은 보기 싫은 row가 있는데 intercept입니다. 이거 없애볼까요?
df_analysis.drop(["Intercept"], axis=0, inplace=True)
df_analysis.head()
Coef. | Std.Err. | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
bedrooms | -315.493951 | 42.604011 | -7.405264 | 1.629045e-13 | -399.024968 | -231.962934 |
bathrooms | 1181.064349 | 74.497518 | 15.853741 | 9.896122e-55 | 1035.001731 | 1327.126968 |
size_sqft | 4.917379 | 0.101255 | 48.564514 | 0.000000e+00 | 4.718856 | 5.115903 |
min_to_subway | -16.417272 | 4.649295 | -3.531131 | 4.190861e-04 | -25.532855 | -7.301690 |
floor | 23.357230 | 2.518140 | 9.275589 | 2.997213e-20 | 18.420071 | 28.294389 |
네, 그런대로 이제 볼 수 있겠는데, 이 분석결과에서 영향력이 큰 Coefficient를 sorting해서 찾아보겠습니다.요
df_analysis = df_analysis[['Coef.', 't', 'P>|t|', 'Std.Err.']] # 테이블로 만들어서
df_analysis = df_analysis.sort_values(by="Coef.", key=abs, ascending=False)
df_analysis
Coef. | t | P>|t| | Std.Err. | |
---|---|---|---|---|
bathrooms | 1181.064349 | 15.853741 | 9.896122e-55 | 74.497518 |
bedrooms | -315.493951 | -7.405264 | 1.629045e-13 | 42.604011 |
has_doorman | -159.679867 | -1.861828 | 6.271050e-02 | 85.765094 |
has_washer_dryer | 152.684388 | 1.914155 | 5.568123e-02 | 79.765939 |
no_fee | -130.325986 | -2.404200 | 1.625920e-02 | 54.207626 |
has_patio | -103.068067 | -0.920646 | 3.572983e-01 | 111.951907 |
has_elevator | 87.303873 | 0.996752 | 3.189533e-01 | 87.588352 |
has_roofdeck | 31.121729 | 0.355530 | 7.222137e-01 | 87.536098 |
has_dishwasher | -26.794883 | -0.350403 | 7.260573e-01 | 76.468776 |
floor | 23.357230 | 9.275589 | 2.997213e-20 | 2.518140 |
min_to_subway | -16.417272 | -3.531131 | 4.190861e-04 | 4.649295 |
has_gym | -11.809396 | -0.123019 | 9.020989e-01 | 95.996391 |
building_age_yrs | -7.480691 | -10.318246 | 1.301044e-24 | 0.724996 |
size_sqft | 4.917379 | 48.564514 | 0.000000e+00 | 0.101255 |
이거 보니까, bathrooms 수에 대해, bedroom 수에 대해, door맨이 있는지등이 영향을 크게 미치네요. 의외로 지하철까지의 거리하고 방 크기가 다른 것들에 비해 큰 영향을 미치지 못하는군요.
그래도 대충 해석해 보자면, 의외 결과이긴 한데, bedroom 수가 많으면 월세가 떨어집니다? 오?
다른 것들은 그런대로 상식적이지 않나 싶은데요, doorman이 있으면 월세가 올라가고요, 지하철까지 멀면 월세가 떨어지고요, 방 크기가 크면 월세가 올라갑니다. 연식이 오래되면 월세가 떨어지고요. 쩝.
대충 봤으니, p value를 보고 95%신뢰도로 significant하지 않은 것들이 무엇이 있는지 보죠.
df_analysis.loc[df_analysis["P>|t|"] >= 0.05/2] #유의하지 않은 것들
Coef. | t | P>|t| | Std.Err. | |
---|---|---|---|---|
has_doorman | -159.679867 | -1.861828 | 0.062711 | 85.765094 |
has_washer_dryer | 152.684388 | 1.914155 | 0.055681 | 79.765939 |
has_patio | -103.068067 | -0.920646 | 0.357298 | 111.951907 |
has_elevator | 87.303873 | 0.996752 | 0.318953 | 87.588352 |
has_roofdeck | 31.121729 | 0.355530 | 0.722214 | 87.536098 |
has_dishwasher | -26.794883 | -0.350403 | 0.726057 | 76.468776 |
has_gym | -11.809396 | -0.123019 | 0.902099 | 95.996391 |
어랏, Boolean Feature들이 영향력(계수) = 0의 Null Hypothesis를 기각하기가 어렵네요. 쩝. 그러면 영향력이 없다고 생각해도 괜찮지 않을까 생각합니다.만 가만히 보면 계수의 값이 엄청 크군요? 영향력이 큰데 유의하지 않다니.. 왜 이런 일이 벌어지는 건가요.
회귀 계수의 값이 큰데 유의하지 않다?
왜 이런 일이 벌어지냐면, 다중회귀에서 걍 회귀를 하면 회귀 계수의 값이 크다고 해서 영향력이 크다고 보기 어려운 것이 단위가 달라서 그렇습니다. 예를 들면 doorman이 있냐 없냐 1, 0의 값인데, 0에서 1이 될 때 월세는 엄청나게 변하게 되니까 계수가 커야만 하는거죠. 오. 그러면, 회귀 계수가 영향력을 의미한다고 했는데, 그건 개소리인가요? 아닙니다. 이런 경우에는 관측데이터를 표준화해서 보면 해석하기가 좀 낫습니다. 후후.
그러면, 관측데이터를 표준화해서 다시 볼까요?
표준화를 해서 회귀를 해 보자
from sklearn.preprocessing import StandardScaler
df_data_norm = df_data.copy()
df_data_norm[col_features] = StandardScaler().fit_transform(df_data_norm[col_features])
df_data_norm.head()
rent | bedrooms | bathrooms | size_sqft | min_to_subway | floor | building_age_yrs | no_fee | has_roofdeck | has_washer_dryer | has_doorman | has_elevator | has_dishwasher | has_patio | has_gym | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 2550 | -1.397410 | -0.611790 | -0.962011 | 0.730862 | -0.904097 | -0.888763 | 1.21585 | 2.336243 | -0.436784 | -0.625393 | 1.546964 | 2.094423 | -0.241482 | 2.171931 |
1 | 11500 | 0.669863 | 1.056257 | 2.218694 | -0.176116 | -0.995343 | 1.117593 | -0.82247 | -0.428038 | -0.436784 | -0.625393 | -0.646428 | -0.477459 | -0.241482 | -0.460420 |
2 | 4500 | -0.363774 | -0.611790 | -0.049651 | -0.538908 | 3.566974 | -0.584000 | -0.82247 | 2.336243 | -0.436784 | 1.598995 | 1.546964 | 2.094423 | -0.241482 | -0.460420 |
3 | 4795 | -0.363774 | -0.611790 | 0.073811 | -0.357512 | -0.356619 | -0.533206 | -0.82247 | -0.428038 | -0.436784 | 1.598995 | 1.546964 | 2.094423 | -0.241482 | 2.171931 |
4 | 17500 | 0.669863 | 1.056257 | 8.077886 | -0.357512 | -0.721604 | 2.133470 | -0.82247 | -0.428038 | -0.436784 | 1.598995 | 1.546964 | 2.094423 | -0.241482 | 2.171931 |
대~충 이런 식으로 표준화를 하였고요, 그러면 다시 다중회귀를 해 보겠습니다.
model_norm = ols(eq_ols, data=df_data_norm).fit() # y~x는 y=... x라는 뜻
model_norm.summary()
Dep. Variable: | rent | R-squared: | 0.779 |
---|---|---|---|
Model: | OLS | Adj. R-squared: | 0.778 |
Method: | Least Squares | F-statistic: | 888.8 |
Date: | Sun, 30 Oct 2022 | Prob (F-statistic): | 0.00 |
Time: | 10:20:51 | Log-Likelihood: | -30869. |
No. Observations: | 3539 | AIC: | 6.177e+04 |
Df Residuals: | 3524 | BIC: | 6.186e+04 |
Df Model: | 14 | ||
Covariance Type: | nonrobust |
coef | std err | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
Intercept | 5138.9404 | 25.026 | 205.345 | 0.000 | 5089.874 | 5188.007 |
bedrooms | -305.2271 | 41.218 | -7.405 | 0.000 | -386.040 | -224.414 |
bathrooms | 708.0522 | 44.662 | 15.854 | 0.000 | 620.487 | 795.617 |
size_sqft | 2349.9247 | 48.388 | 48.565 | 0.000 | 2255.054 | 2444.795 |
min_to_subway | -90.5053 | 25.631 | -3.531 | 0.000 | -140.758 | -40.253 |
floor | 255.9799 | 27.597 | 9.276 | 0.000 | 201.872 | 310.088 |
building_age_yrs | -294.5512 | 28.547 | -10.318 | 0.000 | -350.521 | -238.582 |
no_fee | -63.9379 | 26.594 | -2.404 | 0.016 | -116.080 | -11.796 |
has_roofdeck | 11.2585 | 31.667 | 0.356 | 0.722 | -50.829 | 73.346 |
has_washer_dryer | 56.0054 | 29.259 | 1.914 | 0.056 | -1.360 | 113.371 |
has_doorman | -71.7860 | 38.557 | -1.862 | 0.063 | -147.382 | 3.810 |
has_elevator | 39.8031 | 39.933 | 0.997 | 0.319 | -38.491 | 118.097 |
has_dishwasher | -10.4184 | 29.733 | -0.350 | 0.726 | -68.713 | 47.876 |
has_patio | -23.5177 | 25.545 | -0.921 | 0.357 | -73.602 | 26.566 |
has_gym | -4.4863 | 36.468 | -0.123 | 0.902 | -75.987 | 67.014 |
Omnibus: | 1005.431 | Durbin-Watson: | 2.076 |
---|---|---|---|
Prob(Omnibus): | 0.000 | Jarque-Bera (JB): | 10371.268 |
Skew: | 1.048 | Prob(JB): | 0.00 |
Kurtosis: | 11.120 | Cond. No. | 4.39 |
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
흠.. 결과를 보니 이번에는 결과가 다르게 보입니다. 흠흠. 계수들만 한번 봅시다.
df_analysis_norm = model_norm.summary2().tables[1]
df_analysis_norm.head()
Coef. | Std.Err. | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
Intercept | 5138.940379 | 25.025937 | 205.344576 | 0.000000e+00 | 5089.873591 | 5188.007166 |
bedrooms | -305.227091 | 41.217584 | -7.405264 | 1.629045e-13 | -386.039827 | -224.414355 |
bathrooms | 708.052169 | 44.661520 | 15.853741 | 9.896122e-55 | 620.487123 | 795.617215 |
size_sqft | 2349.924688 | 48.387691 | 48.564514 | 0.000000e+00 | 2255.053972 | 2444.795404 |
min_to_subway | -90.505300 | 25.630680 | -3.531131 | 4.190861e-04 | -140.757770 | -40.252831 |
자, 새로운 분석 결과도 Coefficient의 중요도 순서로 한번 나열해 보고요~!
df_analysis_norm = df_analysis_norm.sort_values(by="Coef.", key=abs, ascending=False)
df_analysis_norm
Coef. | Std.Err. | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
Intercept | 5138.940379 | 25.025937 | 205.344576 | 0.000000e+00 | 5089.873591 | 5188.007166 |
size_sqft | 2349.924688 | 48.387691 | 48.564514 | 0.000000e+00 | 2255.053972 | 2444.795404 |
bathrooms | 708.052169 | 44.661520 | 15.853741 | 9.896122e-55 | 620.487123 | 795.617215 |
bedrooms | -305.227091 | 41.217584 | -7.405264 | 1.629045e-13 | -386.039827 | -224.414355 |
building_age_yrs | -294.551239 | 28.546639 | -10.318246 | 1.301044e-24 | -350.520846 | -238.581631 |
floor | 255.979928 | 27.597162 | 9.275589 | 2.997213e-20 | 201.871900 | 310.087955 |
min_to_subway | -90.505300 | 25.630680 | -3.531131 | 4.190861e-04 | -140.757770 | -40.252831 |
has_doorman | -71.785990 | 38.556722 | -1.861828 | 6.271050e-02 | -147.381740 | 3.809760 |
no_fee | -63.937945 | 26.594268 | -2.404200 | 1.625920e-02 | -116.079662 | -11.796228 |
has_washer_dryer | 56.005413 | 29.258553 | 1.914155 | 5.568123e-02 | -1.360001 | 113.370826 |
has_elevator | 39.803145 | 39.932843 | 0.996752 | 3.189533e-01 | -38.490680 | 118.096971 |
has_patio | -23.517655 | 25.544733 | -0.920646 | 3.572983e-01 | -73.601614 | 26.566305 |
has_roofdeck | 11.258529 | 31.666868 | 0.355530 | 7.222137e-01 | -50.828716 | 73.345773 |
has_dishwasher | -10.418399 | 29.732625 | -0.350403 | 7.260573e-01 | -68.713294 | 47.876497 |
has_gym | -4.486254 | 36.467928 | -0.123019 | 9.020989e-01 | -75.986638 | 67.014130 |
일단 유의하지 않은 것들을 한번 보시죠.
df_analysis_norm.loc[df_analysis_norm['P>|t|'] > 0.025]
Coef. | Std.Err. | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
has_doorman | -71.785990 | 38.556722 | -1.861828 | 0.062711 | -147.381740 | 3.809760 |
has_washer_dryer | 56.005413 | 29.258553 | 1.914155 | 0.055681 | -1.360001 | 113.370826 |
has_elevator | 39.803145 | 39.932843 | 0.996752 | 0.318953 | -38.490680 | 118.096971 |
has_patio | -23.517655 | 25.544733 | -0.920646 | 0.357298 | -73.601614 | 26.566305 |
has_roofdeck | 11.258529 | 31.666868 | 0.355530 | 0.722214 | -50.828716 | 73.345773 |
has_dishwasher | -10.418399 | 29.732625 | -0.350403 | 0.726057 | -68.713294 | 47.876497 |
has_gym | -4.486254 | 36.467928 | -0.123019 | 0.902099 | -75.986638 | 67.014130 |
여전히 doorman, elevator, gym등이 월세에 대한 영향이 유의하지 않군요? 이것의 의미를 대충 해석한다면, 어쩄든 뭔가 facility가 더 있어야 같은 가격이라도 세입자에게 선택받을 수 있다는 뭐 그런건가 싶은데요.
어찌되었건 표준화 전과 표준화 후의 계수를 비교해 보시죠. 후후
col_compare = ['Coef.', 'P>|t|']
df_analysis_compare = pd.concat([df_analysis[col_compare], df_analysis_norm[col_compare]], axis=1)
df_analysis_compare.columns = ["Coef. (1)", "P>|t| (1)", "Coef. (2)", "P>|t| (2)"]
df_analysis_compare
Coef. (1) | P>|t| (1) | Coef. (2) | P>|t| (2) | |
---|---|---|---|---|
bathrooms | 1181.064349 | 9.896122e-55 | 708.052169 | 9.896122e-55 |
bedrooms | -315.493951 | 1.629045e-13 | -305.227091 | 1.629045e-13 |
has_doorman | -159.679867 | 6.271050e-02 | -71.785990 | 6.271050e-02 |
has_washer_dryer | 152.684388 | 5.568123e-02 | 56.005413 | 5.568123e-02 |
no_fee | -130.325986 | 1.625920e-02 | -63.937945 | 1.625920e-02 |
has_patio | -103.068067 | 3.572983e-01 | -23.517655 | 3.572983e-01 |
has_elevator | 87.303873 | 3.189533e-01 | 39.803145 | 3.189533e-01 |
has_roofdeck | 31.121729 | 7.222137e-01 | 11.258529 | 7.222137e-01 |
has_dishwasher | -26.794883 | 7.260573e-01 | -10.418399 | 7.260573e-01 |
floor | 23.357230 | 2.997213e-20 | 255.979928 | 2.997213e-20 |
min_to_subway | -16.417272 | 4.190861e-04 | -90.505300 | 4.190861e-04 |
has_gym | -11.809396 | 9.020989e-01 | -4.486254 | 9.020989e-01 |
building_age_yrs | -7.480691 | 1.301044e-24 | -294.551239 | 1.301044e-24 |
size_sqft | 4.917379 | 0.000000e+00 | 2349.924688 | 0.000000e+00 |
Intercept | NaN | NaN | 5138.940379 | 0.000000e+00 |
(1)이 표준화전, (2)가 표준화 후입니다. 자, 결과를 가만히 보면 표준화를 한 관측값으로 회귀를 한 결과를 보면 아까 너무 컸던 계수의 크기가 작아진 것을 볼 수 있군요. 오히려 커진 것도 있고요. 그렇지만, p value는 그대로 입니다. 그 점이 재미있는 점! 입니다. 음. 그것 참 신기한 결과로군요. 여튼 이걸 보면, 표준화 하지 않은 결과에서는 계수가 크다고 해서 영향력이 크다고 볼 수는 없다는 것입니다. 그냥 단위가 달라서 그렇게 보일 뿐이죠. 유의한 것들만 한번 보시죠.
df_analysis_norm = df_analysis_norm.loc[df_analysis_norm['P>|t|'] < 0.05]
df_analysis_norm
Coef. | Std.Err. | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
Intercept | 5138.940379 | 25.025937 | 205.344576 | 0.000000e+00 | 5089.873591 | 5188.007166 |
size_sqft | 2349.924688 | 48.387691 | 48.564514 | 0.000000e+00 | 2255.053972 | 2444.795404 |
bathrooms | 708.052169 | 44.661520 | 15.853741 | 9.896122e-55 | 620.487123 | 795.617215 |
bedrooms | -305.227091 | 41.217584 | -7.405264 | 1.629045e-13 | -386.039827 | -224.414355 |
building_age_yrs | -294.551239 | 28.546639 | -10.318246 | 1.301044e-24 | -350.520846 | -238.581631 |
floor | 255.979928 | 27.597162 | 9.275589 | 2.997213e-20 | 201.871900 | 310.087955 |
min_to_subway | -90.505300 | 25.630680 | -3.531131 | 4.190861e-04 | -140.757770 | -40.252831 |
no_fee | -63.937945 | 26.594268 | -2.404200 | 1.625920e-02 | -116.079662 | -11.796228 |
유의한 것들만 가지고 회귀를 한번 해 보겠습니다. 어떨까나~?
표준화 하고, 유의한 것들로만 회귀를 해 보자.
eq_ols = 'rent ~ bathrooms + bedrooms + no_fee + floor + min_to_subway + building_age_yrs + size_sqft'
model_norm_selected = ols(eq_ols, data=df_data_norm).fit() # y~x는 y=... x라는 뜻
model_norm_selected.summary()
Dep. Variable: | rent | R-squared: | 0.779 |
---|---|---|---|
Model: | OLS | Adj. R-squared: | 0.778 |
Method: | Least Squares | F-statistic: | 1776. |
Date: | Sun, 30 Oct 2022 | Prob (F-statistic): | 0.00 |
Time: | 10:21:07 | Log-Likelihood: | -30873. |
No. Observations: | 3539 | AIC: | 6.176e+04 |
Df Residuals: | 3531 | BIC: | 6.181e+04 |
Df Model: | 7 | ||
Covariance Type: | nonrobust |
coef | std err | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
Intercept | 5138.9404 | 25.030 | 205.312 | 0.000 | 5089.866 | 5188.015 |
bathrooms | 704.6164 | 44.604 | 15.797 | 0.000 | 617.164 | 792.069 |
bedrooms | -303.3503 | 41.134 | -7.375 | 0.000 | -384.000 | -222.701 |
no_fee | -59.0600 | 26.006 | -2.271 | 0.023 | -110.049 | -8.071 |
floor | 252.3347 | 27.438 | 9.197 | 0.000 | 198.539 | 306.130 |
min_to_subway | -90.1643 | 25.623 | -3.519 | 0.000 | -140.403 | -39.926 |
building_age_yrs | -294.6879 | 28.458 | -10.355 | 0.000 | -350.485 | -238.891 |
size_sqft | 2353.3132 | 48.305 | 48.718 | 0.000 | 2258.605 | 2448.021 |
Omnibus: | 1012.726 | Durbin-Watson: | 2.078 |
---|---|---|---|
Prob(Omnibus): | 0.000 | Jarque-Bera (JB): | 10585.960 |
Skew: | 1.054 | Prob(JB): | 0.00 |
Kurtosis: | 11.206 | Cond. No. | 3.80 |
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
분석 결과에서 영향력이 큰 순으로 한번 봅시다.
df_analysis_norm_selected = model_norm_selected.summary2().tables[1]
df_analysis_norm_selected = df_analysis_norm_selected.sort_values(by='Coef.', key=abs, ascending=False)
df_analysis_norm_selected
Coef. | Std.Err. | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
Intercept | 5138.940379 | 25.029860 | 205.312388 | 0.000000e+00 | 5089.865932 | 5188.014825 |
size_sqft | 2353.313208 | 48.304848 | 48.717951 | 0.000000e+00 | 2258.604981 | 2448.021434 |
bathrooms | 704.616356 | 44.604129 | 15.797110 | 2.274021e-54 | 617.163892 | 792.068820 |
bedrooms | -303.350320 | 41.134306 | -7.374631 | 2.043036e-13 | -383.999723 | -222.700917 |
building_age_yrs | -294.687852 | 28.458481 | -10.355010 | 8.952632e-25 | -350.484575 | -238.891128 |
floor | 252.334659 | 27.437763 | 9.196619 | 6.153221e-20 | 198.539192 | 306.130126 |
min_to_subway | -90.164340 | 25.623401 | -3.518828 | 4.389265e-04 | -140.402503 | -39.926177 |
no_fee | -59.060022 | 26.006486 | -2.270973 | 2.320859e-02 | -110.049276 | -8.070767 |
표준화만 한 것과(모든 feature가 들어간 경우), 표준화 후 Featue를 고른 것을 순위대로 한번 보면,
df_analysis_norm.drop(["Intercept"], axis=0, inplace=True)
df_analysis_norm_selected.drop(['Intercept'], axis=0, inplace=True)
print("========= 표준화 계수 ========= ")
print(df_analysis_norm['Coef.'])
print("========= 표준화, 유의하지 않은 feature 제거 후 회귀 계수 ========")
print(df_analysis_norm_selected['Coef.'])
========= 표준화 계수 ========= size_sqft 2349.924688 bathrooms 708.052169 bedrooms -305.227091 building_age_yrs -294.551239 floor 255.979928 min_to_subway -90.505300 no_fee -63.937945 Name: Coef., dtype: float64 ========= 표준화, 유의하지 않은 feature 제거 후 회귀 계수 ======== size_sqft 2353.313208 bathrooms 704.616356 bedrooms -303.350320 building_age_yrs -294.687852 floor 252.334659 min_to_subway -90.164340 no_fee -59.060022 Name: Coef., dtype: float64
어랏 순위가 똑같네요. 그런데 모든 Feature로 회귀를 했을 떄와 계수가 약간씩 차이가 나는군요.
실제로 size_sqt, bedrooms, building_age_yrs등이 contribution하는데에 대한 차이가 좀 많이 나는데 상관분석을 통해서 왜이런가 한번 보면 좋겠군요.
import matplotlib.pyplot as plt
df_data.plot(x='size_sqft', y='rent', kind="scatter") # 방크기~
plt.figure(figsize=(20,5))
plt.show()
df_data.plot(x='bedrooms', y='rent', kind="scatter") # 침실 개수~
plt.figure(figsize=(20,5))
plt.show()
df_data.plot(x='building_age_yrs', y='rent', kind="scatter") # 연식~
plt.figure(figsize=(20,5))
plt.show()
영향력 상위의 Feature일 수록 조금 더 이쁜 상관관계가 있어보이는 군요. 어느 정도 상식적이네요.
네, 월세가 어떤 것들에 의해 영향력을 받는지를 대~충 분석 해 보았는데 말이죠. 여기에서 조금더 나아가자면, 회귀를 통해서 Feature들의 영향력 분석을 했는데, 그러면, 예측도 해 보고 싶군요. 예측을 하고, 그 모델에 대한 성능을 보고 싶을 떄는 모든 관측치를 이용해서 하면 안되고, 회귀를 할 관측치 (train)와 모델 검증을 위한 (test) 관측치를 나눠서 해야 합니다.만 지금의 경우에는 어떤 식으로 하는 지만 좀 볼까 합니다.
이전에 딥러닝으로 자동차연비예측에 대하여 회귀를 실습 해 본적이 있는데, 그때의 기억을 더듬어 주세요. 더듬더듬
예측도 해 볼까? 가즈아~
그러면, tran과 test 세트를 일단 나눠보겠습니다. 후후 .
from sklearn.model_selection import train_test_split
col_learn = ["bathrooms", "bedrooms", "no_fee", "floor", "min_to_subway", "building_age_yrs", "size_sqft"]
df_data_ml = df_data.copy()
y_data = df_data_ml.pop('rent')
x_data = df_data_ml[col_learn]
x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size=0.2, random_state=777)
x_train.head()
bathrooms | bedrooms | no_fee | floor | min_to_subway | building_age_yrs | size_sqft | |
---|---|---|---|---|---|---|---|
1739 | 1 | 0.0 | 1 | 15.0 | 2 | 25 | 470 |
3312 | 2 | 2.0 | 1 | 5.0 | 9 | 86 | 1350 |
781 | 1 | 1.0 | 0 | 9.0 | 0 | 87 | 694 |
1216 | 1 | 0.5 | 1 | 11.0 | 4 | 85 | 430 |
577 | 1 | 1.0 | 0 | 30.0 | 2 | 86 | 800 |
유의하지 않은 feature를 제거하고 train과 test데이터를 나눴으니, train data를 표준화 해 보겠습니다.
from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
sc.fit(x_train[col_learn])
x_train[col_learn] = sc.transform(x_train[col_learn])
x_train.head()
bathrooms | bedrooms | no_fee | floor | min_to_subway | building_age_yrs | size_sqft | |
---|---|---|---|---|---|---|---|
1739 | -0.612125 | -1.385918 | 1.191494 | 0.278865 | -0.539443 | -0.673086 | -0.971599 |
3312 | 1.055754 | 0.678828 | 1.191494 | -0.644163 | 0.726712 | 0.880808 | 0.859369 |
781 | -0.612125 | -0.353545 | -0.839282 | -0.274952 | -0.901202 | 0.906282 | -0.505534 |
1216 | -0.612125 | -0.869731 | 1.191494 | -0.090347 | -0.177685 | 0.855334 | -1.054825 |
577 | -0.612125 | -0.353545 | -0.839282 | 1.663406 | -0.539443 | 0.880808 | -0.284986 |
자, 그럼 찾아낸 표준화 Parameter를 이용해서 train data도 표준화 해 보겠습니다. 후후.
x_test[col_learn] = sc.transform(x_test[col_learn])
x_test.head()
bathrooms | bedrooms | no_fee | floor | min_to_subway | building_age_yrs | size_sqft | |
---|---|---|---|---|---|---|---|
379 | -0.612125 | 0.678828 | -0.839282 | -0.736466 | 0.003195 | 1.390281 | -0.076921 |
663 | -0.612125 | -0.353545 | -0.839282 | 0.186562 | -0.358564 | 0.626071 | -0.284986 |
1327 | -0.612125 | -0.353545 | -0.839282 | 2.863342 | -0.539443 | -1.080665 | -0.351567 |
2202 | -0.612125 | -1.385918 | -0.839282 | -0.182649 | -0.358564 | -0.520244 | -0.950792 |
1225 | 2.723633 | 3.775946 | -0.839282 | -0.736466 | -0.539443 | 0.906282 | 1.795659 |
train을 이용해서 회귀모델을 한번 만들어보죠 후후. 기존의 ols 회귀 모델을 이용할 수도 있겠습니다.만 다른 간단한 방법도 있으니까, 그걸 이용해 보십시다.
from sklearn.linear_model import LinearRegression
model_ols = LinearRegression()
model_ols.fit(x_train, y_train)
LinearRegression()
참고로 아래와 같이 하면 기존의 모델을 이용할 수도 있습니다. 입맛에 맞는 방법을 쓰면 되겠습니다.
import numpy as np
# Predict for a list of observations, list length can be 1 to many..**
prediction = model_norm_selected.predict(np.array(x_test))
prediction.summary_frame(alpha=0.05)
자, 이제 대망의 예측 타임!
y_predict = model_ols.predict(x_test)
plt.scatter(y_test, y_predict, alpha=0.5)
plt.xlabel("Rent Observed")
plt.ylabel("Rent Predicted")
plt.show()
대~충 예측하는 것 같긴 한데, 이 예측이 괜찮은건지도 판별해야겠죠. 어떻게 하냐면 RMSE입니다.
$RMSE = \sqrt{\cfrac{1}{N}\sum{(y_i - \hat{y_i})^2}}$ 로 판별하는데요, 이거는 다음과 같이 자동 계산이 가능합니다.
(값이 적을수록 좋은 모델입니다)
import numpy as np
from sklearn.metrics import mean_squared_error
MSE = mean_squared_error(y_test, y_predict)
np.sqrt(MSE)
# sklearn 은 mse만 제공하기 때문에 rmse는 직접 만들어 써야한다.
1557.9905325236393
학습된 회귀 모델의 계수도 확인할 수 있습니다. 거의 비슷해요.
print(model_ols.coef_)
print(df_data_norm[col_learn].columns)
[ 631.08320961 -263.04572977 -70.12762055 245.39425302 -87.49216869 -321.30072268 2348.34592166] Index(['bathrooms', 'bedrooms', 'no_fee', 'floor', 'min_to_subway', 'building_age_yrs', 'size_sqft'], dtype='object')
이렇게 보면 유의하지 않은 feature들이 있으나 없으나~ 어떤지 모르겠으니까, 모든 feature를 떄려넣은 회귀도 한번 해 볼까요?
전체를 표준화해서 회귀를 해 보자
df_data_ml = df_data.copy()
y_data = df_data_ml.pop('rent')
x_data = df_data_ml
x_train, x_test, y_train, y_test = train_test_split(x_data, y_data, test_size=0.2, random_state=777)
x_train.head()
bedrooms | bathrooms | size_sqft | min_to_subway | floor | building_age_yrs | no_fee | has_roofdeck | has_washer_dryer | has_doorman | has_elevator | has_dishwasher | has_patio | has_gym | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1739 | 0.0 | 1 | 470 | 2 | 15.0 | 25 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
3312 | 2.0 | 2 | 1350 | 9 | 5.0 | 86 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
781 | 1.0 | 1 | 694 | 0 | 9.0 | 87 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
1216 | 0.5 | 1 | 430 | 4 | 11.0 | 85 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
577 | 1.0 | 1 | 800 | 2 | 30.0 | 86 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 |
cols = x_train.columns
sc = StandardScaler()
sc.fit(x_train)
x_train = pd.DataFrame(sc.transform(x_train), columns=cols)
x_train.head()
bedrooms | bathrooms | size_sqft | min_to_subway | floor | building_age_yrs | no_fee | has_roofdeck | has_washer_dryer | has_doorman | has_elevator | has_dishwasher | has_patio | has_gym | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | -1.385918 | -0.612125 | -0.971599 | -0.539443 | 0.278865 | -0.673086 | 1.191494 | -0.427247 | -0.438751 | -0.627064 | -0.646240 | -0.471557 | -0.242309 | -0.459763 |
1 | 0.678828 | 1.055754 | 0.859369 | 0.726712 | -0.644163 | 0.880808 | 1.191494 | -0.427247 | -0.438751 | -0.627064 | -0.646240 | -0.471557 | -0.242309 | -0.459763 |
2 | -0.353545 | -0.612125 | -0.505534 | -0.901202 | -0.274952 | 0.906282 | -0.839282 | 2.340568 | 2.279199 | 1.594735 | 1.547412 | 2.120634 | -0.242309 | -0.459763 |
3 | -0.869731 | -0.612125 | -1.054825 | -0.177685 | -0.090347 | 0.855334 | 1.191494 | -0.427247 | -0.438751 | -0.627064 | -0.646240 | -0.471557 | -0.242309 | -0.459763 |
4 | -0.353545 | -0.612125 | -0.284986 | -0.539443 | 1.663406 | 0.880808 | -0.839282 | -0.427247 | -0.438751 | 1.594735 | 1.547412 | -0.471557 | -0.242309 | 2.175033 |
x_test = pd.DataFrame(sc.transform(x_test), columns=cols)
x_test.head()
bedrooms | bathrooms | size_sqft | min_to_subway | floor | building_age_yrs | no_fee | has_roofdeck | has_washer_dryer | has_doorman | has_elevator | has_dishwasher | has_patio | has_gym | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.678828 | -0.612125 | -0.076921 | 0.003195 | -0.736466 | 1.390281 | -0.839282 | -0.427247 | -0.438751 | -0.627064 | -0.646240 | -0.471557 | -0.242309 | -0.459763 |
1 | -0.353545 | -0.612125 | -0.284986 | -0.358564 | 0.186562 | 0.626071 | -0.839282 | -0.427247 | -0.438751 | -0.627064 | -0.646240 | -0.471557 | -0.242309 | -0.459763 |
2 | -0.353545 | -0.612125 | -0.351567 | -0.539443 | 2.863342 | -1.080665 | -0.839282 | 2.340568 | -0.438751 | -0.627064 | 1.547412 | -0.471557 | -0.242309 | -0.459763 |
3 | -1.385918 | -0.612125 | -0.950792 | -0.358564 | -0.182649 | -0.520244 | -0.839282 | -0.427247 | -0.438751 | -0.627064 | 1.547412 | -0.471557 | -0.242309 | 2.175033 |
4 | 3.775946 | 2.723633 | 1.795659 | -0.539443 | -0.736466 | 0.906282 | -0.839282 | -0.427247 | -0.438751 | -0.627064 | -0.646240 | -0.471557 | -0.242309 | -0.459763 |
model_ols_full = LinearRegression()
model_ols_full.fit(x_train, y_train)
LinearRegression()
y_predict_full = model_ols_full.predict(x_test)
plt.scatter(y_test, y_predict_full, alpha=0.5)
plt.xlabel("Rent Observed")
plt.ylabel("Rent Predicted")
plt.show()
MSE = mean_squared_error(y_test, y_predict_full)
np.sqrt(MSE) #RMSE
1570.7231728568229
결과만 보면, 모든 feautre를 때려 박은 것은 MSE지표가 더 안좋네요.
그러니까, 월세에 영향을 미치는 유의한 feature들만 고려해도 어느정도 월세를 예측할 수 있다는 뭐 그런 시덥지 않은 이야기입니다.
이 정도 스토리라면 어느 정도 다중회귀를 할 때, 어떻게 feature를 선택해야 하는지 정도가 감이 올거라고 생각합니다.
물론 이 외에도 모델을 개선하기 위해서 하는 여러가지 방법론들이 있지만 그건 그대로 또 다른 실습에서 알아보는 것도 좋지 않을까 합니다.
유의성을 기반으로 feature를 선택한다던가, 회귀를 한다던가 하는 것들을 기억해 두면 좋겠습니다.
다중선형회귀(Multiple Linear Regression) - https://hleecaster.com/ml-multiple-linear-regression-example/ 이 분석의 출처입니다. 조금은 다른 시각으로 분석을 했습니다.
혹시 scaler에서 학습한 내용을 나중에 test 데이터에 적용하고 싶다면, import pickle with open('scaler.pkl', 'wb') as ofile : pickle.dump(sc, ofile) with open('scaler.pkl', 'rb') as ifile : sc = pickle.load(open(ifile,'rb')) 이런 식으로 나중에 test데이터를 위해서 다시 사용할 수도 있겠습니다.
댓글