Spaces:
Sleeping
Sleeping
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="",
|