🔨URL 설정하기
우선 프로젝트 폴더의 urls.py를 수정합니다.
현재 프로젝트 이름은 myproject입니다.
''로 경로가 없으면 beer 앱의 경로로 넘어갑니다.
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('beer.urls')),
]
이번엔 beer 앱의 urls.py를 수정합니다.
'' 경로로 접근 시 views.py의 index 함수를 실행합니다.
ver1, ver2 역시 마찬가지로 views.py의 ver1, ver2 함수를 실행합니다.
from django.urls import path
from . import views
urlpatterns = [
path('', views.index ,name = 'index'),
path('ver1', views.ver1, name = 'ver1'),
path('ver2', views.ver2, name = 'ver2'),
]
views.py에 index(), ver1(), ver2()함수를 작성하기 전에 우선 templates 폴더와 렌더링 할 html 파일을 생성했습니다.
📝templates 작성하기
1. index.html 작성
우선 홈 화면에 해당하는 index.html 파일을 작성하겠습니다.
홈 화면에 들어갈 이미지는 beer > static > img 폴더 안에 저장합니다.
index.html 코드입니다.
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap-theme.min.css" integrity="sha384-6pzBo3FDv/PJ8r2KRkGHifhEocL+1X2rVCTTkUfGk7/0pbek5mMa1upzvWbrUbOZ" crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
<meta charset="UTF-8">
<title>🍺Prost!</title>
<style>
body{
}
div{
text-align: center;
}
</style>
</head>
<body>
<div><img src= "{% static 'img/home.png' %}"</div>
<h1>🍺Prost!</h1>
<div id = "top">추천 유형을 선택해주세요.</div><br>
<div id = "recommendation">
<a href="ver1">1. 맥주간 유사도 기반 추천</a><br><br>
<a href="ver2">2. 내 평점이 반영된 맥주 추천</a>
</div>
</body>
</html>
index.html 파일을 작성했으니 views.py에서 html 파일을 렌더링해주는 함수를 작성하겠습니다.
from django.shortcuts import render
def index(request):
return render(request, 'beer/index.html')
def ver1(request):
return render(request, 'beer/ver1.html')
def ver2(request):
return render(request, 'beer/ver2.html')
python manage.py runserver로 실행합니다.
127.0.0.8000 에 접속하면 index.html로 연결되고 다음과 같은 페이지가 뜨게됩니다.
2. ver1.html 작성
ver1은 맥주간 유사도에 기반한 추천입니다.
따라서 input으로 받을 값은 맥주 종류와 맥주 선택시 중요시하는 취향입니다.
둘 다 Select box로 구현했으며 선택한 취향에 따라 가중치를 부여하여 계산했습니다.
선택할 취향의 종류는 향(Aroma), 맛(Flavor), 목넘김(Mouthfell)입니다.
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap-theme.min.css" integrity="sha384-6pzBo3FDv/PJ8r2KRkGHifhEocL+1X2rVCTTkUfGk7/0pbek5mMa1upzvWbrUbOZ" crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
<meta charset="UTF-8">
<title>🍺Ver1)Recommendation</title>
<style>
h1 { text-align: center; color: white;}
img {
display: block; margin: 0px auto;
width:40%;
}
select {
width: 200px;
padding: .5em .5em;
border: 1px solid #999;
font-family: inherit;
background: url('arrow.jpg') no-repeat 95% 50%;
border-radius: 0px;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
input[type=text] {
width : 190px;
height : 30px;
font-size : 20px;
}
body{
background-image:url("{% static 'img/맥주배경.jpg' %}");
background-size:100% 200%;
}
</style>
</head>
<body>
<div style="background-color:#0d47a1;">
<h1>좋아하는 맥주와 중요시 하는 취향을 선택해주세요.</h1>
</div>
<div id = 'contents' style="text-align:center;">
<img id="img1" src="static/img/Beer.jfif">
<div>
<form method="POST">
<br>
{% csrf_token %}
아이디 :
<input type="text" name="name"><br><br>
맥주 :
<select name="beer">
{% for tmp in beer_list %}
<option value='{{tmp}}'>{{tmp}}</option>
{% endfor %}
</select><br><br>
취향 :
<select name="detail">
<option value="Flavor">맛</option>
<option value="Aroma">향</option>
<option value="Mouthfeel">목넘김</option>
</select><br><br>
<input class="btn btn-success" type="submit" value="Pick!">
</form>
</div>
</div>
</body>
</html>
3. ver1_result.html 작성
ver1_result.html은 ver1.html에서 요청한 값에 대해 추천된 맥주들을 표시하는 페이지입니다.
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
<!-- <link rel= "stylesheet" type="text/css" href="{% static 'css/ver1_result.css' %}">-->
<meta charset="UTF-8">
<title>🍺Ver1)Recommendation</title>
<style>
img {
display: block; margin: 0px auto;
max-width:10%; max-height:10%;
}
h1 { text-align: center; color: white;}
h2 { text-align: center; }
h3 { text-align: center; }
</style>
</head>
<body>
<div style="background-color:#0d47a1;">
<h1>첫 번째 추천 맥주는 {{ result.0 }} 입니다.</h1>
</div>
<img src="static/img/{{ result.0 }}.jfif" alt="사진은 수집중입니다😢">
<h2>추천 맥주 유형 </h2>
<h3>{{ category.0 }}<h3>
<h2>어울리는 요리</h2>
<h3>{{ food.0 }}</h3>
<div id='myDiv'></div>
<script>
var data = JSON.parse("{{ targetJson|escapejs }}")
data = [
{
type: 'scatterpolar',
r: data['cluster3'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'Good',
marker : { color : 'salmon'}
},
{
type: 'scatterpolar',
r: data['cluster2'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'SoSo',
marker : { color : 'blue'}
},
{
type: 'scatterpolar',
r: data['cluster1'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'Sad',
marker : { color : 'skyblue'}
},
{
type: 'scatterpolar',
r: data['beer_cluster1'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: data['beer_name'][0],
marker : { color : 'green'}
},
]
layout = {
polar: {
radialaxis: {
visible: true,
range: [0, 1]
}
},
title: {
text:'추천 맥주 클러스터 유형',
font: {
size: 24
},
x : 0.47
}
}
Plotly.newPlot("myDiv", data, layout)
</script>
<div id='line1'></div>
<script>
var data = JSON.parse("{{ targetJson|escapejs }}")
var trace1 = {
x: data['tmp_year'][0],
y: data['tmp_ratings'][0],
type: 'scatter'
}
var result = [trace1];
layout = {
title: {
text:'추천 맥주의 연도별 평점 트렌드',
font: {
size: 24
},
x : 0.5
}
}
Plotly.newPlot('line1', result, layout);
</script>
<div style="background-color:#0d47a1;">
<h1>두 번째 추천 맥주는 {{ result.1 }} 입니다.</h1>
</div>
<img src="static/img/{{ result.1 }}.jfif" alt="사진은 수집중입니다😢">
<h2>추천 맥주 유형 </h2>
<h3>{{ category.1 }}<h3>
<h2>어울리는 요리</h2>
<h3>{{ food.1 }}</h3>
<div id='myDiv2'></div>
<script>
var data = JSON.parse("{{ targetJson|escapejs }}")
data = [
{
type: 'scatterpolar',
r: data['cluster3'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'Good',
marker : { color : 'salmon'}
},
{
type: 'scatterpolar',
r: data['cluster2'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'SoSo',
marker : { color : 'blue'}
},
{
type: 'scatterpolar',
r: data['cluster1'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'Sad',
marker : { color : 'skyblue'}
},
{
type: 'scatterpolar',
r: data['beer_cluster2'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: data['beer_name'][1],
marker : { color : 'green'}
}
]
layout = {
polar: {
radialaxis: {
visible: true,
range: [0, 1]
}
},
title: {
text:'추천 맥주 클러스터 유형',
font: {
size: 24
},
x : 0.47
}
}
Plotly.newPlot("myDiv2", data, layout)
</script>
<div id='line2'></div>
<script>
var data = JSON.parse("{{ targetJson|escapejs }}")
var trace1 = {
x: data['tmp_year'][1],
y: data['tmp_ratings'][1],
type: 'scatter'
}
var result = [trace1];
layout = {
title: {
text:'추천 맥주의 연도별 평점 트렌드',
font: {
size: 24
},
x : 0.5
}
}
Plotly.newPlot('line2', result, layout);
</script>
<div style="background-color:#0d47a1;">
<h1>세 번째 추천 맥주는 {{ result.2 }} 입니다.</h1>
</div>
<img src="static/img/{{ result.2 }}.jfif" alt="사진은 수집중입니다😢">
<h2>추천 맥주 유형 </h2>
<h3>{{ category.2 }}<h3>
<h2>어울리는 요리</h2>
<h3>{{ food.2 }}</h3>
<div id='myDiv3'></div>
<script>
var data = JSON.parse("{{ targetJson|escapejs }}")
data = [
{
type: 'scatterpolar',
r: data['cluster3'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'Good',
marker : { color : 'salmon'}
},
{
type: 'scatterpolar',
r: data['cluster1'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'Sad',
marker : { color : 'skyblue'}
},
{
type: 'scatterpolar',
r: data['cluster2'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'SoSo',
marker : { color : 'blue'}
},
{
type: 'scatterpolar',
r: data['beer_cluster3'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: data['beer_name'][2],
marker : { color : 'green'}
},
]
layout = {
polar: {
radialaxis: {
visible: true,
range: [0, 1]
}
},
title: {
text:'추천 맥주 클러스터 유형',
font: {
size: 24
},
x : 0.47
}
}
Plotly.newPlot("myDiv3", data, layout)
</script>
<div id='line3'></div>
<script>
var data = JSON.parse("{{ targetJson|escapejs }}")
var trace1 = {
x: data['tmp_year'][2],
y: data['tmp_ratings'][2],
type: 'scatter'
}
var result = [trace1];
layout = {
title: {
text:'추천 맥주의 연도별 평점 트렌드',
font: {
size: 24
},
x : 0.5
}
}
Plotly.newPlot('line3', result, layout);
</script>
</body>
</html>
4. ver2.html 작성
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap-theme.min.css" integrity="sha384-6pzBo3FDv/PJ8r2KRkGHifhEocL+1X2rVCTTkUfGk7/0pbek5mMa1upzvWbrUbOZ" crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js" integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous"></script>
<meta charset="UTF-8">
<title>🍺Ver2)Recommendation</title>
<style>
h1 { text-align: center; color: white;}
img {
display: block; margin: 0px auto;
width:40%;
}
select {
width: 200px;
padding: .8em .5em;
border: 1px solid #999;
font-family: inherit;
background: url('arrow.jpg') no-repeat 95% 50%;
border-radius: 0px;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
input[type=text] {
width : 190px;
height : 30px;
font-size : 20px;
}
#user {
}
body{
background-image:url("{% static 'img/맥주배경.jpg' %}");
background-size:100% 200%;
}
</style>
</head>
<body>
<div style="background-color:#0d47a1;">
<h1>좋아하는 맥주와 평점을 선택해주세요.</h1>
</div>
<div style="text-align:center;">
아이디 : <input type="text" name="name" text-align="center"><br><br>
</div>
<img src="static/img/Beer.jfif" alt="추천맥주">
<div id="user">
<div style="text-align:center; margin:10px;">
<form method="POST">
{% csrf_token %}
<div style="float:left; margin:5px;">
맥주 :
<select name="beer1">
{% for tmp in beer_list %}
<option value='{{tmp}}'>{{tmp}}</option>
{% endfor %}
</select><br>
평점 :
<select name="rating1">
<option value=1>1</option>
<option value=2>2</option>
<option value=3 selected>3</option>
<option value=4>4</option>
<option value=5>5</option>
</select><br><br>
</div>
<div style="float:left; margin:5px;">
맥주 :
<select name="beer2">
{% for tmp in beer_list %}
<option value='{{tmp}}'>{{tmp}}</option>
{% endfor %}
</select><br>
평점 :
<select name="rating2">
<option value=1>1</option>
<option value=2>2</option>
<option value=3 selected>3</option>
<option value=4>4</option>
<option value=5>5</option>
</select><br><br>
</div>
<div style="float:left; margin:5px;">
맥주 :
<select name="beer3">
{% for tmp in beer_list %}
<option value='{{tmp}}'>{{tmp}}</option>
{% endfor %}
</select><br>
평점 :
<select name="rating3">
<option value=1>1</option>
<option value=2>2</option>
<option value=3 selected>3</option>
<option value=4>4</option>
<option value=5>5</option>
</select><br><br>
</div>
<div style="float:left; margin:5px;">
맥주 :
<select name="beer4">
{% for tmp in beer_list %}
<option value='{{tmp}}'>{{tmp}}</option>
{% endfor %}
</select><br>
평점 :
<select name="rating4">
<option value=1>1</option>
<option value=2>2</option>
<option value=3 selected>3</option>
<option value=4>4</option>
<option value=5>5</option>
</select><br><br>
</div>
<div style="float:left; margin:5px;">
맥주 :
<select name="beer5">
{% for tmp in beer_list %}
<option value='{{tmp}}'>{{tmp}}</option>
{% endfor %}
</select><br>
평점 :
<select name="rating5">
<option value=1>1</option>
<option value=2>2</option>
<option value=3 selected>3</option>
<option value=4>4</option>
<option value=5>5</option>
</select>
</div>
<br><br><br>
<input class="btn btn-success" type="submit" value="Pick!">
</form>
</div>
</div>
</div>
</body>
</html>
5. ver2_result.html 작성
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
<link rel= "stylesheet" type="text/css" href="{% static 'css/ver2_result.css' %}">
<meta charset="UTF-8">
<title>🍺Ver2)Recommendation</title>
<style>
img {
display: block; margin: 0px auto;
max-width:10%; max-height:10%;
}
h1 { text-align: center; color: white;}
h2 { text-align: center;}
h3 { text-align: center;}
#header {
background-color:lightgrey;
height:100px;
}
</style>
</head>
<body>
<div style="background-color:#0d47a1;">
<h1>첫 번째 추천 맥주는 {{ result.0 }} 입니다.</h1>
</div>
<img src="/static/img/{{ result.0 }}.jfif" alt="사진은 수집중입니다😢">
<h2>추천 맥주 유형 </h2>
<h3>{{ category.0 }}<h3>
<h2>어울리는 요리</h2>
<h3>{{ food.0 }}</h3>
<div id='myDiv'></div>
<script>
var data = JSON.parse("{{ targetJson|escapejs }}")
data = [
{
type: 'scatterpolar',
r: data['cluster3'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'Good',
marker : { color : 'salmon'}
},
{
type: 'scatterpolar',
r: data['cluster2'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'SoSo',
marker : { color : 'blue'}
},
{
type: 'scatterpolar',
r: data['cluster1'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'Sad',
marker : { color : 'skyblue'}
},
{
type: 'scatterpolar',
r: data['beer_cluster1'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: data['beer_name'][0],
marker : { color : 'green'}
},
]
layout = {
polar: {
radialaxis: {
visible: true,
range: [0, 1]
}
},
title: {
text:'추천 맥주 클러스터 유형',
font: {
size: 24
},
x : 0.47
}
}
Plotly.newPlot("myDiv", data, layout)
</script>
<div id='line1'></div>
<script>
var data = JSON.parse("{{ targetJson|escapejs }}")
var trace1 = {
x: data['tmp_year'][0],
y: data['tmp_ratings'][0],
type: 'scatter'
}
var result = [trace1];
layout = {
title: {
text:'추천 맥주의 연도별 평점 트렌드',
font: {
size: 24
},
x : 0.5
}
}
Plotly.newPlot('line1', result, layout);
</script>
<div style="background-color:#0d47a1;">
<h1>두 번째 추천 맥주는 {{ result.1 }} 입니다.</h1>
</div>
<img src="/static/img/{{ result.1 }}.jfif" alt="사진은 수집중입니다😢">
<h2>추천 맥주 유형 </h2>
<h3>{{ category.1 }}<h3>
<h2>어울리는 요리</h2>
<h3>{{ food.1 }}</h3>
<div id='myDiv2'></div>
<script>
var data = JSON.parse("{{ targetJson|escapejs }}")
data = [
{
type: 'scatterpolar',
r: data['cluster3'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'Good',
marker : { color : 'salmon'}
},
{
type: 'scatterpolar',
r: data['cluster2'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'SoSo',
marker : { color : 'blue'}
},
{
type: 'scatterpolar',
r: data['cluster1'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'Sad',
marker : { color : 'skyblue'}
},
{
type: 'scatterpolar',
r: data['beer_cluster2'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: data['beer_name'][1],
marker : { color : 'green'}
}
]
layout = {
polar: {
radialaxis: {
visible: true,
range: [0, 1]
}
},
title: {
text:'추천 맥주 클러스터 유형',
font: {
size: 24
},
x : 0.47
}
}
Plotly.newPlot("myDiv2", data, layout)
</script>
<div id='line2'></div>
<script>
var data = JSON.parse("{{ targetJson|escapejs }}")
var trace1 = {
x: data['tmp_year'][1],
y: data['tmp_ratings'][1],
type: 'scatter'
}
var result = [trace1];
layout = {
title: {
text:'추천 맥주의 연도별 평점 트렌드',
font: {
size: 24
},
x : 0.5
}
}
Plotly.newPlot('line2', result, layout);
</script>
<div style="background-color:#0d47a1;">
<h1>세 번째 추천 맥주는 {{ result.2 }} 입니다.</h1>
</div>
<img src="/static/img/{{ result.2 }}.jfif" alt="사진은 수집중입니다😢">
<h2>추천 맥주 유형 </h2>
<h3>{{ category.2 }}<h3>
<h2>어울리는 요리</h2>
<h3>{{ food.2 }}</h3>
<div id='myDiv3'></div>
<script>
var data = JSON.parse("{{ targetJson|escapejs }}")
data = [
{
type: 'scatterpolar',
r: data['cluster3'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'Good',
marker : { color : 'salmon'}
},
{
type: 'scatterpolar',
r: data['cluster1'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'Sad',
marker : { color : 'skyblue'}
},
{
type: 'scatterpolar',
r: data['cluster2'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: 'SoSo',
marker : { color : 'blue'}
},
{
type: 'scatterpolar',
r: data['beer_cluster3'],
theta: ['Aroma', 'Appearance', 'Flavor','Mouthfeel', 'Overall'],
fill: 'toself',
name: data['beer_name'][2],
marker : { color : 'green'}
},
]
layout = {
polar: {
radialaxis: {
visible: true,
range: [0, 1]
}
},
title: {
text:'추천 맥주 클러스터 유형',
font: {
size: 24
},
x : 0.47
}
}
Plotly.newPlot("myDiv3", data, layout)
</script>
<div id='line3'></div>
<script>
var data = JSON.parse("{{ targetJson|escapejs }}")
var trace1 = {
x: data['tmp_year'][2],
y: data['tmp_ratings'][2],
type: 'scatter'
}
var result = [trace1];
layout = {
title: {
text:'추천 맥주의 연도별 평점 트렌드',
font: {
size: 24
},
x : 0.5
}
}
Plotly.newPlot('line3', result, layout);
</script>
</body>
</html>
📝views.py 작성하기
views.py에는 로직을 담당하는 코드들이 들어갑니다.
실질적인 추천시스템을 구현한 내용이 들어가므로 필요한 파일들을 포함시키겠습니다.
추가로 수집한 맥주 이미지들도 static > img 폴더안에 넣어두었습니다.
구현했던 함수들과 필요한 라이브러리도 추가했습니다.
from django.shortcuts import render
import pandas as pd
import numpy as np
import json
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.metrics import mean_squared_error
# 우리가 예측한 평점과 실제 평점간의 차이를 MSE로 계산
def get_mse(pred, actual):
# 평점이 있는 실제 영화만 추출
pred = pred[actual.nonzero()].flatten()
actual = actual[actual.nonzero()].flatten()
return mean_squared_error(pred, actual)
# 특정 맥주와 비슷한 유사도를 가지는 맥주 Top_N에 대해서만 적용 -> 시간오래걸림
def predict_rating_topsim(ratings_arr, item_sim_arr, n=20):
# 사용자-아이템 평점 행렬 크기만큼 0으로 채운 예측 행렬 초기화
pred = np.zeros(ratings_arr.shape)
# 사용자-아이템 평점 행렬의 맥주 개수만큼 루프
for col in range(ratings_arr.shape[1]):
# 유사도 행렬에서 유사도가 큰 순으로 n개의 데이터 행렬의 인덱스 반환
top_n_items = [np.argsort(item_sim_arr[:, col])[:-n - 1:-1]]
# 개인화된 예측 평점 계산 : 각 col 맥주별(1개), 2496 사용자들의 예측평점
for row in range(ratings_arr.shape[0]):
pred[row, col] = item_sim_arr[col, :][top_n_items].dot(
ratings_arr[row, :][top_n_items].T)
pred[row, col] /= np.sum(item_sim_arr[col, :][top_n_items])
return pred
# 사용자가 안 먹어본 맥주를 추천하자.
def get_not_tried_beer(ratings_matrix, userId):
# userId로 입력받은 사용자의 모든 맥주 정보를 추출해 Series로 반환
# 반환된 user_rating은 영화명(title)을 인덱스로 가지는 Series 객체
user_rating = ratings_matrix.loc[userId, :]
# user_rating이 0보다 크면 기존에 관란함 영화.
# 대상 인덱스를 추출해 list 객체로 만듦
tried = user_rating[user_rating > 0].index.tolist()
# 모든 맥주명을 list 객체로 만듦
beer_list = ratings_matrix.columns.tolist()
# list comprehension으로 tried에 해당하는 영화는 beer_list에서 제외
not_tried = [beer for beer in beer_list if beer not in tried]
return not_tried
# 예측 평점 DataFrame에서 사용자 id 인덱스와 not_tried로 들어온 맥주명 추출 후
# 가장 예측 평점이 높은 순으로 정렬
def recomm_beer_by_userid(pred_df, userId, not_tried, top_n):
recomm_beer = pred_df.loc[userId, not_tried].sort_values(ascending=False)[:top_n]
return recomm_beer
# 평점, Aroma, Flavor, Mouthfeel 중 피처 선택 후 유사도 계산
def recomm_feature(df, col):
feature = col
ratings = df[['아이디','맥주', feature]]
# 피벗 테이블을 이용해 유저-아이디 매트릭스 구성
ratings_matrix = ratings.pivot_table(feature, index='아이디', columns='맥주')
ratings_matrix.head(3)
# fillna함수를 이용해 Nan처리
ratings_matrix = ratings_matrix.fillna(0)
# 유사도 계산을 위해 트랜스포즈
ratings_matrix_T = ratings_matrix.transpose()
# 아이템-유저 매트릭스로부터 코사인 유사도 구하기
item_sim = cosine_similarity(ratings_matrix_T, ratings_matrix_T)
# cosine_similarity()로 반환된 넘파이 행렬에 영화명을 매핑해 DataFrame으로 변환
item_sim_df = pd.DataFrame(data=item_sim, index=ratings_matrix.columns,
columns=ratings_matrix.columns)
return item_sim_df
# 해당 맥주와 유사한 유사도 5개 추천
def recomm_beer(item_sim_df, beer_name):
# 해당 맥주와 유사도가 높은 맥주 5개만 추천
return item_sim_df[beer_name].sort_values(ascending=False)[1:4
1. ver1 함수 작성하기
ver1에서는 클러스터링 및 EDA과정에서 얻었던 결과값을 불러옵니다.
그리고 추천시스템에 해당하는 로직만 계산하도록 구현했습니다.
POST로부터 프론트에서 입력을 받아오고,
추천된 결과를 ver1_result.html로 return하도록 작성했습니다.
beer_list = pd.read_csv('맥주이름.csv', encoding='utf-8', index_col=0)
beer_year = pd.read_csv('맥주_연도별평점.csv', encoding='utf-8', index_col=0)
ratings = pd.read_csv('정제된데이터.csv', encoding='utf-8', index_col=0)
cluster_3 = pd.read_csv('대표군집클러스터링.csv', encoding='utf-8', index_col=0)
cluster_all = pd.read_csv('전체맥주클러스터링.csv', encoding='utf-8', index_col=0)
beer_data = pd.read_csv('맥주_cbf_data.csv', encoding='utf-8', index_col=0)
beer_list = beer_list['맥주']
cluster_3 = cluster_3.values
if request.method == 'POST':
beer_name = request.POST.get('beer', '')
detail = request.POST.get('detail', '')
df_aroma = recomm_feature(ratings, 'Aroma')
df_flavor = recomm_feature(ratings, 'Flavor')
df_mouthfeel = recomm_feature(ratings, 'Mouthfeel')
if detail=='Aroma':
df = df_aroma*0.8 + df_flavor*0.1 + df_mouthfeel*0.1
if detail=='Flavor':
df = df_aroma*0.1 + df_flavor*0.8 + df_mouthfeel*0.1
if detail=='Mouthfeel':
df = df_aroma*0.1 + df_flavor*0.1 + df_mouthfeel*0.8
result = recomm_beer(df, beer_name)
result = result.index.tolist()
# 클러스터링 결과
tmp_cluster=[]
category=[]
food=[]
for i in range(3):
target = cluster_all[cluster_all['맥주']==result[i]]
target = target[['Aroma', 'Appearance', 'Flavor', 'Mouthfeel', 'Overall']]
target = target.values[0]
tmp_cluster.append(target)
try :
category.append(beer_data[beer_data['맥주이름']==result[i]]['Main Category'].values[0])
food.append(beer_data[beer_data['맥주이름']==result[i]]['Paring Food'].values[0])
except :
category.append('수집되지 않았습니다.')
food.append('수집되지 않았습니다.')
# 연도별 평점 결과
tmp_year = []
tmp_ratings = []
for i in range(3):
target = beer_year[beer_year['맥주']==result[i]]
target_year = target['년'].tolist()
target_rating = target['평점'].tolist()
tmp_year.append(target_year)
tmp_ratings.append(target_rating)
# 넘겨줄 데이터 Json 변환
targetdict = {
'beer_name' : result,
'beer_cluster1' : tmp_cluster[0].tolist(),
'beer_cluster2' : tmp_cluster[1].tolist(),
'beer_cluster3' : tmp_cluster[2].tolist(),
'cluster1' : cluster_3[0].tolist(),
'cluster2' : cluster_3[1].tolist(),
'cluster3' : cluster_3[2].tolist(),
'tmp_year' : tmp_year,
'tmp_ratings' : tmp_ratings
}
targetJson = json.dumps(targetdict)
return render(request, 'beer/ver1_result.html',
{'result':result, 'beer_list':beer_list,'targetJson':targetJson,
'category':category, 'food':food})
else:
return render(request, 'beer/ver1.html', {'beer_list':beer_list})
2. ver2 함수 작성하기
def ver2(request):
beer_list = pd.read_csv('맥주이름.csv', encoding='utf-8', index_col=0)
beer_year = pd.read_csv('맥주_연도별평점.csv', encoding='utf-8', index_col=0)
ratings = pd.read_csv('정제된데이터.csv', encoding='utf-8', index_col=0)
cluster_3 = pd.read_csv('대표군집클러스터링.csv', encoding='utf-8', index_col=0)
cluster_all = pd.read_csv('전체맥주클러스터링.csv', encoding='utf-8', index_col=0)
beer_data = pd.read_csv('맥주_cbf_data.csv', encoding='utf-8', index_col=0)
beer_list = beer_list['맥주']
cluster_3 = cluster_3.values
if request.method == 'POST':
name = request.POST.get('name', '')
beer = []
rating = []
for i in range(1,6):
beer.append(request.POST.get('beer'+str(i), ''))
rating.append((request.POST.get('rating'+str(i), '')))
for i in range(len(beer)):
tmp = []
tmp.append(name)
tmp.append(beer[i])
tmp.append(float(rating[i]))
tmp = pd.DataFrame(data=[tmp], columns=['아이디','맥주','평점'])
ratings = pd.concat([ratings, tmp])
uname = name
ratings_matrix = ratings.pivot_table('평점', index='아이디', columns='맥주')
ratings_matrix = ratings_matrix.fillna(0)
ratings_matrix_T = ratings_matrix.transpose()
item_sim = cosine_similarity(ratings_matrix_T, ratings_matrix_T)
item_sim_df = pd.DataFrame(data=item_sim, index=ratings_matrix.columns,
columns=ratings_matrix.columns)
# top_n과 비슷한 유저들만 추천에 사용
ratings_pred = predict_rating_topsim(ratings_matrix.values, item_sim_df.values, n=5)
# 계산된 예측 평점 데이터는 DataFrame으로 재생성
ratings_pred_matrix = pd.DataFrame(data=ratings_pred, index=ratings_matrix.index,
columns=ratings_matrix.columns)
# 유저가 먹지 않은 맥주이름 추출
not_tried = get_not_tried_beer(ratings_matrix, uname)
# 아이템 기반의 최근접 이웃 CF로 맥주 추천
recomm_beer = recomm_beer_by_userid(ratings_pred_matrix, uname, not_tried, top_n=3)
recomm_beer = pd.DataFrame(data=recomm_beer.values, index=recomm_beer.index,
columns=['예측평점'])
# 추천 결과로 나온 맥주이름만 추출
result = recomm_beer.index.tolist()
# 클러스터링 결과
tmp_cluster = []
category = []
food = []
for i in range(3):
target = cluster_all[cluster_all['맥주'] == result[i]]
target = target[['Aroma', 'Appearance', 'Flavor', 'Mouthfeel', 'Overall']]
target = target.values[0]
tmp_cluster.append(target)
try :
category.append(beer_data[beer_data['맥주이름']==result[i]]['Main Category'].values[0])
food.append(beer_data[beer_data['맥주이름']==result[i]]['Paring Food'].values[0])
except :
category.append('수집되지 않았습니다.')
food.append('수집되지 않았습니다.')
tmp_year = []
tmp_ratings = []
for i in range(3):
target = beer_year[beer_year['맥주'] == result[i]]
target_year = target['년'].tolist()
target_rating = target['평점'].tolist()
tmp_year.append(target_year)
tmp_ratings.append(target_rating)
targetdict = {
'beer_name': result,
'beer_cluster1': tmp_cluster[0].tolist(),
'beer_cluster2': tmp_cluster[1].tolist(),
'beer_cluster3': tmp_cluster[2].tolist(),
'cluster1': cluster_3[0].tolist(),
'cluster2': cluster_3[1].tolist(),
'cluster3': cluster_3[2].tolist(),
'tmp_year': tmp_year,
'tmp_ratings': tmp_ratings
}
targetJson = json.dumps(targetdict)
return render(request, 'beer/ver2_result.html',
{'result': result, 'beer_list': beer_list,'targetJson': targetJson,
'category':category, 'food':food})
else:
return render(request, 'beer/ver2.html', {'beer_list': beer_list})
✨구현 결과
홈 화면
Ver1) 맥주간 유사도 기반 추천 화면
Ver1_result) 추천 결과 화면
Ver2) 개인의 평점이 반영된 맥주 추천 화면
Ver2_result) 추천 결과 화면
다음 포스팅에서는 웹을 Pythonanywhere에 배포해보려고 합니다.
'데이터사이언스 > 추천시스템' 카테고리의 다른 글
맥주 추천시스템 구현 - 3. 탐색적데이터분석 (0) | 2021.03.08 |
---|---|
맥주 추천시스템 구현 - 8. 웹 배포하기(Pythonanywhere) (3) | 2021.03.02 |
맥주 추천시스템 구현 - 6. 웹 설정하기(Django) (2) | 2021.02.28 |
맥주 추천시스템 구현 - 5. CF 기반 추천시스템 구현 (0) | 2021.02.27 |
맥주 추천시스템 구현 - 4. 클러스터링 (0) | 2021.02.26 |