본문 바로가기
Restful API

[Restful API] DB에 저장된 정보를 가져와서 작업한 후에 추천하는 API 만들기

by coding_su 2023. 1. 9.

📝DB에 저장된 정보를 가져와서 작업한 후에 추천하는 API

이런 API를 만들면 실시간이 아니므로 주기적(매일 밤이나 일주일 단위 등)으로 상관계수 파일을 업데이트 해야한다(batch 작업)

 

우선 추천하는 시스템 만들기 위해 DB에서 파일을 가져온다

내가 만들 시스템은 영화를 추천해주는 시스템이기 때문에 DB에서 영화정보와 리뷰정보 테이블을 가져왔다

※ 가져올 테이블의 우측 클릭 후 Table Data Export Wizard 선택 후 제이슨 파일로 저장

 

저장한 파일을 가지고 구글 코랩에서 작업해줬다 (참고 https://coding-jisu.tistory.com/173)

import numpy as np
import pandas as pd

# 데이터 불러오기
df_movie = pd.read_json('movie.json')
df_rating = pd.read_json('rating.json')

# 필요없는 컬럼 제거
df_movie = df_movie.iloc[ : , 0: 1+1]
df_rating = df_rating.iloc[ : , 1: ]

# 테이블 합치기위해 컬럼명 변경
df_movie.rename(columns= {'id' : 'movie_id'}, inplace= True)

# 테이블 합치기(왼쪽 데이터는 다 가져오게)
df = pd.merge(df_movie, df_rating, on= 'movie_id', how= 'left')

# 피봇테이블 생성
df = df.pivot_table(index= 'user_id', columns= 'title', values= 'rating')

# 상관계수 뽑기 (리뷰가 50개 이상만)
movie_correlations = df.corr(min_periods= 50)

# 내 별점 리스트도 DB에서 가져와서 불필요한 컬럼을 제거한다(필요한 컬럼만 가져온다)
my_rating = pd.read_json('my_rating.json')
my_rating = my_rating[['title', 'rating']]

# 내가 리뷰를 작성한 영화목록을 토대로 추천 시스템을 만든다
similar_movies_list = pd.DataFrame()

for i in range( my_rating.shape[0] ) :
  movie_title = my_rating['title'][i]
  similar_movie = movie_correlations[movie_title].dropna().sort_values(ascending= False).to_frame()
  similar_movie.columns = ['correlation']
  similar_movie['weight'] = similar_movie['correlation'] * my_rating['rating'][i]
  similar_movies_list = similar_movies_list.append( similar_movie )
 
# 내가 본 영화 제거
drop_index_list = my_rating['title'].to_list()
for name in drop_index_list :
  if name in similar_movies_list.index :
    similar_movies_list.drop(name, axis=0, inplace= True)
    
# 중복되는 영화가 있는지 확인하고 있으면 weight값이 높은 것만 남기고 제거
recomm_movie_list = similar_movies_list.groupby('title')['weight'].max().sort_values(ascending= False).head(10)

# 전체 상관계수를 구한 정보는 파일로 저장해서 사용한다
movie_correlations.to_csv('movie_correlations.csv')

 

구글 코랩에서 추천기능을 작업한 후 포스트맨에 테스트할 데이터 셋팅, 비주얼 스튜디오 코드로 API 코드 작업을 해준다

from flask import request
from flask_jwt_extended import get_jwt_identity, jwt_required
from flask_restful import Resource
import pandas as pd
from mysql_connection import get_connection
from mysql.connector import Error

class MovieRecommendResource(Resource) :

    @jwt_required()
    def get(self) :
        # 쿼리스트링으로 받는 데이터는 전부 문자열로 처리된다
        count = request.args.get('count')

        # 1. 클라이언트로부터 데이터를 받아온다
        user_id = get_jwt_identity()

        # 2. 추천을 위한 상관계수 데이터프레임을 읽어온다
        movie_correlations = pd.read_csv('data/movie_correlations.csv', index_col= 0)
        # print(movie_correlations) 데이터프레임을 제대로 읽어오는지 꼭 확인할 것

        # 3. 이 유저의 별점 정보를 가져온다(DB에서 가져온다)
        try :
            connection = get_connection()

            query = '''select m.title, r.rating
                    from rating r
                    join movie m 
                    on r.movie_id = m.id
                    where r.user_id = %s ;'''

            record = (user_id, )

            cursor = connection.cursor(dictionary= True)

            cursor.execute(query, record)

            result_list = cursor.fetchall()
            # print(result_list)

            cursor.close()
            connection.close()

        except Error as e :
            print(e)
            cursor.close()
            connection.close()

            return {"error" : str(e)}, 500

        # 4. DB로부터 가져온 내 별점 정보를 데이터프레임으로 만든다
        my_rating = pd.DataFrame(data= result_list)
        # print(my_rating)

        # 5. 내 별점 정보 기반으로 추천영화 목록을 만든다
        similar_movies_list = pd.DataFrame()

        for i in range( my_rating.shape[0] ) :
            movie_title = my_rating['title'][i]
            similar_movie = movie_correlations[movie_title].dropna().sort_values(ascending= False).to_frame()
            similar_movie.columns = ['correlation']
            similar_movie['weight'] = similar_movie['correlation'] * my_rating['rating'][i]
            similar_movies_list = similar_movies_list.append( similar_movie )
        # print(similar_movies_list)

        # 6. 중복 영화는 제거한다(내가 본 영화 제거)
        drop_index_list = my_rating['title'].to_list()

        for name in drop_index_list :
            if name in similar_movies_list.index :
                similar_movies_list.drop(name, axis=0, inplace= True)

        # 7. 중복 추천된 영화는 weight가 가장 큰 값으로만 남기고 중복 제거한다
        recomm_movie_list = similar_movies_list.groupby('title')['weight'].max().sort_values(ascending= False).head(int(count))
        # print(recomm_movie_list)

        # 8. 제이슨(json)으로 클라이언트에게 보낸다
        recomm_movie_list = recomm_movie_list.to_frame().reset_index()
        recomm_movie_list = recomm_movie_list.to_dict('records')
        # print(recomm_movie_list)

        return {"result" : "success", "items" : recomm_movie_list, "count" : len(recomm_movie_list)}, 200

※ 판다스 시리즈 데이터를 그대로 보낼 수 없으니

 시리즈 데이터를 다시 데이터프레임으로 만들고 인덱스를 리셋, 딕트(dict)로 만들어 전송한다

 > to_dict('records')

 

+ 추천 결과가 음수인 것도 나올 수 있으니 상관계수가 0보다 큰 것만 가져와 보낸다

댓글