JUNGU commited on
Commit
d107d23
ยท
verified ยท
1 Parent(s): 4cb7c38

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +86 -164
src/streamlit_app.py CHANGED
@@ -10,99 +10,50 @@ import matplotlib.font_manager as fm
10
  import warnings
11
  warnings.filterwarnings('ignore')
12
 
13
- # ์ „์—ญ ํฐํŠธ ์„ค์ • - ์•ฑ ์‹œ์ž‘ ์ฆ‰์‹œ ์‹คํ–‰
14
- def configure_matplotlib_korean():
15
- """matplotlib ํ•œ๊ธ€ ํฐํŠธ ๊ฐ•์ œ ์„ค์ •"""
16
-
17
- # matplotlib ๋ฐฑ์—”๋“œ ์„ค์ •
18
- plt.switch_backend('Agg')
19
-
20
- # ๊ธฐ์กด ์„ค์ • ์™„์ „ ์ดˆ๊ธฐํ™”
21
- plt.rcdefaults()
22
-
23
- # ํฐํŠธ ์บ์‹œ ์™„์ „ ์‚ญ์ œ ๋ฐ ์žฌ๊ตฌ์„ฑ
24
  try:
25
- fm._load_fontmanager(try_read_cache=False)
26
- except:
27
- pass
28
-
29
- # ์šด์˜์ฒด์ œ๋ณ„ ํ•œ๊ธ€ ํฐํŠธ ๊ฒฝ๋กœ ์ง์ ‘ ์ง€์ •
30
- korean_font_paths = []
31
-
32
- if platform.system() == 'Windows':
33
- korean_font_paths = [
34
- 'C:/Windows/Fonts/malgun.ttf',
35
- 'C:/Windows/Fonts/gulim.ttc',
36
- 'C:/Windows/Fonts/batang.ttc',
37
- ]
38
- fallback_font = 'Malgun Gothic'
39
- elif platform.system() == 'Darwin': # macOS
40
- korean_font_paths = [
41
- '/System/Library/Fonts/AppleGothic.ttf',
42
- '/System/Library/Fonts/Helvetica.ttc',
43
- ]
44
- fallback_font = 'AppleGothic'
45
- else: # Linux
46
- korean_font_paths = [
47
- '/usr/share/fonts/truetype/nanum/NanumGothic.ttf',
48
- '/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf',
49
- ]
50
- fallback_font = 'DejaVu Sans'
51
-
52
- # ์‚ฌ์šฉ์ž ์ •์˜ ํฐํŠธ ํŒŒ์ผ ๊ฒฝ๋กœ๋„ ์ถ”๊ฐ€
53
- user_font = os.path.join(os.getcwd(), "NanumGaRamYeonGgoc.ttf")
54
- if os.path.exists(user_font):
55
- korean_font_paths.insert(0, user_font)
56
-
57
- selected_font_path = None
58
- selected_font_name = fallback_font
59
-
60
- # ์‹ค์ œ ์กด์žฌํ•˜๋Š” ํฐํŠธ ํŒŒ์ผ ์ฐพ๊ธฐ
61
- for font_path in korean_font_paths:
62
- if os.path.exists(font_path):
63
- try:
64
- font_prop = fm.FontProperties(fname=font_path)
65
- selected_font_name = font_prop.get_name()
66
- selected_font_path = font_path
67
-
68
- # ํฐํŠธ ๋งค๋‹ˆ์ €์— ๊ฐ•์ œ ๋“ฑ๋ก
69
- fm.fontManager.addfont(font_path)
70
  break
