-
(DACON) 영화 관객수 예측 튜토리얼 대회IT 지식 창고 2019. 7. 7. 16:46
데이콘에서 주최하는 제 1회 튜토리얼 대회
https://dacon.io/tutorial_comp/87871
전체 44명 중에서 3등을 하였지만, 외부데이터 사용과정에서 Data leakage issue가 있어 실격처리 되어 수상하지 못하였습니다.
하지만, 외부데이터를 크롤링하는 과정을 공부하게 되었고 외부데이터를 사용하지 않아도 110만점에 가까운 Score를 얻을 수 있던 의미있는 코드라고 생각합니다.
영화 관객수 예측 정리 In [1]:#주피터 노트북 블로그게시용 함수 from IPython.core.display import display, HTML display(HTML("<style> .container{width:100% !important;}</style>"))
In [1]:# For example, here's several helpful packages to load in import numpy as np # linear algebra import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv) import matplotlib.pyplot as plt #data visualization import seaborn as sns import warnings warnings.filterwarnings("ignore") # Input data files are available in the "../영화관객수예측/" directory. # For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory import os print(os.listdir("../영화관객수예측/")) # Any results you write to the current directory are saved as output. import sys print ('Python version ->', sys.version) print ('Numpy version ->', np.__version__) print ('Pandas version ->', pd.__version__)
1. Data set 불러오기¶
In [2]:train_df = pd.read_csv('../영화관객수예측/movies_train.csv') # training dataframe test_df = pd.read_csv('../영화관객수예측/movies_test.csv')# testing dataframe
In [3]:#원본데이터는 보존하기 위함 train = train_df.copy() test = test_df.copy()
In [4]:print("train.csv. Shape: ",train.shape) print("test.csv. Shape: ",test.shape)
In [5]:train_null = train.drop('box_off_num', axis = 1).isnull().sum()/len(train)*100 test_null = test.isnull().sum()/len(test)*100 pd.DataFrame({'train_null_count' : train_null, 'test_null_count' : test_null})
Out[5]:missing data는 dir_prev_bfnum가 55%정도 있습니다. 그외에는 없습니다.
In [6]:train.info()
NAVER 검색 API사용¶
naver에서 제공하는 검색 API에서 평점이라는 데이터를 가지고 새로운 피쳐를 생성합니다.
train¶
In [7]:# import os # import sys # import urllib.request # import json # import time # import timeit # start = timeit.default_timer() # count = 0 # #API 사용 아이디 및 비밀번호 # client_id = "Fo_P8tuHi5_qUQUIGIWp" # client_secret = "nwKNomCW5F" # train_title = train.loc[:,'title'] # train_director = train.loc[:,'director'] # train['user_rating'] = 0 # for movie, name in zip(train_title, train_director): # if count == 30: # count = 0 # title = movie # director = name + '|' # encText = urllib.parse.quote(title) # display = '&display=100' # yearfrom = '&yearfrom=2010' # yearto = '&yearto=2015' # url = "https://openapi.naver.com/v1/search/movie?query=" + encText + display + yearfrom + yearto # json 결과 # # url = "https://openapi.naver.com/v1/search/blog.xml?query=" + encText # xml 결과 # request = urllib.request.Request(url) # request.add_header("X-Naver-Client-Id",client_id) # request.add_header("X-Naver-Client-Secret",client_secret) # response = urllib.request.urlopen(request) # rescode = response.getcode() # if(rescode==200): # response_body = response.read() # else: # print("Error Code:" + rescode) # break # result = json.loads(response_body) # for i in range(len(result['items'])): # if result['items'][i]['director'] == director: # train.loc[train['title']==title, 'user_rating'] = result['items'][i]['userRating'] # count += 1 # if count == 30: # time.sleep(1) # stop = timeit.default_timer() # print('불러오는데 걸린 시간 : {}초'.format(stop - start)) # print('rating이 0인 row 갯수 : {}개'.format(len(train[train['user_rating']==0])))
감독이나, 년도가 달라서 데이터가 0인 경우는 다시 조건을 넓혀 검색해봄
In [8]:# import os # import sys # import urllib.request # import json # start = timeit.default_timer() # count = 0 # client_id = "Fo_P8tuHi5_qUQUIGIWp" # client_secret = "nwKNomCW5F" # train_title = train.loc[train['user_rating']==0,'title'] # train_director = train.loc[train['user_rating']==0,'director'] # for movie, name in zip(train_title, train_director): # if count == 30: # count = 0 # title = movie # director = name + '|' # encText = urllib.parse.quote(title) # display = '&display=100' # url = "https://openapi.naver.com/v1/search/movie?query=" + encText # json 결과 # # url = "https://openapi.naver.com/v1/search/blog.xml?query=" + encText # xml 결과 # request = urllib.request.Request(url) # request.add_header("X-Naver-Client-Id",client_id) # request.add_header("X-Naver-Client-Secret",client_secret) # response = urllib.request.urlopen(request) # rescode = response.getcode() # if(rescode==200): # response_body = response.read() # else: # print("Error Code:" + rescode) # break # result = json.loads(response_body) # for i in range(len(result['items'])): # if result['items'][i]['director'] == director: # train.loc[train['title']==title, 'user_rating'] = result['items'][i]['userRating'] # count += 1 # if count == 30: # time.sleep(1) # stop = timeit.default_timer() # print('불러오는데 걸린 시간 : {}초'.format(stop - start)) # print('rating이 0인 row 갯수 : {}개'.format(len(train[train['user_rating']==0])))
test¶
In [9]:# start = timeit.default_timer() # count = 0 # client_id = "Fo_P8tuHi5_qUQUIGIWp" # client_secret = "nwKNomCW5F" # test_title = test.loc[:,'title'] # test_director = test.loc[:,'director'] # test['user_rating'] = 0 # for movie, name in zip(test_title, test_director): # if count == 30: # count = 0 # title = movie # director = name + '|' # encText = urllib.parse.quote(title) # display = '&display=100' # yearfrom = '&yearfrom=2010' # yearto = '&yearto=2015' # url = "https://openapi.naver.com/v1/search/movie?query=" + encText + display + yearfrom + yearto # json 결과 # # url = "https://openapi.naver.com/v1/search/blog.xml?query=" + encText # xml 결과 # request = urllib.request.Request(url) # request.add_header("X-Naver-Client-Id",client_id) # request.add_header("X-Naver-Client-Secret",client_secret) # response = urllib.request.urlopen(request) # rescode = response.getcode() # if(rescode==200): # response_body = response.read() # else: # print("Error Code:" + rescode) # break # result = json.loads(response_body) # for i in range(len(result['items'])): # if result['items'][i]['director'] == director: # test.loc[test['title']==title, 'user_rating'] = result['items'][i]['userRating'] # count += 1 # if count == 30: # time.sleep(1) # stop = timeit.default_timer() # print('불러오는데 걸린 시간 : {}초'.format(stop - start)) # print('rating이 0인 row 갯수 : {}개'.format(len(test[test['user_rating']==0])))
In [10]:# start = timeit.default_timer() # count = 0 # client_id = "Fo_P8tuHi5_qUQUIGIWp" # client_secret = "nwKNomCW5F" # test_title = test.loc[test['user_rating']==0,'title'] # test_director = test.loc[test['user_rating']==0,'director'] # for movie, name in zip(test_title, test_director): # if count == 30: # count = 0 # title = movie # director = name + '|' # encText = urllib.parse.quote(title) # display = '&display=100' # url = "https://openapi.naver.com/v1/search/movie?query=" + encText # json 결과 # # url = "https://openapi.naver.com/v1/search/blog.xml?query=" + encText # xml 결과 # request = urllib.request.Request(url) # request.add_header("X-Naver-Client-Id",client_id) # request.add_header("X-Naver-Client-Secret",client_secret) # response = urllib.request.urlopen(request) # rescode = response.getcode() # if(rescode==200): # response_body = response.read() # else: # print("Error Code:" + rescode) # result = json.loads(response_body) # for i in range(len(result['items'])): # if result['items'][i]['director'] == director: # test.loc[test['title']==title, 'user_rating'] = result['items'][i]['userRating'] # count += 1 # if count == 30: # time.sleep(1) # stop = timeit.default_timer() # print('불러오는데 걸린 시간 : {}초'.format(stop - start)) # print('rating이 0인 row 갯수 : {}개'.format(len(test[test['user_rating']==0])))
In [11]:# train[['title', 'user_rating']].to_csv('rate_train.csv', index=False) # test[['title', 'user_rating']].to_csv('rate_test.csv', index=False)
In [12]:#외부데이터(user_rating)이 포함된 csv파일 rate_train = pd.read_csv('../영화관객수예측/rate_train.csv') # user rating train rate_test = pd.read_csv('../영화관객수예측/rate_test.csv')# user rating test
In [13]:train = pd.merge(train, rate_train, on='title') test = pd.merge(test, rate_test, on='title')
2. Exploratory Data Analysis AND Processing¶
Column_name Description¶
- title : 영화의 제목
- distributor : 배급사
- genre : 장르
- release_time : 개봉일
- time : 상영시간(분)
- screening_rat : 상영등급
- director : 감독이름
- dir_prev_bfnum : 해당 감독이 이 영화를 만들기 전 제작에 참여한 영화에서의 평균 관객수(단 관객수가 알려지지 않은 영화 제외)
- dir_prev_num : 해당 감독이 이 영화를 만들기 전 제작에 참여한 영화의 개수(단 관객수가 알려지지 않은 영화 제외)
- num_staff : 스텝수
- num_actor : 주연배우수
- box_off_num : 관객수
- user_rating(외부데이터) : 네이버에서 제공하는 평점 데이터
데이터 탐색 과정에서 이상한 값들이 있어서.. 미리 삭제하고 시작하겠습니다.
In [14]:#슈퍼레이서 엔지의 time이 release_time데이터로 있어서 값을 수정하겠습니다. test.loc[test['title'] == '슈퍼레이서 엔지', 'time'] = 63 #시리즈물중에 중복되어있어서 값을 수정하는 것 보다 삭제를 하겠습니다. train.drop([10, 311], inplace=True)
2.1 Target Variable (Dependent Variable)¶
box_off_num : 관객수¶
In [15]:train['box_off_num'].describe()
Out[15]:- 총 600개의 데이터
- 평균 : 708181.8 약 70만
- 표준편차 : 1828006 약 180만
- min : 1
- max : 14262770 약 1400만
In [16]:f, ax = plt.subplots(figsize = (8,6)) sns.distplot(train['box_off_num']) print("%s -> Skewness: %f, Kurtosis: %f" % ('box_off_num',train['box_off_num'].skew(), train['box_off_num'].kurt()))
- 왜도(Skewness) : 왼쪽으로 치우쳐져 있을수록 값이크고, 오른쪽으로 치우쳐져 있을 수록 값이 작아진다. 즉, 0에 가까울수록 좋은 형태
- 첨도(Kurtosis) : 첨도 값이 3에 가까울 경우 정규분포에 가까우며, 첨도 값이 클수록 뾰족하고 값이 작을 수록 완만해진다
In [17]:train['box_off_num'] = np.log1p(train['box_off_num']) print("%s -> Skewness: %f, Kurtosis: %f" % ('box_off_num',train['box_off_num'].skew(), train['box_off_num'].kurt()))
In [18]:f, ax = plt.subplots(figsize = (8,6)) sns.distplot(train['box_off_num'])
Out[18]:2.2 Missing data¶
missing data가 있는 dir_prev_bfnum 부터 확인하겠습니다.
dir_prev_bfnum¶
감독이 해당 영화를 만들 기 전의 평균 관객수
In [19]:#Nan값을 제외한 데이터 히스토그램 분포 f, ax = plt.subplots(figsize = (8,6)) train_bfnum = train[train['dir_prev_bfnum'].notnull()] sns.distplot(train_bfnum['dir_prev_bfnum']) print("%s -> Skewness: %f, Kurtosis: %f" % ('dir_prev_bfnum',train_bfnum['dir_prev_bfnum'].skew(), train_bfnum['dir_prev_bfnum'].kurt()))
대부분이 작은 값을 가지고 있습니다.
dir_prev_bfnum은 감독이 해당영화 만들기전의 평균 관객수입니다.
하나의 영화를 만들었다면 과거데이터가 없어서 값이 없을 수도 있습니다.
따라서 1개의 영화를 만든 감독은 0의 데이터를 집어넣겠습니다.
In [20]:#train과 test를 합침 all_data = pd.concat([train, test], sort=False).reset_index(drop=True) print('Null count :',all_data['dir_prev_bfnum'].isnull().sum()) #dir_prev_bfnum에서 NaN값을 가지고 있는 데이터를 따로 만듬 bfnum_null = all_data[all_data['dir_prev_bfnum'].isnull()] #NaN 값중 감독별 영화갯수가 몇개인지 확인 director_count = bfnum_null[['title','director']].groupby('director').count() director_count = director_count.reset_index() print('Director Null count :',director_count['director'].count()) #영화가 1개인 경우는 이전의 영화가 없어서 NaN값일 수 도 있으므로 감독의 이름을 가져오기 위해 #또 새로운 데이터프레임을 만들고 해당하는 감독의 영화는 0으로 채워 넣는다. dire_1 = director_count[director_count['title']==1]['director'] for name in dire_1: all_data.loc[all_data['director']==name, 'dir_prev_bfnum'] = all_data.loc[all_data['director']==name, 'dir_prev_bfnum'].fillna(0)
나머지 null값에 대해서 확인해보겠습니다.
In [21]:print('Null count :',all_data['dir_prev_bfnum'].isnull().sum()) #위와 똑같은 과정 반복 #한 감독이 2개 이상 만든 경우의 NaN 데이터프레임 생성 bfnum_null = all_data[all_data['dir_prev_bfnum'].isnull()] director_count = bfnum_null[['title','director']].groupby('director').count() director_count = director_count.reset_index() print('Director Null count :',director_count['director'].count()) #관람객 수를 log취했기 때문에 원래대로 돌려 bfnum을 채우기 위한 용도로 사용. all_data['box_off_num'] = np.expm1(all_data['box_off_num'])
In [22]:#데이터를 순서대로 평균값을 계산하고 채워 넣을 수 있도록 사용자 정의 함수 만듬 def director_bfnum(all_director): #만약 첫번째 bfnum의 값이 있다면 그대로 두고, 없다면 다 0으로 채워 넣음 if pd.isnull(all_director.iloc[0, 7]): all_director.iloc[0, 7] = 0 count = 1 num = 0 #순차적으로 bfnum과 box_off_num을 더하여 갯수만큼 나눌 수 있는 평균을 구하도록함 for i in range(1, len(all_director)): num += all_director['box_off_num'].iloc[i-1] bfnum = num/count all_director.iloc[i, 7] = bfnum count += 1 return all_director
In [23]:#2개 이상인 데이터의 감독명을 받아옴 dire_2 = director_count['director'] #test데이터도 계산을 위해 nan값을 0의 값으로 가져온다. #train과 test로 나눌 때 삭제해야함 all_data['box_off_num'].fillna(0, inplace=True) for name in dire_2: all_director = all_data[all_data['director'] == name].sort_values('release_time') all_data[all_data['director']==name] = director_bfnum(all_director)
In [24]:#원래의 데이터로 다 돌려줌 ntrain = len(train) train = all_data[:ntrain] test = all_data[ntrain:] test.drop('box_off_num', axis=1, inplace=True) train['box_off_num'] = np.log1p(train['box_off_num'])
In [25]:train_null = train.drop('box_off_num', axis = 1).isnull().sum()/len(train)*100 test_null = test.isnull().sum()/len(test)*100 pd.DataFrame({'train_null_count' : train_null, 'test_null_count' : test_null})
Out[25]:말끔 ㅎㅎ..
겸사겸사 왜도, 첨도도 조사하여 조정해주겠습니다.
In [26]:f, ax = plt.subplots(figsize = (8,6)) sns.distplot(train['dir_prev_bfnum']) print("%s -> Skewness: %f, Kurtosis: %f" % ('dir_prev_bfnum',train_bfnum['dir_prev_bfnum'].skew(), train_bfnum['dir_prev_bfnum'].kurt()))
In [27]:train['dir_prev_bfnum'] = np.log1p(train['dir_prev_bfnum']) test['dir_prev_bfnum'] = np.log1p(test['dir_prev_bfnum']) print("%s -> Skewness: %f, Kurtosis: %f" % ('dir_prev_bfnum',train['dir_prev_bfnum'].skew(), train['dir_prev_bfnum'].kurt()))
In [28]:f, ax = plt.subplots(figsize = (8,6)) sns.distplot(train['dir_prev_bfnum'])
Out[28]:dir_prev_num¶
dir_prev_num이 0인데 dir_prev_bfnum이 값을 가진 경우가 있습니다.
이 경우에 감독이 영화를 하나만 만든경우는 1을 넣어주겠습니다.
In [29]:#train과 test를 합침 all_data = pd.concat([train, test], sort=False).reset_index(drop=True) #num이 0인데 값을 가진 경우를 새로운 데이터프레임으로 만듬 num_group = all_data[(all_data['dir_prev_num']==0) & (all_data['dir_prev_bfnum']>0)].groupby('director')[['dir_prev_num']].count().reset_index() print('num_zero count : ', num_group['dir_prev_num'].count()) #해당하는 감독의 이름을 가져오기 위한 데이터프레임을 새로 생성 dire_1 = num_group[num_group['dir_prev_num']==1]['director'] for name in dire_1: all_data.loc[all_data['director']==name, 'dir_prev_num'] = 1
그 외 데이터에 대해서 첫번째 num 값 이후로 순차적으로 데이터를 넣어주겠습니다.
In [30]:def director_num(prev_num): count = prev_num.iloc[0, 8] for i in range(1, len(prev_num)): prev_num.iloc[i, 8] = count count+=1 return prev_num director = all_data['director'] for name in director: prev_num = all_data[all_data['director'] == name].sort_values('release_time') all_data[all_data['director']==name] = director_num(prev_num)
In [31]:#원래의 데이터로 다 돌려줌 ntrain = len(train) train = all_data[:ntrain] test = all_data[ntrain:] test.drop('box_off_num', axis=1, inplace=True)
In [32]:f, ax = plt.subplots(figsize = (8,6)) sns.boxplot(train['dir_prev_num'], train['box_off_num'])
Out[32]:columns 상관관계¶
In [33]:#상관관계 확인 k=20 #히트맵 변수 갯수 corrmat = train.corr() #변수간의 상관관계 cols = corrmat.nlargest(k, 'box_off_num')['box_off_num'].index #price기준으로 제일 큰순서대로 20개를 뽑아냄 cm = np.corrcoef(train[cols].values.T) f, ax = plt.subplots(figsize=(8, 6)) sns.heatmap(data = cm, annot=True, square=True, fmt = '.2f', linewidths=.5, cmap='Reds', yticklabels = cols.values, xticklabels = cols.values)
Out[33]:2.3 Categorical Variable¶
distributor (배급사)¶
object인 데이터들 우선적으로 탐색하며 데이터를 변형하겠습니다.
In [34]:dis_unique = train['distributor'].unique() len(dis_unique)
Out[34]:In [35]:train_dist = train[['box_off_num','distributor']].groupby('distributor').mean().sort_values('box_off_num').reset_index() train_dist.head()
Out[35]:In [36]:dist_num = {} for i in range(len(train_dist)): distributor = train_dist['distributor'].iloc[i] dist_num[distributor] = i
In [37]:train_test_data = [train, test] for dataset in train_test_data: dataset['distributor'] = dataset['distributor'].map(dist_num)
test에는 train의 distributor가 없는 경우도 있기때문에
NaN값은 우선 0으로 채워넣겠습니다.
In [38]:test['distributor'].fillna(0, inplace = True)
screening_rat¶
In [39]:train['screening_rat'].unique()
Out[39]:In [40]:replace_name = {'청소년 관람불가' : 'No Youth', '15세 관람가' : '15 years old', '전체 관람가' : 'G rating', '12세 관람가' : '12 years old' } train.replace({'screening_rat' : replace_name}, inplace = True) test.replace({'screening_rat' : replace_name}, inplace = True) train.head()
Out[40]:In [41]:f, ax = plt.subplots(figsize = (8,6)) sns.boxplot(x='screening_rat', y='box_off_num', data=train[['screening_rat', 'box_off_num']])
Out[41]:데이터의 중앙값에 비해 편차들이 아주 큽니다. 각 관람가에 해당하는 관객수의 평균을 확인해보겠습니다.
In [42]:train[['box_off_num','screening_rat']].groupby('screening_rat').mean()
Out[42]:마지막에 라벨인코더로 처리하겠습니다.
라벨인코더에서가 가장 성능이 좋게 나왔습니다.
아마 test세트에서는 평균이 낮은 경우가 관람객수가 많고, 평균이 높은 경우 관람객수가 적은 듯함.
genre¶
In [43]:train['genre'].unique()
Out[43]:In [44]:replace_name = {'액션' : 'Action', '느와르' : 'noir', '코미디' : 'comedy', '다큐멘터리' : 'documentary', '뮤지컬' : 'musical', '드라마' : 'drama', '멜로/로맨스' : 'melo/romance', '공포' : 'horror', '서스펜스' : 'suspense', '애니메이션' : 'animation', '미스터리' : 'mistery'} train.replace({'genre' : replace_name}, inplace = True) test.replace({'genre' : replace_name}, inplace = True) train.head()
Out[44]:In [45]:train_genre = train[['box_off_num','genre']].groupby('genre').mean().sort_values('box_off_num').reset_index() train_genre
Out[45]:In [46]:genre_num = {} for i in range(len(train_genre)): genre = train_genre['genre'].iloc[i] genre_num[genre] = i genre_num
Out[46]:In [47]:train.replace({'genre' : genre_num}, inplace = True) test.replace({'genre' : genre_num}, inplace = True) train.head()
Out[47]:In [48]:f, ax = plt.subplots(figsize = (8,6)) sns.boxplot(x='genre', y='box_off_num', data=train[['genre', 'box_off_num']])
Out[48]:director¶
In [49]:len(train['director'].unique())
Out[49]:director는 그냥.. 삭제하는 부분이 제일 좋네요..
release_time¶
In [50]:train_test_data = [train, test] for dataset in train_test_data: #date -> 년, 월, 일 단위로 새로운 칼럼 만듦 dataset['release_time'] = dataset['release_time'].map(lambda x : x.replace('-', '')) dataset['year'] = dataset['release_time'].str[:4] dataset['year'] = dataset['year'].astype(int) dataset['month'] = dataset['release_time'].str[4:6] dataset['month'] = dataset['month'].astype(int) dataset['day'] = dataset['release_time'].str[6:8] dataset['day'] = dataset['day'].astype(int) dataset['release_time'] = dataset['release_time'].astype(int)
In [51]:f, ax = plt.subplots(figsize = (8,6)) sns.boxplot(x='year', y='box_off_num', data=train[['year', 'box_off_num']])
Out[51]:In [52]:f, ax = plt.subplots(figsize = (8,6)) sns.countplot(train['year'])
Out[52]:2.4 Numeric Variable¶
num_staff¶
In [53]:f, ax = plt.subplots(figsize = (8,6)) sns.distplot(train['num_staff'])
Out[53]:time¶
In [54]:f, ax = plt.subplots(figsize = (8,6)) sns.distplot(train['time'])
Out[54]:num_actor¶
In [55]:f, ax = plt.subplots(figsize = (8,6)) sns.distplot(train['num_actor'])
Out[55]:user_rating¶
In [56]:f, ax = plt.subplots(figsize = (8,6)) sns.distplot(train['user_rating'])
Out[56]:In [57]:train.head()
Out[57]:In [58]:f, ax = plt.subplots(figsize = (8,6)) sns.regplot(train['user_rating'], train['box_off_num'])
Out[58]:In [59]:train_rating = train[train['user_rating']==0].sort_values('box_off_num') train_rating.head()
Out[59]:In [60]:test_rating = test[test['user_rating']==0] test_rating.head()
Out[60]:user_rating이 제대로 적혀져 있지 않은 데이터에 한해서 네이버에서 웹크롤링으로 채워 넣겠습니다.
In [61]:from bs4 import BeautifulSoup import requests import urllib.request import json movie = train_rating['title'] director = train_rating['director'] for title, name in zip(movie, director): enctext = urllib.parse.quote(title) enctext2 = urllib.parse.quote(name) url = 'https://search.naver.com/search.naver?sm=top_hty&fbm=1&ie=utf8&query=' + enctext + enctext2 r = requests.get(url) html = r.text soup = BeautifulSoup(html, 'lxml') try: train.loc[train['title']==title, 'user_rating'] = float(soup.find('dl', {'class':'r_grade'} ).find('em').text) except AttributeError: pass
In [62]:movie = test_rating['title'] director = test_rating['director'] for title, name in zip(movie, director): enctext = urllib.parse.quote(title) enctext2 = urllib.parse.quote(name) url = 'https://search.naver.com/search.naver?sm=top_hty&fbm=1&ie=utf8&query=' + enctext + enctext2 r = requests.get(url) html = r.text soup = BeautifulSoup(html, 'lxml') try: test.loc[test['title']==title, 'user_rating'] = float(soup.find('dl', {'class':'r_grade'} ).find('em').text) except AttributeError: pass
2.5 Preprocessing¶
In [63]:from sklearn.preprocessing import LabelEncoder cols = ['screening_rat', 'director'] # process columns, apply LabelEncoder to categorical features ntrain = len(train) all_data = pd.concat([train, test], sort=False).reset_index(drop=True) target = train['box_off_num'] all_data.drop(['box_off_num', 'title'], axis=1, inplace=True) for c in cols: lbl = LabelEncoder() lbl.fit(list(all_data[c].values)) all_data[c] = lbl.transform(list(all_data[c].values)) # shape print('Shape all_data: {}'.format(all_data.shape))
In [64]:ntrain = len(train) train = all_data[:ntrain] test = all_data[ntrain:]
In [65]:#상관관계 확인 k=20 #히트맵 변수 갯수 train = pd.concat([train, target], axis=1) corrmat = train.corr() #변수간의 상관관계 cols = corrmat.nlargest(k, 'box_off_num')['box_off_num'].index #price기준으로 제일 큰순서대로 20개를 뽑아냄 cm = np.corrcoef(train[cols].values.T) f, ax = plt.subplots(figsize=(16, 10)) sns.heatmap(data = cm, annot=True, square=True, fmt = '.2f', linewidths=.5, cmap='Reds', yticklabels = cols.values, xticklabels = cols.values)
Out[65]:3. Feature Engineering¶
In [66]:train_test_data = [train, test]
In [67]:for dataset in train_test_data: #배우 1명당 스태프수 dataset['staff_per_actor'] = 0 dataset.loc[dataset['num_actor']>0, 'staff_per_actor'] = dataset['num_staff']/dataset['num_actor']
In [68]:train.head()
Out[68]:In [69]:train = train.drop(['director'], axis= 1) test = test.drop([ 'director'], axis= 1)
In [70]:train_columns = [] for column in train.columns[:]: if train[column].skew() >= 1: print("%s -> Skewness: %f, Kurtosis: %f" % (column,train[column].skew(), train[column].kurt())) train_columns.append(column) elif train[column].kurt() >= 3: print("%s -> Skewness: %f, Kurtosis: %f" % (column,train[column].skew(), train[column].kurt())) train_columns.append(column)
In [71]:#정규분포모형을 가질 수 있도록 첨도와 왜도를 조정 #조정하는 방법에는 square root, quarter root, log 등이 있다. #log에서 0의 값이 들어왔을 때 무한으로 가는 것을 방지하도록 1 더해주는 log1p를 사용 for column in train_columns : train[column] = np.log1p(train[column]) test[column] = np.log1p(test[column]) print("%s -> Skewness: %f, Kurtosis: %f" % (column,train[column].skew(), train[column].kurt()))
In [72]:#상관관계 확인 k=20 #히트맵 변수 갯수 corrmat = train.corr() #변수간의 상관관계 cols = corrmat.nlargest(k, 'box_off_num')['box_off_num'].index #price기준으로 제일 큰순서대로 20개를 뽑아냄 cm = np.corrcoef(train[cols].values.T) f, ax = plt.subplots(figsize=(16, 10)) sns.heatmap(data = cm, annot=True, square=True, fmt = '.2f', linewidths=.5, cmap='Reds', yticklabels = cols.values, xticklabels = cols.values)
Out[72]:4. Modeling¶
In [73]:train.head()
Out[73]:In [74]:from sklearn.linear_model import ElasticNet, Lasso from sklearn.ensemble import GradientBoostingRegressor, RandomForestRegressor from sklearn.kernel_ridge import KernelRidge from sklearn.pipeline import make_pipeline from sklearn.preprocessing import RobustScaler from sklearn.model_selection import KFold, cross_val_score, train_test_split from sklearn.metrics import mean_squared_error import xgboost as xgb import lightgbm as lgb
In [75]:target = train['box_off_num'] del train['box_off_num']
In [76]:#cross validation score n_folds = 5 def cv_score(models): kfold = KFold(n_splits=n_folds, shuffle=True ,random_state=42).get_n_splits(train.values) for m in models: cvs = np.mean(cross_val_score(m['model'], train.values, target, cv=kfold)) rmse = np.mean(np.sqrt(-cross_val_score(m['model'], train.values, np.expm1(target), scoring = "neg_mean_squared_error", cv = kfold))) print("Model {} CV score : {:.4f}".format(m['name'], cvs)) print("RMSE : {:.4f}".format(rmse))
In [77]:lasso = make_pipeline(RobustScaler(), Lasso(alpha = 0.0005, random_state=42)) ENet = make_pipeline(RobustScaler(), ElasticNet(alpha=0.0005, l1_ratio=.9, random_state=42)) forest = RandomForestRegressor(random_state=42) gboost = GradientBoostingRegressor(random_state=42) xgboost = xgb.XGBRegressor(random_state=42) lightgbm = lgb.LGBMRegressor(random_state=42) models = [{'model':gboost, 'name':'GradientBoosting'}, {'model':xgboost, 'name':'XGBoost'}, {'model':lightgbm, 'name':'LightGBM'}, {'model' : lasso, 'name' : 'LASSO Regression'}, {'model' : ENet, 'name' : 'Elastic Net Regression'}, {'model' : forest, 'name' : 'RandomForset'}]
In [78]:cv_score(models)
In [79]:#x.values 는 배열로 데이터를 뽑아옴 #여러개의 모델로 만들어진 predict 데이터들을 구한다. models = [{'model':xgboost, 'name':'XGBoost'}, {'model':lightgbm, 'name':'LightGBM'}] def AveragingBlending(models, x, y, sub_x): for m in models : m['model'].fit(x.values, y) predictions = np.column_stack([m['model'].predict(sub_x.values) for m in models]) return predictions
In [80]:y_test_pred = AveragingBlending(models, train, target, train) y_test_pred = (y_test_pred[:, 1]*0.9 + y_test_pred[:, 0]*0.1) print(np.sqrt(mean_squared_error(np.expm1(target), np.expm1(y_test_pred))))
In [81]:y_test_pred = AveragingBlending(models, train, target, test) y_test_pred = (y_test_pred[:, 1]*0.9 + y_test_pred[:, 0]*0.1) predictions = y_test_pred
In [82]:# lightgbm.fit(train.values, target) # predictions = lightgbm.predict(test.values)
In [83]:sub = pd.read_csv('../영화관객수예측/submission.csv')
In [84]:sub['box_off_num'] = np.expm1(predictions)
In [85]:sub.to_csv('movies_sub.csv', index=False)
'IT 지식 창고' 카테고리의 다른 글
주가등락예측 프로젝트(캡스톤 디자인) (0) 2019.07.07 (DACON) 아파트 실거래가 예측 튜토리얼 대회 (0) 2019.07.07 (Kaggle) 2019 2nd ML month KaKR - House Price (0) 2019.04.21 주가 예측 딥 러닝을 위한 자료들 (0) 2019.04.02 KNN(K Neighbor Nearest)이란? (0) 2019.03.25 댓글