working_helen
[데이터 전처리] EDA 및 변수 선택 본문
전처리 데이터를 바탕으로 EDA를 진행하고, 모델링에 포함할 변수를 논의한 과정을 정리해본다. 본 프로젝트에서는 변수의 개수가 많고, 상관관계와 VIF를 보았을 때 다중공선성 문제가 존재한다고 판단되어 변수 선택 + PCA 전처리 과정을 진행했다.
이상치 확인 => 변수 제거 없음
상관관계 확인 => 아래의 칼럼을 제거하기로 결정
['1년 생존율','5년 생존율','최근 30년 기준 평균영업기간', '주거인구','직장인구','반경500_대학개수','상권활성화지수등급','매출지수','인프라지수','가맹점지수','인구지수','금융지수']
import math
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings(action='ignore')
data_원본 = pd.read_csv(path + "/전처리/찐찐최종.csv", encoding = 'cp949')
data_원본.head(3)
Unnamed: 0.1 | Unnamed: 0 | 상호명 | 시군구명 | 행정동명 | 도로명주소 | 위도 | 경도 | 폐업여부 | 소득분위 | ... | 반경500_대학개수 | 반경1000_대학개수 | index | 상권활성화지수등급 | 상권활성화지수 | 매출지수 | 인프라지수 | 가맹점지수 | 인구지수 | 금융지수 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | (주)도피오커피서남병원인 | 양천구 | 신정3동 | 서울특별시 양천구 신정이펜1로 20(신정동, 서남병원) | 37.511895 | 126.833157 | y | 7 | ... | 0 | 0 | 24 | 9.26 | 1 | 3.00 | 6.45 | 0.36 | 12.50 | 2.51 |
1 | 1 | 1 | 커피나무 | 강남구 | 논현2동 | 서울특별시 강남구 언주로148길 14(논현동,2층) | 37.520407 | 127.036095 | y | 8 | ... | 0 | 0 | 415 | 26.56 | 10 | 10.82 | 14.35 | 2.04 | 17.63 | 8.70 |
2 | 2 | 2 | (주)프라빈 | 강남구 | 역삼1동 | 서울특별시 강남구 테헤란로22길 11(지상9층 역삼동) | 37.499232 | 127.035300 | y | 7 | ... | 0 | 0 | 423 | 31.32 | 10 | 14.21 | 17.25 | 4.66 | 21.81 | 8.82 |
3 rows × 35 columns
data_원본.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20886 entries, 0 to 20885
Data columns (total 35 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Unnamed: 0.1 20886 non-null int64
1 Unnamed: 0 20886 non-null int64
2 상호명 20886 non-null object
3 시군구명 20886 non-null object
4 행정동명 20886 non-null object
5 도로명주소 20886 non-null object
6 위도 20886 non-null float64
7 경도 20886 non-null float64
8 폐업여부 20886 non-null object
9 소득분위 20886 non-null int64
10 가구수 20886 non-null int64
11 1년 생존율 20886 non-null float64
12 3년 생존율 20886 non-null float64
13 5년 생존율 20886 non-null float64
14 최근 10년 기준 평균영업기간 20886 non-null float64
15 최근 30년 기준 평균영업기간 20886 non-null float64
16 임대시세 20886 non-null float64
17 유동인구 20886 non-null int64
18 주거인구 20886 non-null int64
19 직장인구 20886 non-null int64
20 방범지수 20886 non-null int64
21 반경500_카페개수 20886 non-null int64
22 반경500_지하철역개수 20886 non-null int64
23 반경500_정류장개수 20886 non-null int64
24 반경500_공공기관개수 20886 non-null int64
25 반경500_대학개수 20886 non-null int64
26 반경1000_대학개수 20886 non-null int64
27 index 20886 non-null int64
28 상권활성화지수등급 20886 non-null float64
29 상권활성화지수 20886 non-null int64
30 매출지수 20886 non-null float64
31 인프라지수 20886 non-null float64
32 가맹점지수 20886 non-null float64
33 인구지수 20886 non-null float64
34 금융지수 20886 non-null float64
dtypes: float64(14), int64(16), object(5)
memory usage: 5.6+ MB
data = data_원본.drop("index", axis=1).iloc[:, 8:]
data.head(3)
폐업여부 | 소득분위 | 가구수 | 1년 생존율 | 3년 생존율 | 5년 생존율 | 최근 10년 기준 평균영업기간 | 최근 30년 기준 평균영업기간 | 임대시세 | 유동인구 | ... | 반경500_공공기관개수 | 반경500_대학개수 | 반경1000_대학개수 | 상권활성화지수등급 | 상권활성화지수 | 매출지수 | 인프라지수 | 가맹점지수 | 인구지수 | 금융지수 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | y | 7 | 16957 | 75.0 | 75.0 | 25.0 | 2.8 | 3.3 | 167322.0 | 33003 | ... | 0 | 0 | 0 | 9.26 | 1 | 3.00 | 6.45 | 0.36 | 12.50 | 2.51 |
1 | y | 8 | 11676 | 72.3 | 29.8 | 21.3 | 2.6 | 3.9 | 166981.0 | 80595 | ... | 1 | 0 | 0 | 26.56 | 10 | 10.82 | 14.35 | 2.04 | 17.63 | 8.70 |
2 | y | 7 | 23983 | 67.6 | 42.6 | 33.3 | 2.7 | 4.0 | 182473.0 | 72621 | ... | 5 | 0 | 0 | 31.32 | 10 | 14.21 | 17.25 | 4.66 | 21.81 | 8.82 |
3 rows × 26 columns
1) 기본 데이터 파악¶
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20886 entries, 0 to 20885
Data columns (total 26 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 폐업여부 20886 non-null object
1 소득분위 20886 non-null int64
2 가구수 20886 non-null int64
3 1년 생존율 20886 non-null float64
4 3년 생존율 20886 non-null float64
5 5년 생존율 20886 non-null float64
6 최근 10년 기준 평균영업기간 20886 non-null float64
7 최근 30년 기준 평균영업기간 20886 non-null float64
8 임대시세 20886 non-null float64
9 유동인구 20886 non-null int64
10 주거인구 20886 non-null int64
11 직장인구 20886 non-null int64
12 방범지수 20886 non-null int64
13 반경500_카페개수 20886 non-null int64
14 반경500_지하철역개수 20886 non-null int64
15 반경500_정류장개수 20886 non-null int64
16 반경500_공공기관개수 20886 non-null int64
17 반경500_대학개수 20886 non-null int64
18 반경1000_대학개수 20886 non-null int64
19 상권활성화지수등급 20886 non-null float64
20 상권활성화지수 20886 non-null int64
21 매출지수 20886 non-null float64
22 인프라지수 20886 non-null float64
23 가맹점지수 20886 non-null float64
24 인구지수 20886 non-null float64
25 금융지수 20886 non-null float64
dtypes: float64(12), int64(13), object(1)
memory usage: 4.1+ MB
#결측치 확인
data.isnull().sum()
폐업여부 0
소득분위 0
가구수 0
1년 생존율 0
3년 생존율 0
5년 생존율 0
최근 10년 기준 평균영업기간 0
최근 30년 기준 평균영업기간 0
임대시세 0
유동인구 0
주거인구 0
직장인구 0
방범지수 0
반경500_카페개수 0
반경500_지하철역개수 0
반경500_정류장개수 0
반경500_공공기관개수 0
반경500_대학개수 0
반경1000_대학개수 0
index 0
상권활성화지수등급 0
상권활성화지수 0
매출지수 0
인프라지수 0
가맹점지수 0
인구지수 0
금융지수 0
dtype: int64
#수치형 데이터 통계량 확인
data.describe().iloc[:,:12]
소득분위 | 가구수 | 1년 생존율 | 3년 생존율 | 5년 생존율 | 최근 10년 기준 평균영업기간 | 최근 30년 기준 평균영업기간 | 임대시세 | 유동인구 | 주거인구 | 직장인구 | 방범지수 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 |
mean | 7.000958 | 11622.036149 | 73.257349 | 47.365020 | 34.413162 | 2.648396 | 3.774749 | 166402.330317 | 55893.587092 | 197.680073 | 150.283779 | 2.995451 |
std | 1.077375 | 4818.922891 | 14.809369 | 18.168337 | 17.072555 | 0.301518 | 0.651484 | 54971.964270 | 37125.116631 | 112.310803 | 199.226407 | 1.008097 |
min | 1.000000 | 1130.000000 | 0.000000 | 0.000000 | 0.000000 | 1.800000 | 1.900000 | 69104.000000 | 162.000000 | 14.000000 | 3.000000 | 1.000000 |
25% | 6.000000 | 8551.000000 | 66.700000 | 37.500000 | 25.000000 | 2.500000 | 3.400000 | 123515.000000 | 24733.000000 | 105.000000 | 30.000000 | 2.000000 |
50% | 7.000000 | 11580.000000 | 72.700000 | 45.900000 | 33.300000 | 2.600000 | 3.800000 | 147239.000000 | 48179.000000 | 187.000000 | 63.000000 | 3.000000 |
75% | 8.000000 | 14654.000000 | 80.000000 | 56.300000 | 42.900000 | 2.800000 | 4.100000 | 208466.000000 | 79282.000000 | 280.000000 | 174.000000 | 4.000000 |
max | 9.000000 | 26338.000000 | 100.000000 | 100.000000 | 100.000000 | 5.900000 | 7.300000 | 479959.000000 | 387942.000000 | 526.000000 | 1878.000000 | 5.000000 |
data.describe().iloc[:,12:]
반경500_카페개수 | 반경500_지하철역개수 | 반경500_정류장개수 | 반경500_공공기관개수 | 반경500_대학개수 | 반경1000_대학개수 | index | 상권활성화지수등급 | 상권활성화지수 | 매출지수 | 인프라지수 | 가맹점지수 | 인구지수 | 금융지수 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 | 20886.000000 |
mean | 74.198315 | 0.711098 | 25.498707 | 0.440056 | 0.130087 | 0.470650 | 278.817294 | 17.902995 | 7.032079 | 6.394257 | 12.423330 | 1.607876 | 14.735890 | 5.513466 |
std | 55.045863 | 0.649958 | 10.046827 | 1.134407 | 0.381996 | 0.788033 | 116.811514 | 5.600638 | 2.722894 | 3.799277 | 4.810399 | 1.286841 | 3.704978 | 1.902903 |
min | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 4.980000 | 1.000000 | 0.320000 | 1.370000 | 0.000000 | 1.520000 | 0.520000 |
25% | 36.000000 | 0.000000 | 18.000000 | 0.000000 | 0.000000 | 0.000000 | 191.000000 | 13.710000 | 5.000000 | 3.430000 | 9.140000 | 0.710000 | 12.530000 | 4.320000 |
50% | 59.000000 | 1.000000 | 25.000000 | 0.000000 | 0.000000 | 0.000000 | 301.000000 | 16.760000 | 8.000000 | 5.000000 | 11.750000 | 1.270000 | 14.740000 | 5.270000 |
75% | 98.000000 | 1.000000 | 31.000000 | 0.000000 | 0.000000 | 1.000000 | 383.000000 | 21.490000 | 10.000000 | 9.090000 | 14.710000 | 2.020000 | 17.180000 | 6.670000 |
max | 328.000000 | 4.000000 | 70.000000 | 11.000000 | 3.000000 | 5.000000 | 424.000000 | 35.580000 | 10.000000 | 19.490000 | 29.950000 | 5.950000 | 23.700000 | 13.870000 |
- '3년 생존율', '5년 생존율', '임대시세', '유동인구', '직장인구', '반경500_카페개수', '반경500_지하철역개수', '반경500_정류장개수', '반경500_공공기관개수' 열의 경우 Q3와 max 값 간 차이가 큰 것으로 보임 > 분포 치우침 + 이상치 확인
#범주형 데이터 통계량
data.describe(include=np.object)
폐업여부 | |
---|---|
count | 20886 |
unique | 2 |
top | n |
freq | 20709 |
2) 종속변수 파악¶
- 폐업 데이터와 영업 데이터의 개수 차이가 매우 큰 불균형 데이터임을 확인
data["폐업여부"].value_counts()
n 20709
y 177
Name: 폐업여부, dtype: int64
sns.countplot(y="폐업여부", data=data)
<AxesSubplot: xlabel='count', ylabel='폐업여부'>
3) 설명변수 EDA¶
변수 data type 파악
- 현재 설명 변수는 모두 수치형 변수이고 총 25개 존재
#종속변수만 범주형 변수
category_feature = [col for col in data.columns if data[col].dtype == object]
category_feature
['폐업여부']
#설명변수 모두 수치형 변수
numerical_feature = list(set(data.columns)-set(category_feature)-set(["위도", "경도"]))
numerical_feature = np.sort(numerical_feature)
numerical_feature
array(['1년 생존율', '3년 생존율', '5년 생존율', '가구수', '가맹점지수', '금융지수', '매출지수',
'반경1000_대학개수', '반경500_공공기관개수', '반경500_대학개수', '반경500_정류장개수',
'반경500_지하철역개수', '반경500_카페개수', '방범지수', '상권활성화지수', '상권활성화지수등급',
'소득분위', '유동인구', '인구지수', '인프라지수', '임대시세', '주거인구', '직장인구',
'최근 10년 기준 평균영업기간', '최근 30년 기준 평균영업기간'], dtype='<U16')
len(numerical_feature)
25
각 변수의 분포 파악
fig, axs = plt.subplots(figsize=(30, 30), ncols=5, nrows=5)
ax=axs.flatten()
fig.subplots_adjust(hspace=0.5)
i=0
for col in numerical_feature:
sns.distplot(data[col], ax=ax[i])
plt.title(col)
i=i+1
plt.show()
- sns.displot() 이용해 설명변수 확률 분포 파악
- '방범지수' '소득분위'의 경우 범주형으로 전환 가능
- 설명변수의 분포를 보았을 때 특이한 형태의 분포를 보이는 변수는 없었고, 대부분 한쪽으로 치우치거나 중심으로 모인 형태의 종모양 분포를 보임
fig, axs = plt.subplots(figsize=(30, 30), ncols=5, nrows=5)
ax=axs.flatten()
fig.subplots_adjust(hspace=0.5)
i=0
for col in numerical_feature:
sns.histplot(data=data[data["폐업여부"]=='y'], x=col, stat='density', alpha = 0.5, element='step', ax=ax[i])
sns.histplot(data=data[data["폐업여부"]=='n'], color='orange', x=col, stat='density', alpha = 0.3, element='step', ax=ax[i])
plt.title(col)
i=i+1
plt.show()
- sns.hisplot() 이용해 폐업여부 'y'와 'n' 에 따른 설명변수 분포의 차이 확인
- stat='density'를 통해 확률 분포로 비교했을때 폐업여부에 따른 설명 변수 분포에서 큰 차이는 없어 보임
fig, axs = plt.subplots(figsize=(30, 30), ncols=5, nrows=5)
ax=axs.flatten()
fig.subplots_adjust(hspace=0.5)
i=0
for col in numerical_feature:
sns.boxplot(data[col], ax=ax[i]).set_title(col)
#plt.title(col)
i=i+1
plt.show()
4) 모델링에 사용할 변수 선택¶
=> 팀원들과 상의하여 모델링에 사용할 변수 선택
이상치 확인 => 변수 제거 없음
상관관계 확인 => 제거하기로 결정한 칼럼['1년 생존율','5년 생존율','최근 30년 기준 평균영업기간', '주거인구','직장인구','반경500_대학개수','상권활성화지수등급','매출지수','인프라지수','가맹점지수','인구지수','금융지수']
변수별 이상치 확인
임대시세
, 유동인구
, 직장인구
, 반경500_카페개수
, 반경500_지하철역개수
, 반경500_정류장개수
, 반경500_공공기관개수
- max와 Q3간 차이가 컸던 위 열에 대해서 중점적으로 이상치 확인을 진행
- 이상치가 아닌 분포의 치우침으로 인한 것으로 판단되는 경우(생존율 관련 열들) 제외
- 대부분 이상치가 데이터의 오류라기보단 해당 행정동의 특성을 나타내는 경우 = 이상치 자체가 특정 행정동의 특성을 나타내는 경우가 많음
- 따라서 오히려 지역에 따른 폐업 예측 모델링에 적합한 데이터라고 판단했고, 이에 해당되는 경우 이상치로 제거하지 않고 그대로 사용했다.
임대시세
'잠실2동'의 임대시세가 다른 행정동에 비해 높게 나옴. 행정동별 특성을 드러내는 특징이라 판단하여 이상치로 분류하지 않음.
plt.figure(figsize=(4,3))
sns.boxplot(data["임대시세"])
data_원본[data_원본["임대시세"]>400000]["행정동명"].unique()
array(['잠실2동'], dtype=object)
유동인구
, 직장인구
반경500_카페개수
, 반경500_지하철역개수
, 반경500_정류장개수
, 반경500_공공기관개수
Q3 이상인 데이터에서 특정동만 포함되는 거나 특정 값으로만 존재하는 등의 특별한 패턴이 없었음.
데이터 그대로 정말 인근에 직장인구가 많은 경우, 카페가 많은 경우 등에 해당하는 것이라고 판단하여 이상치로 분류하지 않음.
plt.figure(figsize=(15, 3))
plt.subplots_adjust(wspace=0.4)
pop_col = ['유동인구', '직장인구']
i=1
for col in pop_col:
plt.subplot(1,4,i)
plt.title(col)
sns.boxplot(data[col])
i+=1
plt.show()
data_원본[data_원본["직장인구"]>174]["행정동명"].unique()
array(['논현2동', '역삼1동', '가산동', '서초4동', '을지로동', '종로5.6가동', '송파2동', '약수동',
'신정6동', '방배본동', '청담동', '명동', '아현동', '서초2동', '소공동', '서초3동', '대치4동',
'대치2동', '구로3동', '문래동', '가락본동', '서초1동', '반포4동', '논현1동', '문정1동',
'성내1동', '여의동', '광희동', '삼성1동', '동선동', '신대방2동', '대치1동', '회현동',
'성수2가3동', '삼성2동', '종로1.2.3.4가동', '남영동', '도곡1동', '사직동', '압구정동',
'이태원1동', '도곡2동', '자양1동', '잠실6동', '역삼2동', '방이2동', '방배4동', '방배1동',
'신정2동', '가회동', '잠실7동'], dtype=object)
plt.figure(figsize=(12, 3))
plt.subplots_adjust(wspace=0.2)
map_col = ['반경500_카페개수', '반경500_지하철역개수', '반경500_정류장개수', '반경500_공공기관개수']
i=1
for col in map_col:
plt.subplot(1,4,i)
plt.title(col)
sns.boxplot(data[col])
i+=1
plt.show()
data_원본[data_원본["반경500_카페개수"]>98]["행정동명"].unique()
array(['역삼1동', '서초4동', '을지로동', '종로5.6가동', '신촌동', '가산동', '명동', '수유3동',
'서교동', '서초2동', '잠원동', '신사동', '소공동', '성수1가2동', '연남동', '성수1가1동',
'대흥동', '망원1동', '당산1동', '연희동', '가양1동', '논현1동', '망원2동', '서초3동',
'대치4동', '문정2동', '서강동', '광희동', '화양동', '삼성1동', '혜화동', '동선동', '합정동',
'송파1동', '여의동', '구로3동', '종로1.2.3.4가동', '사직동', '성수2가3동', '청파동',
'낙성대동', '역삼2동', '자양3동', '청운효자동', '중앙동', '신림동', '한강로동', '삼성2동',
'이화동', '자양4동', '발산1동', '회현동', '안암동', '삼선동', '사당1동', '필동', '창신1동',
'용강동', '성수2가1동', '논현2동', '반포1동', '상암동', '가회동', '도화동', '번1동',
'서초1동', '충현동', '대치2동', '삼청동', '성산1동', '행운동', '서원동', '청룡동', '문정1동',
'염리동', '휘경1동', '가락본동', '아현동', '압구정동', '보문동', '도곡1동', '창신2동', '신당동',
'이문1동', '방이2동', '신수동', '신원동', '제기동', '공릉2동', '공릉1동', '회기동',
'원효로1동'], dtype=object)
변수 간 상관관계 확인
fig, ax = plt.subplots(1, 1, figsize=(25,20))
corrmat = data.iloc[:, 1:].corr().abs()
mask = np.triu(np.ones_like(corrmat, dtype=np.bool_))
sns.heatmap(corrmat, mask=mask, annot=True, cmap='RdYlBu_r', ax=ax, annot_kws={"size": 15},
cbar_kws={"shrink": .8}, vmin=0, vmax=1)
plt.title('Correlation map for Data', size=30)
plt.show()
- 1, 3, 5년 간 생존율의 상관관계가 높음 > 3가지 중 분포가 가장 skewed 되어있지 않은
3년 생존율
만 사용 - 인근 지역에 회사가 많이 들어서 있는지에 대한 정보를 포함하는 공공기관개수와 직장인구 분포가 유사하고 상관관계 높음 > 다른 열과도 상관관계가 높은
직장인구
를 제거하고반경500_공공기관개수
만 사용 - 주거인구와 유동인구 사이 높은 상관관계 > 카페 사용자 특성 상 단순히 주변 주거자뿐만 아니라 방문자가 사용자일 가능성이 높으므로 2가지를 모두 반영하는
유동인구
만 사용 - 인근 대학 개수가 500m인 경우와 1000m인 경우 차이가 크지 않으므로 하나만 사용 > 대학 크기 등을 고려해 범위가 더 넓은
반경1000_대학개수
만 사용 - 평균영업기간이 10년과 30년으로 2가지 존재 > 최근 데이터를 사용하기 위해
30년 기준 평균영업기간
제외하고10년 기준 평균영업기간
만 사용 - 상권 활성화와 관련된 지표로
상권활성화지수
,상권활성화지수등급
2가지가 존재 > 두 칼럼 상관관계가 높으므로 float type의상권활성화지수등급
열을 사용하되 칼럼명을상권활성화지수
로 변경해서 사용 - 상권 활성화 지표와 상관관계가 높은 '매출지수','인프라지수','가맹점지수','인구지수','금융지수' 열은 삭제
'TAVE > 뿌스팅 project' 카테고리의 다른 글
[데이터 전처리] 설명변수 PCA (0) | 2023.08.07 |
---|---|
[데이터 전처리] Encoding 인코딩 (0) | 2023.08.07 |
[데이터 수집] 데이터 수집 Workflow (0) | 2023.08.07 |
[데이터 수집] 위도/경도 기반 지도 반경 내 데이터 (0) | 2023.08.07 |
[모델링] 불균형 데이터 처리/SMOTH (0) | 2023.08.01 |