71
- except Exception as e:
72
- continue
73
-
74
- # matplotlib rcParams ๊ฐ•์ œ ์„ค์ •
75
- plt.rcParams.update({
76
- 'font.family': 'sans-serif',
77
- 'font.sans-serif': [selected_font_name, 'DejaVu Sans', 'Arial', 'sans-serif'],
78
- 'axes.unicode_minus': False,
79
- 'font.size': 12,
80
- 'figure.dpi': 100,
81
- 'savefig.dpi': 100,
82
- 'figure.facecolor': 'white',
83
- 'axes.facecolor': 'white'
84
- })
85
-
86
- # ์ „์—ญ ํฐํŠธ ์†์„ฑ ๊ฐ์ฒด ์ƒ์„ฑ
87
- if selected_font_path:
88
- global KOREAN_FONT_PROP
89
- KOREAN_FONT_PROP = fm.FontProperties(fname=selected_font_path)
90
- else:
91
- KOREAN_FONT_PROP = fm.FontProperties(family=selected_font_name)
92
-
93
- return selected_font_name, selected_font_path
94
-
95
- # ์•ฑ ์‹œ์ž‘ ์‹œ ์ฆ‰์‹œ ํฐํŠธ ์„ค์ •
96
- FONT_NAME, FONT_PATH = configure_matplotlib_korean()
97
- KOREAN_FONT_PROP = None
98
 
99
- def apply_korean_font_to_plot():
100
- """๊ฐœ๋ณ„ ํ”Œ๋กฏ์— ํ•œ๊ธ€ ํฐํŠธ ์ง์ ‘ ์ ์šฉ"""
101
- if FONT_PATH and os.path.exists(FONT_PATH):
102
- font_prop = fm.FontProperties(fname=FONT_PATH)
103
- return font_prop
104
- else:
105
- return fm.FontProperties(family=FONT_NAME)
106
 
107
  def analyze_scores(df):
108
  """๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„์„ ๋ฐ›์•„ ๋ถ„์„ ๊ฒฐ๊ณผ๋ฅผ ํ‘œ์‹œํ•˜๋Š” ํ•จ์ˆ˜"""
@@ -126,7 +77,7 @@ def analyze_scores(df):
126
 
127
  st.subheader(f"๐Ÿ“ˆ '{score_column}' ์ ์ˆ˜ ๋ถ„ํฌ ๋ถ„์„ ๊ฒฐ๊ณผ")
128
 
129
- # 1. ๊ธฐ์ˆ  ํ†ต๊ณ„๋Ÿ‰
130
  st.write("#### ๐Ÿ“Š ๊ธฐ๋ณธ ํ†ต๊ณ„๋Ÿ‰")
131
  col1, col2, col3, col4 = st.columns(4)
132
  with col1:
@@ -142,66 +93,45 @@ def analyze_scores(df):
142
  st.write("#### ๐Ÿ“‹ ์ƒ์„ธ ํ†ต๊ณ„๋Ÿ‰")
143
  st.dataframe(scores.describe().to_frame().T)
144
 
145
- # 2. ๋ถ„ํฌ ์‹œ๊ฐํ™” - ๊ฐ•ํ™”๋œ ํ•œ๊ธ€ ํฐํŠธ ์ ์šฉ
146
  st.write("#### ๐ŸŽจ ์ ์ˆ˜ ๋ถ„ํฌ ์‹œ๊ฐํ™”")
147
 
148
  try:
149
- # ํฐํŠธ ์†์„ฑ ๊ฐ์ฒด ์ƒ์„ฑ
150
- korean_font = apply_korean_font_to_plot()
151
-
152
- # Figure ์ƒ์„ฑ ๋ฐ ์„ค์ •
153
- fig, ax = plt.subplots(figsize=(14, 8))
154
- fig.patch.set_facecolor('white')
155
 
156
- # ํžˆ์Šคํ† ๊ทธ๋žจ ์ƒ์„ฑ
157
- n, bins, patches = ax.hist(scores, bins=20, density=True, alpha=0.7,
158
- color='skyblue', edgecolor='navy', linewidth=0.8)
159
-
160
- # KDE ๊ณก์„  ์ถ”๊ฐ€
161
- try:
162
- from scipy.stats import gaussian_kde
163
- kde = gaussian_kde(scores)
164
- x_range = np.linspace(scores.min(), scores.max(), 200)
165
- ax.plot(x_range, kde(x_range), 'orange', linewidth=3, label='์‹ค์ œ ๋ถ„ํฌ ๊ณก์„ ')
166
- except:
167
- pass
168
 
169
  # ์ •๊ทœ๋ถ„ํฌ ๊ณก์„  ์ถ”๊ฐ€
170
  mu, std = norm.fit(scores)
