Spaces:
Running
Running
Update src/streamlit_app.py
Browse files- src/streamlit_app.py +103 -43
src/streamlit_app.py
CHANGED
@@ -9,62 +9,76 @@ import os
|
|
9 |
import matplotlib.font_manager as fm
|
10 |
|
11 |
def set_korean_font():
|
12 |
-
"""ํ๊ธ ํฐํธ ์ค์ ํจ์ -
|
13 |
|
14 |
-
#
|
15 |
-
|
16 |
-
korean_fonts = [
|
17 |
-
'NanumGothic', 'NanumBarunGothic', 'NanumSquare',
|
18 |
-
'Malgun Gothic', 'AppleGothic', 'Noto Sans CJK KR',
|
19 |
-
'DejaVu Sans', 'Arial Unicode MS'
|
20 |
-
]
|
21 |
|
22 |
-
|
23 |
-
|
24 |
-
# 2. ์ฌ์ฉ์ ์ง์ ํฐํธ ํ์ผ ํ์ธ
|
25 |
font_filename = "NanumGaRamYeonGgoc.ttf"
|
26 |
font_path = os.path.join(os.getcwd(), font_filename)
|
|
|
27 |
|
28 |
if os.path.exists(font_path):
|
29 |
try:
|
|
|
|
|
30 |
# ํฐํธ ํ์ผ์ ์์คํ
์ ๋ฑ๋ก
|
31 |
fm.fontManager.addfont(font_path)
|
32 |
font_prop = fm.FontProperties(fname=font_path)
|
33 |
selected_font = font_prop.get_name()
|
34 |
-
plt.rcParams['font.family'] = selected_font
|
35 |
st.sidebar.success(f"์ฌ์ฉ์ ํฐํธ '{selected_font}' ๋ก๋ฉ ์ฑ๊ณต!")
|
36 |
except Exception as e:
|
37 |
st.sidebar.warning(f"์ฌ์ฉ์ ํฐํธ ๋ก๋ฉ ์คํจ: {e}")
|
38 |
|
39 |
-
#
|
40 |
if not selected_font:
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
|
48 |
-
#
|
49 |
if not selected_font:
|
50 |
-
# ์ด์์ฒด์ ๋ณ ๊ธฐ๋ณธ ์ค์
|
51 |
if platform.system() == 'Windows':
|
52 |
-
|
53 |
elif platform.system() == 'Darwin': # macOS
|
54 |
-
|
55 |
-
else: # Linux
|
56 |
-
|
57 |
|
58 |
-
st.sidebar.warning(
|
59 |
-
|
60 |
-
|
61 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
|
63 |
-
|
64 |
-
plt.rcParams['axes.unicode_minus'] = False
|
65 |
|
66 |
-
|
67 |
-
st.sidebar.text(f"ํ์ฌ ํฐํธ: {plt.rcParams['font.family']}")
|
68 |
|
69 |
def analyze_scores(df):
|
70 |
"""๋ฐ์ดํฐํ๋ ์์ ๋ฐ์ ๋ถ์ ๊ฒฐ๊ณผ๋ฅผ ํ์ํ๋ ํจ์"""
|
@@ -95,22 +109,52 @@ def analyze_scores(df):
|
|
95 |
|
96 |
# 2. ๋ถํฌ ์๊ฐํ
|
97 |
st.write("#### ๐จ ์ ์ ๋ถํฌ ์๊ฐํ")
|
98 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
99 |
|
100 |
try:
|
101 |
-
|
|
|
|
|
|
|
102 |
mu, std = norm.fit(scores)
|
103 |
xmin, xmax = ax.get_xlim()
|
104 |
x = np.linspace(xmin, xmax, 100)
|
105 |
p = norm.pdf(x, mu, std)
|
106 |
-
ax.plot(x, p, '
|
107 |
-
|
108 |
-
|
109 |
-
ax.
|
110 |
-
ax.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
st.pyplot(fig)
|
|
|
112 |
except Exception as e:
|
113 |
st.error(f"๊ทธ๋ํ ์์ฑ ์ค ์ค๋ฅ ๋ฐ์: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
finally:
|
115 |
plt.close(fig) # ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง
|
116 |
|
@@ -136,11 +180,27 @@ def main():
|
|
136 |
layout="wide"
|
137 |
)
|
138 |
|
139 |
-
# ํฐํธ ์ค์ ์ ๋จผ์
|
140 |
-
set_korean_font()
|
141 |
|
142 |
st.title("ํ์ ์ ์ ๋ถํฌ ๋ถ์ ๋๊ตฌ ๐")
|
143 |
st.write("CSV ํ์ผ์ ์ง์ ์
๋ก๋ํ๊ฑฐ๋ Google Sheets URL์ ๋ถ์ฌ๋ฃ์ด ํ์ ์ ์ ๋ถํฌ๋ฅผ ๋ถ์ํฉ๋๋ค.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
144 |
st.write("---")
|
145 |
|
146 |
st.sidebar.title("๐ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ")
|
@@ -177,7 +237,7 @@ def main():
|
|
177 |
|
178 |
elif source_option == "Google Sheets URL":
|
179 |
st.sidebar.info("Google Sheets๋ฅผ '์น์ ๊ฒ์'ํ๊ณ CSV ํ์์ URL์ ์
๋ ฅํ์ธ์.")
|
180 |
-
sample_url = "https://docs.google.com/spreadsheets/d/e/2PACX-
|
181 |
url = st.sidebar.text_input(
|
182 |
"Google Sheets CSV URL",
|
183 |
value="",
|
|
|
9 |
import matplotlib.font_manager as fm
|
10 |
|
11 |
def set_korean_font():
|
12 |
+
"""ํ๊ธ ํฐํธ ์ค์ ํจ์ - ๊ฐ์ ๋ก ํฐํธ ์ ์ฉ"""
|
13 |
|
14 |
+
# matplotlib ์บ์ ํด๋ฆฌ์ด (์ค์!)
|
15 |
+
plt.rcdefaults()
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
+
# 1. ์ฌ์ฉ์ ์ง์ ํฐํธ ํ์ผ ํ์ธ
|
|
|
|
|
18 |
font_filename = "NanumGaRamYeonGgoc.ttf"
|
19 |
font_path = os.path.join(os.getcwd(), font_filename)
|
20 |
+
selected_font = None
|
21 |
|
22 |
if os.path.exists(font_path):
|
23 |
try:
|
24 |
+
# ํฐํธ ๋งค๋์ ์บ์ ํด๋ฆฌ์ด
|
25 |
+
fm.fontManager.__init__()
|
26 |
# ํฐํธ ํ์ผ์ ์์คํ
์ ๋ฑ๋ก
|
27 |
fm.fontManager.addfont(font_path)
|
28 |
font_prop = fm.FontProperties(fname=font_path)
|
29 |
selected_font = font_prop.get_name()
|
|
|
30 |
st.sidebar.success(f"์ฌ์ฉ์ ํฐํธ '{selected_font}' ๋ก๋ฉ ์ฑ๊ณต!")
|
31 |
except Exception as e:
|
32 |
st.sidebar.warning(f"์ฌ์ฉ์ ํฐํธ ๋ก๋ฉ ์คํจ: {e}")
|
33 |
|
34 |
+
# 2. ์์คํ
์์ ์ฌ์ฉ ๊ฐ๋ฅํ ํ๊ธ ํฐํธ ์ฐพ๊ธฐ
|
35 |
if not selected_font:
|
36 |
+
# ์ค์ ๋ก ์ฌ์ฉ ๊ฐ๋ฅํ ํฐํธ๋ค ์ฒดํฌ
|
37 |
+
system_fonts = fm.findSystemFonts()
|
38 |
+
korean_font_candidates = []
|
39 |
+
|
40 |
+
for font_path in system_fonts:
|
41 |
+
try:
|
42 |
+
font_name = fm.FontProperties(fname=font_path).get_name()
|
43 |
+
# ํ๊ธ ํฐํธ ํ๋ณด๋ค
|
44 |
+
if any(keyword in font_name.lower() for keyword in
|
45 |
+
['nanum', 'malgun', 'gothic', 'gulim', 'dotum', 'batang']):
|
46 |
+
korean_font_candidates.append(font_name)
|
47 |
+
except:
|
48 |
+
continue
|
49 |
+
|
50 |
+
if korean_font_candidates:
|
51 |
+
selected_font = korean_font_candidates[0]
|
52 |
+
st.sidebar.info(f"์์คํ
ํ๊ธ ํฐํธ '{selected_font}' ๋ฐ๊ฒฌ!")
|
53 |
|
54 |
+
# 3. ์ด์์ฒด์ ๋ณ ๊ธฐ๋ณธ ํฐํธ ์ค์
|
55 |
if not selected_font:
|
|
|
56 |
if platform.system() == 'Windows':
|
57 |
+
selected_font = 'Malgun Gothic'
|
58 |
elif platform.system() == 'Darwin': # macOS
|
59 |
+
selected_font = 'AppleGothic'
|
60 |
+
else: # Linux - ๋๋ํฐํธ๊ฐ ์์ผ๋ฉด ๊ธฐ๋ณธ Sans ์ฌ์ฉ
|
61 |
+
selected_font = 'sans-serif'
|
62 |
|
63 |
+
st.sidebar.warning(f"๊ธฐ๋ณธ ํฐํธ '{selected_font}' ์ฌ์ฉ")
|
64 |
+
|
65 |
+
# 4. matplotlib ์ค์ ๊ฐ์ ์ ์ฉ
|
66 |
+
plt.rcParams.update({
|
67 |
+
'font.family': selected_font,
|
68 |
+
'font.sans-serif': [selected_font, 'DejaVu Sans', 'Arial'],
|
69 |
+
'axes.unicode_minus': False,
|
70 |
+
'font.size': 10
|
71 |
+
})
|
72 |
+
|
73 |
+
# 5. ํฐํธ ์ค์ ๊ฒ์ฆ
|
74 |
+
test_fig, test_ax = plt.subplots(figsize=(1, 1))
|
75 |
+
test_ax.text(0.5, 0.5, 'ํ๊ธํ
์คํธ', ha='center', va='center')
|
76 |
+
current_font = test_ax.texts[0].get_fontname()
|
77 |
+
plt.close(test_fig)
|
78 |
|
79 |
+
st.sidebar.text(f"์ ์ฉ๋ ํฐํธ: {current_font}")
|
|
|
80 |
|
81 |
+
return selected_font
|
|
|
82 |
|
83 |
def analyze_scores(df):
|
84 |
"""๋ฐ์ดํฐํ๋ ์์ ๋ฐ์ ๋ถ์ ๊ฒฐ๊ณผ๋ฅผ ํ์ํ๋ ํจ์"""
|
|
|
109 |
|
110 |
# 2. ๋ถํฌ ์๊ฐํ
|
111 |
st.write("#### ๐จ ์ ์ ๋ถํฌ ์๊ฐํ")
|
112 |
+
|
113 |
+
# ๋งค๋ฒ ์๋ก์ด figure ์์ฑ ์ ํฐํธ ์ค์ ์ฌ์ ์ฉ
|
114 |
+
plt.rcParams.update({
|
115 |
+
'font.family': plt.rcParams.get('font.family', 'sans-serif'),
|
116 |
+
'axes.unicode_minus': False
|
117 |
+
})
|
118 |
+
|
119 |
+
fig, ax = plt.subplots(figsize=(12, 7))
|
120 |
|
121 |
try:
|
122 |
+
# ํ์คํ ๊ทธ๋จ๊ณผ KDE ๊ณก์ ๊ทธ๋ฆฌ๊ธฐ
|
123 |
+
sns.histplot(scores, kde=True, stat='density', alpha=0.7, ax=ax)
|
124 |
+
|
125 |
+
# ์ ๊ท๋ถํฌ ๊ณก์ ์ถ๊ฐ
|
126 |
mu, std = norm.fit(scores)
|
127 |
xmin, xmax = ax.get_xlim()
|
128 |
x = np.linspace(xmin, xmax, 100)
|
129 |
p = norm.pdf(x, mu, std)
|
130 |
+
ax.plot(x, p, 'r-', linewidth=2, label=f'์ ๊ท๋ถํฌ (ฮผ={mu:.1f}, ฯ={std:.1f})')
|
131 |
+
|
132 |
+
# ์ ๋ชฉ๊ณผ ๋ผ๋ฒจ ์ค์ (ํ๊ธ ํฌํจ)
|
133 |
+
ax.set_title(f'{score_column} ์ ์ ๋ถํฌ ๋ถ์', fontsize=16, pad=20)
|
134 |
+
ax.set_xlabel('์ ์', fontsize=12)
|
135 |
+
ax.set_ylabel('๋ฐ๋', fontsize=12)
|
136 |
+
ax.legend(fontsize=10)
|
137 |
+
ax.grid(True, alpha=0.3)
|
138 |
+
|
139 |
+
# ํต๊ณ ์ ๋ณด ํ
์คํธ ๋ฐ์ค ์ถ๊ฐ
|
140 |
+
stats_text = f'ํ๊ท : {mu:.2f}\nํ์คํธ์ฐจ: {std:.2f}\n์ํ ์: {len(scores)}'
|
141 |
+
ax.text(0.02, 0.98, stats_text, transform=ax.transAxes,
|
142 |
+
fontsize=10, verticalalignment='top',
|
143 |
+
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))
|
144 |
+
|
145 |
+
plt.tight_layout()
|
146 |
st.pyplot(fig)
|
147 |
+
|
148 |
except Exception as e:
|
149 |
st.error(f"๊ทธ๋ํ ์์ฑ ์ค ์ค๋ฅ ๋ฐ์: {e}")
|
150 |
+
# ์๋ฌ ๋ฐ์ ์ ๊ฐ๋จํ ํ์คํ ๊ทธ๋จ์ผ๋ก ๋์ฒด
|
151 |
+
fig2, ax2 = plt.subplots(figsize=(10, 6))
|
152 |
+
ax2.hist(scores, bins=20, alpha=0.7, edgecolor='black')
|
153 |
+
ax2.set_title('Score Distribution (Fallback)', fontsize=14)
|
154 |
+
ax2.set_xlabel('Score')
|
155 |
+
ax2.set_ylabel('Frequency')
|
156 |
+
st.pyplot(fig2)
|
157 |
+
plt.close(fig2)
|
158 |
finally:
|
159 |
plt.close(fig) # ๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง
|
160 |
|
|
|
180 |
layout="wide"
|
181 |
)
|
182 |
|
183 |
+
# ํฐํธ ์ค์ ์ ๋จผ์ ์คํํ๊ณ ์ค์ ๋ ํฐํธ๋ช
๋ฐ๊ธฐ
|
184 |
+
selected_font = set_korean_font()
|
185 |
|
186 |
st.title("ํ์ ์ ์ ๋ถํฌ ๋ถ์ ๋๊ตฌ ๐")
|
187 |
st.write("CSV ํ์ผ์ ์ง์ ์
๋ก๋ํ๊ฑฐ๋ Google Sheets URL์ ๋ถ์ฌ๋ฃ์ด ํ์ ์ ์ ๋ถํฌ๋ฅผ ๋ถ์ํฉ๋๋ค.")
|
188 |
+
|
189 |
+
# ํฐํธ ํ
์คํธ (๋๋ฒ๊น
์ฉ)
|
190 |
+
if st.sidebar.checkbox("ํฐํธ ํ
์คํธ ๋ณด๊ธฐ"):
|
191 |
+
test_fig, test_ax = plt.subplots(figsize=(8, 4))
|
192 |
+
test_ax.text(0.5, 0.7, f'ํ๊ธ ํฐํธ ํ
์คํธ: {selected_font}',
|
193 |
+
ha='center', va='center', fontsize=14)
|
194 |
+
test_ax.text(0.5, 0.5, '๊ฐ๋๋ค๋ผ๋ง๋ฐ์ฌ ABCD 1234',
|
195 |
+
ha='center', va='center', fontsize=12)
|
196 |
+
test_ax.text(0.5, 0.3, '์ ์, ๋ถํฌ, ๋ถ์, ๊ทธ๋ํ',
|
197 |
+
ha='center', va='center', fontsize=12)
|
198 |
+
test_ax.set_xlim(0, 1)
|
199 |
+
test_ax.set_ylim(0, 1)
|
200 |
+
test_ax.axis('off')
|
201 |
+
st.pyplot(test_fig)
|
202 |
+
plt.close(test_fig)
|
203 |
+
|
204 |
st.write("---")
|
205 |
|
206 |
st.sidebar.title("๐ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ")
|
|
|
237 |
|
238 |
elif source_option == "Google Sheets URL":
|
239 |
st.sidebar.info("Google Sheets๋ฅผ '์น์ ๊ฒ์'ํ๊ณ CSV ํ์์ URL์ ์
๋ ฅํ์ธ์.")
|
240 |
+
sample_url = "https://docs.google.com/spreadsheets/d/e/2PACX-1vQ2Z8kzJq2sM7w2_9gXo-jZ-mO5o-BvC-w5p2nJ6oJ7oJ9xL-w3kZ9j5Z3kX7vN1aQ4mB1cW8jB7fR/pub?gid=0&single=true&output=csv"
|
241 |
url = st.sidebar.text_input(
|
242 |
"Google Sheets CSV URL",
|
243 |
value="",
|