출처:https://www.kaggle.com/code/packchanwoo/movie-recommender-systems-8370d4/edit
데이터 받기
md = pd. read_csv('../input/movies_metadata.csv')
md.head()
장르 전처리 하기
#장르 전처리하기
md['genres'] = md['genres'].fillna('[]').apply(literal_eval).apply(lambda x: [i['name'] for i in x] if isinstance(x, list) else [])
코드가 한줄로 되어있어서 이해하기 힘들었다.
난 개인적으로 여려줄이 편한데...
어쨋든 해석하자면 fillna([])부분은 결측치는 []로 대체해준다.
literal_eval은 문자열을 계산해준다.
무슨말이냐면 문자열로 되어있는 계산식이있다면 계산해주는 함수이다.
예를 들어 "1+2"라는 str형식이 있다고하자여기에 eval을 해준다면 3을 리턴해준다.
isinstance는 x값이 list인지 확인해서 맞다면 True 아니라면 false를 리턴해준다.
name이라는 컬럼을 리턴값으로 대채한다.
투표수 전처리하기
vote_counts = md[md['vote_count'].notnull()]['vote_count'].astype('int')
vote_averages = md[md['vote_average'].notnull()]['vote_average'].astype('int')
C = vote_averages.mean()
C
vote_counts라는 변수에 투표수가 결측치가 아닌경우를 int형으로 바꾸어서 넣는다.
투표수평균을 또한 같은 방식으로 전처리를 한다.이후 평균을 낸후 c라는 변수에 넣는다.
아래는 백분위중에 95%의 숫자를 출력한다
m = vote_counts.quantile(0.95)
m
md['year'] = pd.to_datetime(md['release_date'], errors='coerce').apply(lambda x: str(x).split('-')[0] if x != np.nan else np.nan)
연도를 추출하는 코드임
qualified = md[(md['vote_count'] >= m) & (md['vote_count'].notnull()) & (md['vote_average'].notnull())][['title', 'year', 'vote_count', 'vote_average', 'popularity', 'genres']]
qualified['vote_count'] = qualified['vote_count'].astype('int')
qualified['vote_average'] = qualified['vote_average'].astype('int')
qualified.shape
'title', 'year', 'vote_count', 'vote_average', 'popularity', 'genres'컬럼을 추출한 qualified 변수생성하기
def weighted_rating(x):
v = x['vote_count']
R = x['vote_average']
return (v/(v+m) * R) + (m/(m+v) * C)
Wilson Score Interval의 목적은 �과 � 사이에서 적절한 균형을 찾는 것입니다. 즉, �을 투표 수 �에 비례하여 증가시키고, �를 �에 비례하여 감소시킵니다. �가 증가함에 따라 �의 중요성이 더 커지고, �가 감소함에 따라 �의 중요성이 더 커집니다.
이 공식을 사용하면 샘플 크기가 작거나 항목의 평균 평가가 극단적일 때도 신뢰할 수 있는 평가를 얻을 수 있습니다. 이것은 실제 사용자 평가와 집단 평균 간의 균형을 유지하여 효과적인 평가 체계를 만들 수 있도록 돕습니다.
qualified['wr'] = qualified.apply(weighted_rating, axis=1)
qualified = qualified.sort_values('wr', ascending=False).head(250)
qualified.head(15)
이렇게 낸 통계중에서 15개만 출력하는 코드임
장르컬럼을 제일마지막컬럼으로 옮기기
s = md.apply(lambda x: pd.Series(x['genres']),axis=1).stack().reset_index(level=1, drop=True)
s.name = 'genre'
gen_md = md.drop('genres', axis=1).join(s)
장르를 넣었을때 상위 15개 영화를 추출하기
def build_chart(genre, percentile=0.85):
df = gen_md[gen_md['genre'] == genre]
vote_counts = df[df['vote_count'].notnull()]['vote_count'].astype('int')
vote_averages = df[df['vote_average'].notnull()]['vote_average'].astype('int')
C = vote_averages.mean()
m = vote_counts.quantile(percentile)
qualified = df[(df['vote_count'] >= m) & (df['vote_count'].notnull()) & (df['vote_average'].notnull())][['title', 'year', 'vote_count', 'vote_average', 'popularity']]
qualified['vote_count'] = qualified['vote_count'].astype('int')
qualified['vote_average'] = qualified['vote_average'].astype('int')
qualified['wr'] = qualified.apply(lambda x: (x['vote_count']/(x['vote_count']+m) * x['vote_average']) + (m/(m+x['vote_count']) * C), axis=1)
qualified = qualified.sort_values('wr', ascending=False).head(250)
return qualified
build_chart('Romance').head(15)
컬럼이 notnull인경우만 가져옴
links_small = pd.read_csv('../input/links_small.csv')
links_small = links_small[links_small['tmdbId'].notnull()]['tmdbId'].astype('int')
학습데이터에서 다루었던데이터만 가져옴
#Check EDA Notebook for how and why I got these indices.
md['id'] = md['id'].astype('int')
smd = md[md['id'].isin(links_small)]
smd.shape
tf-idf를 적용할수있는 문자열 데이터를 전처리
문자열데이터를 합쳐주고 빈데이터는 ""로 채워준다.
smd['tagline'] = smd['tagline'].fillna('')
smd['description'] = smd['overview'] + smd['tagline']
smd['description'] = smd['description'].fillna('')
tf-idf 적용하기
tf = TfidfVectorizer(analyzer='word',ngram_range=(1, 2),min_df=0, stop_words='english')
tfidf_matrix = tf.fit_transform(smd['description'])
tfidf_matrix.shape
영화 이름에 따라서 인덱스 부여하기
smd = smd.reset_index()
titles = smd['title']
indices = pd.Series(smd.index, index=smd['title'])
indices
이름에 따른 추천방식
def get_recommendations(title):
idx = indices[title]
sim_scores = list(enumerate(cosine_sim[idx]))
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
sim_scores = sim_scores[1:31]
movie_indices = [i[0] for i in sim_scores]
return titles.iloc[movie_indices]
get_recommendations('The Godfather').head(10)
이제 다른 데이터 받아온후 id값이 겹치는 경우만 가져오기
credits = pd.read_csv('../input/credits.csv')
keywords = pd.read_csv('../input/keywords.csv')
keywords['id'] = keywords['id'].astype('int')
credits['id'] = credits['id'].astype('int')
md['id'] = md['id'].astype('int')
merge를 통해서 id가 겹치는 경우 이어 붙혀줌
md = md.merge(credits, on='id')
md = md.merge(keywords, on='id')
links_small이라는 값이 들고있는지 하기
smd = md[md['id'].isin(links_small)]
smd.shape