171
- x_norm = np.linspace(scores.min(), scores.max(), 100)
172
- y_norm = norm.pdf(x_norm, mu, std)
173
- ax.plot(x_norm, y_norm, 'red', linewidth=2, linestyle='--',
174
- label=f'์ •๊ทœ๋ถ„ํฌ (ํ‰๊ท ={mu:.1f}, ํ‘œ์ค€ํŽธ์ฐจ={std:.1f})')
175
 
176
- # ํ‰๊ท ์„  ์ถ”๊ฐ€
177
  ax.axvline(mu, color='red', linestyle=':', linewidth=2, alpha=0.8, label=f'ํ‰๊ท : {mu:.1f}')
178
 
179
- # ์ œ๋ชฉ๊ณผ ๋ผ๋ฒจ - ํ•œ๊ธ€ ํฐํŠธ ์ง์ ‘ ์ ์šฉ
180
- ax.set_title(f'{score_column} ์ ์ˆ˜ ๋ถ„ํฌ ๋ถ„์„', fontproperties=korean_font, fontsize=18, pad=20)
181
- ax.set_xlabel('์ ์ˆ˜', fontproperties=korean_font, fontsize=14)
182
- ax.set_ylabel('๋ฐ€๋„', fontproperties=korean_font, fontsize=14)
183
-
184
- # ๋ฒ”๋ก€ ์„ค์ •
185
- legend = ax.legend(prop=korean_font, fontsize=11, loc='upper right')
186
- legend.get_frame().set_alpha(0.9)
187
-
188
- # ๊ฒฉ์ž ์ถ”๊ฐ€
189
- ax.grid(True, alpha=0.3, linestyle='-', linewidth=0.5)
190
 
191
  # ํ†ต๊ณ„ ์ •๋ณด ๋ฐ•์Šค
192
  stats_text = f'์ƒ˜ํ”Œ ์ˆ˜: {len(scores)}\nํ‰๊ท : {mu:.2f}\nํ‘œ์ค€ํŽธ์ฐจ: {std:.2f}\n์ตœ์†Ÿ๊ฐ’: {scores.min():.1f}\n์ตœ๋Œ“๊ฐ’: {scores.max():.1f}'
193
  ax.text(0.02, 0.98, stats_text, transform=ax.transAxes,
194
- fontproperties=korean_font, fontsize=10, verticalalignment='top',
195
  bbox=dict(boxstyle='round,pad=0.5', facecolor='lightblue', alpha=0.8))
196
 
197
  plt.tight_layout()
198
  st.pyplot(fig)
199
 
200
  except Exception as e:
201
- st.error(f"โŒ ์ƒ์„ธ ๊ทธ๋ž˜ํ”„ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜: {e}")
202
 
203
  # ๋Œ€์ฒด ๊ทธ๋ž˜ํ”„ (์˜์–ด๋งŒ ์‚ฌ์šฉ)
204
- st.write("**Alternative Chart (English only):**")
205
  fig2, ax2 = plt.subplots(figsize=(10, 6))
206
  ax2.hist(scores, bins=15, alpha=0.7, color='lightcoral', edgecolor='black')
207
  ax2.set_title(f'Distribution of {score_column}', fontsize=14)
@@ -225,30 +155,29 @@ def analyze_scores(df):
225
 
226
  with col2:
227
  if skewness > 0.5:
228
- st.success("๐Ÿ”ด **์–‘์˜ ์™œ๋„ (Right Skewed)**: ๋Œ€๋ถ€๋ถ„ ํ•™์ƒ์ด ๋‚ฎ์€ ์ ์ˆ˜๋Œ€์— ๋ถ„ํฌํ•˜๊ณ , ์†Œ์ˆ˜์˜ ๊ณ ๋“์ ์ž๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.")
229
  elif skewness < -0.5:
230
- st.success("๐Ÿ”ต **์Œ์˜ ์™œ๋„ (Left Skewed)**: ๋Œ€๋ถ€๋ถ„ ํ•™์ƒ์ด ๋†’์€ ์ ์ˆ˜๋Œ€์— ๋ถ„ํฌํ•˜๊ณ , ์†Œ์ˆ˜์˜ ์ €๋“์ ์ž๊ฐ€ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค.")
231
  else:
