dltmdgus commited on
Commit
baa1467
ยท
verified ยท
1 Parent(s): 16a5684

Update pages/3_๐Ÿ†•_์‹ ๊ทœ_๊ณต์—ฐ์žฅ_์ถ”์ฒœ_ํŽ˜์ด์ง€.py

Browse files
pages/3_๐Ÿ†•_์‹ ๊ทœ_๊ณต์—ฐ์žฅ_์ถ”์ฒœ_ํŽ˜์ด์ง€.py CHANGED
@@ -3,7 +3,11 @@ import pandas as pd
3
  import numpy as np
4
  import re
5
  import ast
6
- import os
 
 
 
 
7
 
8
  from utils.recommend_utils import recommend_venues
9
 
@@ -24,71 +28,102 @@ def create_perf_vector(title, cast, genre, price, search_score):
24
  genre_score = genre_score_map.get(genre, 0.5)
25
  price_value = extract_first_ticket_price(price)
26
  price_norm = price_value / 200000 if price_value else 0
27
-
28
  return [round(price_norm, 3), round(genre_score, 2), round(search_score, 3)]
29
 
30
- # ๐Ÿš€ ๋ฉ”์ธ Streamlit ์•ฑ
31
- def render():
32
- st.title("๐Ÿ†• ์‹ ๊ทœ ๋‚ดํ•œ ๊ณต์—ฐ ์ •๋ณด ์ž…๋ ฅ โ†’ ๊ณต์—ฐ์žฅ ์ถ”์ฒœ")
33
-
34
- st.subheader("1๏ธโƒฃ ๊ณต์—ฐ ์ •๋ณด ์ž…๋ ฅ")
35
- title = st.text_input("๊ณต์—ฐ ์ œ๋ชฉ")
36
- cast = st.text_input("์ถœ์—ฐ์ง„ (์ฒซ ๋ช…๋งŒ ์ž…๋ ฅํ•ด๋„ ๋จ)")
37
- genre = st.selectbox("์žฅ๋ฅด ์„ ํƒ", list(genre_score_map.keys()))
38
- price = st.text_input("๋Œ€ํ‘œ ํ‹ฐ์ผ“๊ฐ€๊ฒฉ (์˜ˆ: 99,000์› ๋˜๋Š” ์ˆซ์ž๋งŒ ์ž…๋ ฅ)")
39
-
40
- st.subheader("2๏ธโƒฃ ๊ฒ€์ƒ‰๋Ÿ‰ ์ˆ˜์ค€ ์„ ํƒ")
41
- search_category = st.selectbox("๐Ÿ” ๋‰ด์Šค ๊ฒ€์ƒ‰๋Ÿ‰ ๋“ฑ๊ธ‰", ["๋‚ฎ์Œ", "๋ณดํ†ต", "๋†’์Œ", "๋งค์šฐ ๋†’์Œ"])
42
- search_score_map = {
43
- "๋‚ฎ์Œ": 0.2,
44
- "๋ณดํ†ต": 0.5,
45
- "๋†’์Œ": 0.8,
46
- "๋งค์šฐ ๋†’์Œ": 1.0
47
- }
48
- search_score = search_score_map[search_category]
49
-
50
- st.subheader("3๏ธโƒฃ ์œ ์‚ฌ๋„ ๊ฐ€์ค‘์น˜ ์„ค์ •")
51
- w1 = st.slider("ํ‹ฐ์ผ“๊ฐ€ ๊ฐ€์ค‘์น˜", 0.0, 1.0, 0.5)
52
- w2 = st.slider("์žฅ๋ฅด ๊ฐ€์ค‘์น˜", 0.0, 1.0, 0.3)
53
- w3 = st.slider("๊ฒ€์ƒ‰๋Ÿ‰ ๊ฐ€์ค‘์น˜", 0.0, 1.0, 0.2)
54
- alpha = st.slider("๐ŸŽฏ ์ข…ํ•ฉ์œ ์‚ฌ๋„์—์„œ ๋ฒกํ„ฐ ์œ ์‚ฌ๋„ ๋น„์ค‘ (ฮฑ)", 0.0, 1.0, 0.7)
55
-
56
- if st.button("๐Ÿš€ ๋ฒกํ„ฐ ์ƒ์„ฑ ๋ฐ ์ถ”์ฒœ ์‹คํ–‰"):
57
- if not title or not genre or not price:
58
- st.error("๊ณต์—ฐ ์ œ๋ชฉ, ์žฅ๋ฅด, ๊ฐ€๊ฒฉ์€ ํ•„์ˆ˜ ์ž…๋ ฅ์ž…๋‹ˆ๋‹ค.")
59
- return
60
-
61
- perf_vector = create_perf_vector(title, cast, genre, price, search_score)
62
- st.success(f"๐ŸŽฏ ์ƒ์„ฑ๋œ ๊ณต์—ฐ ๋ฒกํ„ฐ: {perf_vector}")
63
-
64
- # ๋ฐ์ดํ„ฐ ๋กœ๋“œ
65
- try:
66
- df = pd.read_excel("data/์ตœ์ข….xlsx")
67
- venue_df = pd.read_excel("data/๊ณต์—ฐ์‹œ์„คDB.xlsx")
68
- except Exception as e:
69
- st.error(f"โŒ ๋ฐ์ดํ„ฐ ํŒŒ์ผ ๋กœ๋“œ ์˜ค๋ฅ˜: {e}")
70
- return
71
-
72
- df = df[df["๊ณต์—ฐ์žฅ๋ฒกํ„ฐ"].notna()].copy()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  try:
74
- df["๊ณต์—ฐ์žฅ๋ฒกํ„ฐ"] = df["๊ณต์—ฐ์žฅ๋ฒกํ„ฐ"].apply(ast.literal_eval)
75
- except Exception as e:
76
- st.error(f"โŒ ๊ณต์—ฐ์žฅ ๋ฒกํ„ฐ ํŒŒ์‹ฑ ์˜ค๋ฅ˜: {e}")
77
- return
78
-
79
- df = df.merge(venue_df, on="๊ณต์—ฐ์‹œ์„คID", how="left")
80
-
81
- # ์ถ”์ฒœ ์ˆ˜ํ–‰
82
- results = recommend_venues(perf_vector, df, weights=[w1, w2, w3], alpha=alpha)
83
-
84
- # ๊ฒฐ๊ณผ ์ถœ๋ ฅ
85
- st.subheader("โœ… ์ถ”์ฒœ ๊ณต์—ฐ์žฅ ๋ฆฌ์ŠคํŠธ (์œ ์‚ฌ๋„ ๊ธฐ๋ฐ˜ ์ƒ์œ„)")
86
- st.dataframe(results[[
87
- "๊ณต์—ฐ์‹œ์„ค๋ช…", "๊ณต์—ฐ์‹œ์„คID", "์œ ์‚ฌ๋„", "๊ฐ์„์ˆ˜์œ ์‚ฌ๋„", "์ข…ํ•ฉ์œ ์‚ฌ๋„", "๊ฐ์„ ์ˆ˜",
88
- "๋ ˆ์Šคํ† ๋ž‘", "์นดํŽ˜", "ํŽธ์˜์ ",
89
- "์žฅ์• ์‹œ์„ค-์ฃผ์ฐจ์žฅ", "์žฅ์• ์‹œ์„ค-ํ™”์žฅ์‹ค", "์žฅ์• ์‹œ์„ค-๊ฒฝ์‚ฌ๋กœ", "์žฅ์• ์‹œ์„ค-์—˜๋ฆฌ๋ฒ ์ดํ„ฐ"
90
- ]].head(10))
91
-
92
- # ๐ŸŸข ์ง์ ‘ ์‹คํ–‰ ์‹œ
93
- if __name__ == "__main__":
94
- render()
 
 
3
  import numpy as np