232
- st.success("๐ŸŸข **๋Œ€์นญ ๋ถ„ํฌ**: ์ ์ˆ˜๊ฐ€ ํ‰๊ท ์„ ์ค‘์‹ฌ์œผ๋กœ ๊ณ ๋ฅด๊ฒŒ ๋ถ„ํฌ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.")
233
 
234
  except Exception as e:
235
- st.error(f"์™œ๋„ ๊ณ„์‚ฐ ์ค‘ ์˜ค๋ฅ˜: {e}")
236
 
237
- # 4. ์ถ”๊ฐ€ ๋ถ„์„
238
  st.write("#### ๐Ÿ“‹ ๊ตฌ๊ฐ„๋ณ„ ๋ถ„ํฌ")
239
 
240
- # ์ ์ˆ˜ ๊ตฌ๊ฐ„ ๋‚˜๋ˆ„๊ธฐ
241
- if scores.max() <= 100: # 100์  ๋งŒ์  ๊ฐ€์ •
242
- bins_labels = ['0-60', '61-70', '71-80', '81-90', '91-100']
243
- bins_edges = [0, 60, 70, 80, 90, 100]
244
- else:
245
- # ๋™์  ๊ตฌ๊ฐ„ ์ƒ์„ฑ
246
- min_score, max_score = scores.min(), scores.max()
247
- interval = (max_score - min_score) / 5
248
- bins_edges = [min_score + i * interval for i in range(6)]
249
- bins_labels = [f'{bins_edges[i]:.0f}-{bins_edges[i+1]:.0f}' for i in range(5)]
250
-
251
  try:
 
 
 
 
 
 
 
 
 
 
252
  score_counts = pd.cut(scores, bins=bins_edges, labels=bins_labels, include_lowest=True).value_counts().sort_index()
253
  score_percentages = (score_counts / len(scores) * 100).round(1)
254
 
@@ -260,14 +189,13 @@ def analyze_scores(df):
260
  st.dataframe(result_df)
261
 
262
  except Exception as e:
263
- st.warning(f"๊ตฌ๊ฐ„ ๋ถ„์„ ์ค‘ ์˜ค๋ฅ˜: {e}")
264
 
265
  def main():
266
  st.set_page_config(
267
  page_title="ํ•™์ƒ ์ ์ˆ˜ ๋ถ„์„ ๋„๊ตฌ",
268
  page_icon="๐Ÿ“Š",
269
- layout="wide",
270
- initial_sidebar_state="expanded"
271
  )
272
 
273
  # ์ œ๋ชฉ
@@ -276,25 +204,19 @@ def main():
276
 
277
  # ํฐํŠธ ์ •๋ณด ํ‘œ์‹œ
278
  with st.expander("๐Ÿ”ง ํฐํŠธ ์„ค์ • ์ •๋ณด"):
279
- st.write(f"**ํ˜„์žฌ ํฐํŠธ**: {FONT_NAME}")
280
  st.write(f"**ํฐํŠธ ๊ฒฝ๋กœ**: {FONT_PATH if FONT_PATH else '์‹œ์Šคํ…œ ๊ธฐ๋ณธ'}")
281
 
282
- # ํฐํŠธ ํ…Œ์ŠคํŠธ
283
  if st.button("ํฐํŠธ ํ…Œ์ŠคํŠธ"):
284
- try:
285
- test_fig, test_ax = plt.subplots(figsize=(8, 3))
286
- korean_font = apply_korean_font_to_plot()
287
- test_ax.text(0.5, 0.7, 'ํ•œ๊ธ€ ํฐํŠธ ํ…Œ์ŠคํŠธ', ha='center', va='center',
288
- fontproperties=korean_font, fontsize=16)
289
- test_ax.text(0.5, 0.3, '์ ์ˆ˜ ๋ถ„ํฌ ๋ถ„์„ ๊ทธ๋ž˜ํ”„', ha='center', va='center',
290
- fontproperties=korean_font, fontsize=14)
291
- test_ax.set_xlim(0, 1)
292
- test_ax.set_ylim(0, 1)
293
- test_ax.axis('off')
294
- st.pyplot(test_fig)
295
- plt.close(test_fig)
296
- except Exception as e:
297
- st.error(f"ํฐํŠธ ํ…Œ์ŠคํŠธ ์‹คํŒจ: {e}")
298
 
299
  st.markdown("---")
300
 
 
10
  import warnings
11
  warnings.filterwarnings('ignore')
12
 
13
+ # ์‹ฌํ”Œํ•œ ํ•œ๊ธ€ ํฐํŠธ ์„ค์ • - ์•ฑ ์‹œ์ž‘์‹œ ํ•œ๋ฒˆ๋งŒ ์‹คํ–‰
14
+ def setup_korean_font():
15
+ """ํ•œ๊ธ€ ํฐํŠธ๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ์„ค์ •ํ•˜๋Š” ํ•จ์ˆ˜"""
 
 
 
 
 
 
 
 
16
  try:
17
+ # 1. ์‚ฌ์šฉ์ž ํฐํŠธ ํŒŒ์ผ ํ™•์ธ
18
+ script_dir = os.path.dirname(os.path.abspath(__file__))
19
+ possible_fonts = ["NanumGaRamYeonGgoc.ttf", "NanumGothic.ttf", "malgun.ttf"]
20
+
21
+ font_path = None
22
+ for font_file in possible_fonts:
23
+ candidate = os.path.join(script_dir, font_file)
24
+ if os.path.exists(candidate):
25
+ font_path = candidate
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  break
27
+
28
+ # 2. ํฐํŠธ ์ ์šฉ
29
+ if font_path:
30
+ # ํฐํŠธ ํŒŒ์ผ์ด ์žˆ์œผ๋ฉด ์ง์ ‘ ์‚ฌ์šฉ
31
+ plt.rcParams['font.family'] = fm.FontProperties(fname=font_path).get_name()
32
+ st.sidebar.success(f"ํฐํŠธ ๋กœ๋”ฉ ์„ฑ๊ณต: {os.path.basename(font_path)}")
33
+ else:
34
+ # ์‹œ์Šคํ…œ ๊ธฐ๋ณธ ํฐํŠธ ์‚ฌ์šฉ
35
+ if platform.system() == 'Windows':
36
+ plt.rcParams['font.family'] = 'Malgun Gothic'
37
+ elif platform.system() == 'Darwin': # macOS
38
+ plt.rcParams['font.family'] = 'AppleGothic'
39
+ else: # Linux
40
+ plt.rcParams['font.family'] = 'DejaVu Sans'
41
+
42
+ st.sidebar.info(f"์‹œ์Šคํ…œ ๊ธฐ๋ณธ ํฐํŠธ ์‚ฌ์šฉ: {plt.rcParams['font.family']}")
43
+
44
+ # ๋งˆ์ด๋„ˆ์Šค ๊ธฐํ˜ธ ๊นจ์ง ๋ฐฉ์ง€
45
+ plt.rcParams['axes.unicode_minus'] = False
46
+
47
+ return font_path
48
+
49
+ except Exception as e:
50
+ st.sidebar.warning(f"ํฐํŠธ ์„ค์ • ์˜ค๋ฅ˜: {e}")
51
+ plt.rcParams['font.family'] = 'DejaVu Sans'
52
+ plt.rcParams['axes.unicode_minus'] = False
53
+ return None
54
 
55
+ # ์•ฑ ์‹œ์ž‘์‹œ ํฐํŠธ ์„ค์ •
56
+ FONT_PATH = setup_korean_font()
 
 
 
 
 
57
 
58
  def analyze_scores(df):
59
  """๋ฐ์ดํ„ฐํ”„๋ ˆ์ž„์„ ๋ฐ›์•„ ๋ถ„์„ ๊ฒฐ๊ณผ๋ฅผ ํ‘œ์‹œํ•˜๋Š” ํ•จ์ˆ˜"""
 
77
 
78
  st.subheader(f"๐Ÿ“ˆ '{score_column}' ์ ์ˆ˜ ๋ถ„ํฌ ๋ถ„์„ ๊ฒฐ๊ณผ")
79
 
80
+ # 1. ๊ธฐ๋ณธ ํ†ต๊ณ„๋Ÿ‰
81
  st.write("#### ๐Ÿ“Š ๊ธฐ๋ณธ ํ†ต๊ณ„๋Ÿ‰")
82
  col1, col2, col3, col4 = st.columns(4)
83
  with col1:
 