4
  import re
5
  import ast
6
+
7
+ import folium
8
+ from streamlit_folium import st_folium
9
+ from geopy.geocoders import Nominatim
10
+ from geopy.extra.rate_limiter import RateLimiter
11
 
12
  from utils.recommend_utils import recommend_venues
13
 
 
28
  genre_score = genre_score_map.get(genre, 0.5)
29
  price_value = extract_first_ticket_price(price)
30
  price_norm = price_value / 200000 if price_value else 0
 
31
  return [round(price_norm, 3), round(genre_score, 2), round(search_score, 3)]
32
 
33
+ # ๐ŸŸข ๋ฉ”์ธ ์•ฑ
34
+ st.set_page_config(layout="wide")
35
+ st.title("๐Ÿ†• ์‹ ๊ทœ ๋‚ดํ•œ ๊ณต์—ฐ ์ •๋ณด ์ž…๋ ฅ โ†’ ๊ณต์—ฐ์žฅ ์ถ”์ฒœ")
36
+
37
+ st.subheader("1๏ธโƒฃ ๊ณต์—ฐ ์ •๋ณด ์ž…๋ ฅ")
38
+ title = st.text_input("๊ณต์—ฐ ์ œ๋ชฉ")
39
+ cast = st.text_input("์ถœ์—ฐ์ง„ (์ฒซ ๋ช…๋งŒ ์ž…๋ ฅํ•ด๋„ ๋จ)")
40
+ genre = st.selectbox("์žฅ๋ฅด ์„ ํƒ", list(genre_score_map.keys()))
41
+ price = st.text_input("๋Œ€ํ‘œ ํ‹ฐ์ผ“๊ฐ€๊ฒฉ (์˜ˆ: 99,000์› ๋˜๋Š” ์ˆซ์ž๋งŒ ์ž…๋ ฅ)")
42
+
43
+ st.subheader("2๏ธโƒฃ ๊ฒ€์ƒ‰๋Ÿ‰ ์ˆ˜์ค€ ์„ ํƒ")
44
+ search_category = st.selectbox("๐Ÿ” ๋‰ด์Šค ๊ฒ€์ƒ‰๋Ÿ‰ ๋“ฑ๊ธ‰", ["๋‚ฎ์Œ", "๋ณดํ†ต", "๋†’์Œ", "๋งค์šฐ ๋†’์Œ"])
45
+ search_score_map = {
46
+ "๋‚ฎ์Œ": 0.2,
47
+ "๋ณดํ†ต": 0.5,
48
+ "๋†’์Œ": 0.8,
49
+ "๋งค์šฐ ๋†’์Œ": 1.0
50
+ }
51
+ search_score = search_score_map[search_category]
52
+
53
+ st.subheader("3๏ธโƒฃ ์œ ์‚ฌ๋„ ๊ฐ€์ค‘์น˜ ์„ค์ •")
54
+ w1 = st.slider("ํ‹ฐ์ผ“๊ฐ€ ๊ฐ€์ค‘์น˜", 0.0, 1.0, 0.5)
55
+ w2 = st.slider("์žฅ๋ฅด ๊ฐ€์ค‘์น˜", 0.0, 1.0, 0.3)
56
+ w3 = st.slider("๊ฒ€์ƒ‰๋Ÿ‰ ๊ฐ€์ค‘์น˜", 0.0, 1.0, 0.2)
57
+ alpha = st.slider("๐ŸŽฏ ์ข…ํ•ฉ์œ ์‚ฌ๋„์—์„œ ๋ฒกํ„ฐ ์œ ์‚ฌ๋„ ๋น„์ค‘ (ฮฑ)", 0.0, 1.0, 0.7)
58
+
59
+ # ๐Ÿš€ ์‹คํ–‰
60
+ if st.button("๐Ÿš€ ๋ฒกํ„ฐ ์ƒ์„ฑ ๋ฐ ์ถ”์ฒœ ์‹คํ–‰"):
61
+ if not title or not genre or not price:
62
+ st.error("๊ณต์—ฐ ์ œ๋ชฉ, ์žฅ๋ฅด, ๊ฐ€๊ฒฉ์€ ํ•„์ˆ˜ ์ž…๋ ฅ์ž…๋‹ˆ๋‹ค.")
63
+ st.stop()
64
+
65
+ perf_vector = create_perf_vector(title, cast, genre, price, search_score)
66
+ st.success(f"๐ŸŽฏ ์ƒ์„ฑ๋œ ๊ณต์—ฐ ๋ฒกํ„ฐ: {perf_vector}")
67
+
68
+ # โœ… ๋ฐ์ดํ„ฐ ๋กœ๋“œ
69
+ try:
70
+ df = pd.read_excel("data/์ตœ์ข….xlsx")
71
+ venue_df = pd.read_excel("data/๊ณต์—ฐ์‹œ์„คDB.xlsx")
72
+ except Exception as e:
73
+ st.error(f"โŒ ๋ฐ์ดํ„ฐ ํŒŒ์ผ ๋กœ๋“œ ์˜ค๋ฅ˜: {e}")
74
+ st.stop()
75
+
76
+ df = df[df["๊ณต์—ฐ์žฅ๋ฒกํ„ฐ"].notna()].copy()
77
+ try:
78
+ df["๊ณต์—ฐ์žฅ๋ฒกํ„ฐ"] = df["๊ณต์—ฐ์žฅ๋ฒกํ„ฐ"].apply(ast.literal_eval)
79
+ except Exception as e:
80
+ st.error(f"โŒ ๊ณต์—ฐ์žฅ ๋ฒกํ„ฐ ํŒŒ์‹ฑ ์˜ค๋ฅ˜: {e}")
81
+ st.stop()
82
+
83
+ df = df.merge(venue_df, on="๊ณต์—ฐ์‹œ์„คID", how="left")
84
+
85
+ # โœ… ๊ณต์—ฐ์‹œ์„คID ์ค‘๋ณต ์ œ๊ฑฐ
86
+ df = df.drop_duplicates(subset="๊ณต์—ฐ์‹œ์„คID")
87
+
88
+ # โœ… ์ถ”์ฒœ ์ˆ˜ํ–‰
89
+ results = recommend_venues(perf_vector, df, weights=[w1, w2, w3], alpha=alpha)
90
+ top_results = results.sort_values("์ข…ํ•ฉ์œ ์‚ฌ๋„", ascending=False).head(10)
91
+
92
+ # โœ… ํ‘œ ์ถœ๋ ฅ
93
+ st.subheader("โœ… ์ถ”์ฒœ ๊ณต์—ฐ์žฅ ๋ฆฌ์ŠคํŠธ (์ค‘๋ณต ์ œ๊ฑฐ๋œ ์ƒ์œ„ 10๊ณณ)")
94
+ st.dataframe(top_results[[
95
+ "๊ณต์—ฐ์‹œ์„ค๋ช…", "๊ณต์—ฐ์‹œ์„คID", "์œ ์‚ฌ๋„", "๊ฐ์„์ˆ˜์œ ์‚ฌ๋„", "์ข…ํ•ฉ์œ ์‚ฌ๋„", "๊ฐ์„ ์ˆ˜",
96
+ "๋ ˆ์Šคํ† ๋ž‘", "์นดํŽ˜", "ํŽธ์˜์ ",
97
+ "์žฅ์• ์‹œ์„ค-์ฃผ์ฐจ์žฅ", "์žฅ์• ์‹œ์„ค-ํ™”์žฅ๏ฟฝ๏ฟฝ๏ฟฝ", "์žฅ์• ์‹œ์„ค-๊ฒฝ์‚ฌ๋กœ", "์žฅ์• ์‹œ์„ค-์—˜๋ฆฌ๋ฒ ์ดํ„ฐ", "์ฃผ์†Œ"
98
+ ]])
99
+
100
+ # โœ… ์ง€๋„ ์‹œ๊ฐํ™”
101
+ st.subheader("๐Ÿ—บ๏ธ ์ถ”์ฒœ ๊ณต์—ฐ์žฅ ์œ„์น˜ ์ง€๋„")
102
+ geolocator = Nominatim(user_agent="kopis_map_app")
103
+ geocode = RateLimiter(geolocator.geocode, min_delay_seconds=1)
104
+
105
+ address_coords = {}
106
+ for addr in top_results["์ฃผ์†Œ"].dropna().unique():
107
  try:
108
+ loc = geocode(addr)
109
+ if loc:
110
+ address_coords[addr] = (loc.latitude, loc.longitude)
111
+ except:
112
+ continue
113
+
114
+ map_center = [37.5665, 126.9780]
115
+ m = folium.Map(location=map_center, zoom_start=11)
116
+
117
+ for _, row in top_results.iterrows():
118
+ addr = row["์ฃผ์†Œ"]
119
+ name = row["๊ณต์—ฐ์‹œ์„ค๋ช…"]
120
+ latlon = address_coords.get(addr)
121
+ if latlon:
122
+ folium.Marker(
123
+ location=latlon,
124
+ popup=f"{name}<br>{addr}",
125
+ tooltip=name,
126
+ icon=folium.Icon(color='blue', icon='info-sign')
127
+ ).add_to(m)
128
+
129
+ st_folium(m, width=900, height=500)