93
  st.write("#### ๐Ÿ“‹ ์ƒ์„ธ ํ†ต๊ณ„๋Ÿ‰")
94
  st.dataframe(scores.describe().to_frame().T)
95
 
96
+ # 2. ๋ถ„ํฌ ์‹œ๊ฐํ™”
97
  st.write("#### ๐ŸŽจ ์ ์ˆ˜ ๋ถ„ํฌ ์‹œ๊ฐํ™”")
98
 
99
  try:
100
+ fig, ax = plt.subplots(figsize=(12, 7))
 
 
 
 
 
101
 
102
+ # ํžˆ์Šคํ† ๊ทธ๋žจ๊ณผ KDE ๊ณก์„ 
103
+ sns.histplot(scores, kde=True, stat='density', alpha=0.7, ax=ax, color='skyblue')
 
 
 
 
 
 
 
 
 
 
104
 
105
  # ์ •๊ทœ๋ถ„ํฌ ๊ณก์„  ์ถ”๊ฐ€
106
  mu, std = norm.fit(scores)
107
+ x = np.linspace(scores.min(), scores.max(), 100)
108
+ y = norm.pdf(x, mu, std)
109
+ ax.plot(x, y, 'r-', linewidth=2, label=f'์ •๊ทœ๋ถ„ํฌ (ฮผ={mu:.1f}, ฯƒ={std:.1f})')
 
110
 
111
+ # ํ‰๊ท ์„ 
112
  ax.axvline(mu, color='red', linestyle=':', linewidth=2, alpha=0.8, label=f'ํ‰๊ท : {mu:.1f}')
113
 
114
+ # ์ œ๋ชฉ๊ณผ ๋ผ๋ฒจ - ํ•œ๊ธ€์ด ์ž˜ ๋ณด์ด๋„๋ก ์„ค์ •
115
+ ax.set_title(f'{score_column} ์ ์ˆ˜ ๋ถ„ํฌ ๋ถ„์„', fontsize=16, pad=20)
116
+ ax.set_xlabel('์ ์ˆ˜', fontsize=12)
117
+ ax.set_ylabel('๋ฐ€๋„', fontsize=12)
118
+ ax.legend(fontsize=10)
119
+ ax.grid(True, alpha=0.3)
 
 
 
 
 
120
 
121
  # ํ†ต๊ณ„ ์ •๋ณด ๋ฐ•์Šค
122
  stats_text = f'์ƒ˜ํ”Œ ์ˆ˜: {len(scores)}\nํ‰๊ท : {mu:.2f}\nํ‘œ์ค€ํŽธ์ฐจ: {std:.2f}\n์ตœ์†Ÿ๊ฐ’: {scores.min():.1f}\n์ตœ๋Œ“๊ฐ’: {scores.max():.1f}'
123
  ax.text(0.02, 0.98, stats_text, transform=ax.transAxes,
124
+ fontsize=10, verticalalignment='top',
125
  bbox=dict(boxstyle='round,pad=0.5', facecolor='lightblue', alpha=0.8))
126
 
127
  plt.tight_layout()
128
  st.pyplot(fig)
129
 
130
  except Exception as e:
131
+ st.error(f"โŒ ๊ทธ๋ž˜ํ”„ ์ƒ์„ฑ ์˜ค๋ฅ˜: {e}")
132
 
133
  # ๋Œ€์ฒด ๊ทธ๋ž˜ํ”„ (์˜์–ด๋งŒ ์‚ฌ์šฉ)
134
+ st.write("**Simple Chart (English):**")
135
  fig2, ax2 = plt.subplots(figsize=(10, 6))
136
  ax2.hist(scores, bins=15, alpha=0.7, color='lightcoral', edgecolor='black')
137
  ax2.set_title(f'Distribution of {score_column}', fontsize=14)
 
155
 
156
  with col2:
157
  if skewness > 0.5:
158
+ st.success("๐Ÿ”ด **์–‘์˜ ์™œ๋„**: ๋Œ€๋ถ€๋ถ„ ํ•™์ƒ์ด ๋‚ฎ์€ ์ ์ˆ˜๋Œ€์— ๋ถ„ํฌ, ์†Œ์ˆ˜์˜ ๊ณ ๋“์ ์ž ์กด์žฌ")
159
  elif skewness < -0.5:
160
+ st.success("๐Ÿ”ต **์Œ์˜ ์™œ๋„**: ๋Œ€๋ถ€๋ถ„ ํ•™์ƒ์ด ๋†’์€ ์ ์ˆ˜๋Œ€์— ๋ถ„ํฌ, ์†Œ์ˆ˜์˜ ์ €๋“์ ์ž ์กด์žฌ")
161
  else:
162
+ st.success("๐ŸŸข **๋Œ€์นญ ๋ถ„ํฌ**: ์ ์ˆ˜๊ฐ€ ํ‰๊ท ์„ ์ค‘์‹ฌ์œผ๋กœ ๊ณ ๋ฅด๊ฒŒ ๋ถ„ํฌ")
163
 
164
  except Exception as e:
165
+ st.error(f"์™œ๋„ ๊ณ„์‚ฐ ์˜ค๋ฅ˜: {e}")
166
 
167
+ # 4. ๊ตฌ๊ฐ„๋ณ„ ๋ถ„ํฌ
168
  st.write("#### ๐Ÿ“‹ ๊ตฌ๊ฐ„๋ณ„ ๋ถ„ํฌ")
169
 
 
 
 
 
 
 
 
 
 
 
 
170
  try:
171
+ if scores.max() <= 100: # 100์  ๋งŒ์  ๊ฐ€์ •
172
+ bins_labels = ['0-60', '61-70', '71-80', '81-90', '91-100']
173
+ bins_edges = [0, 60, 70, 80, 90, 100]
174
+ else:
175
+ # ๋™์  ๊ตฌ๊ฐ„ ์ƒ์„ฑ
176
+ min_score, max_score = scores.min(), scores.max()
177
+ interval = (max_score - min_score) / 5
178
+ bins_edges = [min_score + i * interval for i in range(6)]
179
+ bins_labels = [f'{bins_edges[i]:.0f}-{bins_edges[i+1]:.0f}' for i in range(5)]
180
+
181
  score_counts = pd.cut(scores, bins=bins_edges, labels=bins_labels, include_lowest=True).value_counts().sort_index()
182
  score_percentages = (score_counts / len(scores) * 100).round(1)
183
 
 
189
  st.dataframe(result_df)
190
 
191
  except Exception as e:
192
+ st.warning(f"๊ตฌ๊ฐ„ ๋ถ„์„ ์˜ค๋ฅ˜: {e}")
193
 
194
  def main():
195
  st.set_page_config(
196
  page_title="ํ•™์ƒ ์ ์ˆ˜ ๋ถ„์„ ๋„๊ตฌ",
197
  page_icon="๐Ÿ“Š",
198
+ layout="wide"
 
199
  )
200
 
201
  # ์ œ๋ชฉ
 
204
 
205
  # ํฐํŠธ ์ •๋ณด ํ‘œ์‹œ
206
  with st.expander("๐Ÿ”ง ํฐํŠธ ์„ค์ • ์ •๋ณด"):
207
+ st.write(f"**ํ˜„์žฌ ํฐํŠธ**: {plt.rcParams['font.family']}")
208
  st.write(f"**ํฐํŠธ ๊ฒฝ๋กœ**: {FONT_PATH if FONT_PATH else '์‹œ์Šคํ…œ ๊ธฐ๋ณธ'}")
209
 
210
+ # ๊ฐ„๋‹จํ•œ ํฐํŠธ ํ…Œ์ŠคํŠธ
211
  if st.button("ํฐํŠธ ํ…Œ์ŠคํŠธ"):
212
+ fig, ax = plt.subplots(figsize=(6, 2))
213
+ ax.text(0.5, 0.5, 'ํ•œ๊ธ€ ํฐํŠธ ํ…Œ์ŠคํŠธ: ์ ์ˆ˜ ๋ถ„ํฌ ๋ถ„์„',
214
+ ha='center', va='center', fontsize=14)
215
+ ax.set_xlim(0, 1)
216
+ ax.set_ylim(0, 1)
217
+ ax.axis('off')
218
+ st.pyplot(fig)
219
+ plt.close(fig)
 
 
 
 
 
 
220
 
221
  st.markdown("---")
222