soojeongcrystal commited on
Commit
4b5031a
·
verified ·
1 Parent(s): 177ec8c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +209 -206
app.py CHANGED
@@ -9,13 +9,12 @@ import re
9
  import os
10
  import altair as alt
11
  import colorsys
 
 
 
12
 
13
- # Streamlit 페이지 설정 (반드시 첫 번째 st 명령어여야 함)
14
- st.set_page_config(
15
- layout="wide",
16
- page_title="📊 토픽모델링 Tool for SK",
17
- page_icon="📊"
18
- )
19
 
20
  # KoNLPy 형태소 분석기 초기화
21
  @st.cache_resource
@@ -29,251 +28,255 @@ default_stop_words = ['이', '그', '저', '것', '수', '등', '들', '및', '
29
 
30
  @st.cache_data
31
  def preprocess_text(text, stop_words):
32
- text = re.sub(r'[^가-힣\s]', '', text)
33
  nouns = okt.nouns(text)
34
  processed = [word for word in nouns if word not in stop_words and len(word) > 1]
35
  return ' '.join(processed)
36
 
37
- # HSL 색상 생성
38
  def generate_colors(n):
39
  HSV_tuples = [(x * 1.0 / n, 0.5, 0.9) for x in range(n)]
40
  return ['#%02x%02x%02x' % tuple(int(x*255) for x in colorsys.hsv_to_rgb(*hsv)) for hsv in HSV_tuples]
41
 
42
- # CSS 스타일 정의
43
- st.markdown("""
44
- <style>
45
- .header {
46
- position: fixed;
47
- top: 0;
48
- left: 0;
49
- right: 0;
50
- background-color: #f1f1f1;
51
- color: black;
52
- padding: 10px 0;
53
- font-size: 16px;
54
- z-index: 999;
55
- }
56
- .header-content {
57
- display: flex;
58
- justify-content: flex-end;
59
- padding-right: 20px;
60
- }
61
- .main-content {
62
- margin-top: 50px;
63
- padding: 20px;
64
- }
65
- .stDataFrame {
66
- width: 100%;
67
- }
68
- .stDataFrame table {
69
- width: 100%;
70
- }
71
- .stDataFrame th {
72
- font-size: 14px;
73
- }
74
- .stDataFrame td {
75
- font-size: 12px;
76
- }
77
- </style>
78
- """, unsafe_allow_html=True)
79
 
80
- # 헤더 렌더링
 
 
 
 
 
 
 
 
 
 
 
81
  st.markdown("""
82
- <div class="header">
83
- <div class="header-content">
84
- mySUNI 행복 College 행복담당조직 Meet-Up
85
- </div>
86
  </div>
87
  """, unsafe_allow_html=True)
88
 
89
- # 메인 컨텐츠
90
- st.markdown('<div class="main-content">', unsafe_allow_html=True)
91
  st.title('토픽모델링 Tool for SK')
92
 
93
- # 사이드바 내용
94
  with st.sidebar:
95
  st.header('설정')
96
 
97
- # 최소화된 사이드바 상태에서도 보이는
98
- st.markdown("⚙️ 설정을 조정하려면 사이드바확장하세요.")
 
 
99
 
100
- # 확장된 사이드바 상태에서 보이는
101
- with st.expander("상세 설정", expanded=True):
102
- # Claude API 설정
103
- api_key = st.text_input("Claude API 키를 입력하세요", type="password")
104
- if not api_key:
105
- api_key = os.environ.get("ANTHROPIC_API_KEY")
106
-
107
- # 불용어 설정
108
- stop_words_input = st.text_area("불용어 목록 (쉼표로 구분)", ', '.join(default_stop_words))
109
- stop_words = [word.strip() for word in stop_words_input.split(',') if word.strip()]
110
-
111
- # 파일 업로드
112
- uploaded_file = st.file_uploader("CSV 파일을 업로드하세요", type="csv")
113
 
114
- if uploaded_file is not None:
115
- # 텍스트 컬럼 선택
116
  df = pd.read_csv(uploaded_file)
117
  text_column = st.selectbox("텍스트 컬럼을 선택하세요", df.columns)
118
-
119
- # 토픽 수 선택
120
  num_topics = st.slider("토픽 수를 선택하세요", 2, 20, 5)
121
-
122
  if st.button("토픽 모델링 실행"):
123
  st.session_state.run_analysis = True
124
  else:
125
  st.session_state.run_analysis = False
 
 
 
 
126
 
 
127
  if 'run_analysis' in st.session_state and st.session_state.run_analysis:
128
  if uploaded_file is not None:
129
- # 데이터 읽기
130
- df = pd.read_csv(uploaded_file)
131
- st.write("데이터 미리보기:")
132
- st.write(df.head())
 
133
 
134
- # 텍스트 전처리
135
- with st.spinner("텍스트 전처리 중..."):
136
- df['processed_text'] = df[text_column].apply(lambda x: preprocess_text(x, stop_words))
137
 
138
- # 토픽 모델링
139
- with st.spinner("토픽 모델링 실행 중..."):
140
- vectorizer = CountVectorizer(max_df=0.95, min_df=2)
141
- doc_term_matrix = vectorizer.fit_transform(df['processed_text'])
142
 
143
- lda = LatentDirichletAllocation(n_components=num_topics, random_state=42)
144
- lda_output = lda.fit_transform(doc_term_matrix)
145
 
146
- # TF-IDF 계산
147
- tfidf_vectorizer = TfidfVectorizer(max_df=0.95, min_df=2)
148
- tfidf_matrix = tfidf_vectorizer.fit_transform(df['processed_text'])
149
 
150
- # 결과 출력
151
- st.header("토픽 모델링 결과")
152
- feature_names = vectorizer.get_feature_names_out()
153
 
154
- topic_results = []
155
 
156
- for idx, topic in enumerate(lda.components_):
157
- st.subheader(f"토픽 {idx + 1}")
158
-
159
- # LDA 상위 단어와 TF-IDF 상위 단어를 나란히 표시
160
- col1, col2 = st.columns(2)
161
-
162
- with col1:
163
- # LDA 상위 단어 테이블
164
- lda_top_words = [(feature_names[i], topic[i]) for i in topic.argsort()[:-11:-1]]
165
- df_lda = pd.DataFrame(lda_top_words, columns=['단어', 'LDA 점수'])
166
- st.subheader("LDA 상위 단어")
167
- st.dataframe(df_lda.style.format({'LDA 점수': '{:.4f}'}), height=400)
168
-
169
- with col2:
170
- # 토픽별 TF-IDF 계산
171
- topic_docs = lda_output[:, idx].argsort()[::-1][:100] # 상위 100개 문서 선택
172
- topic_tfidf = tfidf_matrix[topic_docs].mean(axis=0).A1
173
- tfidf_top_words = [(feature_names[i], topic_tfidf[i]) for i in topic_tfidf.argsort()[:-11:-1]]
174
- df_tfidf = pd.DataFrame(tfidf_top_words, columns=['단어', 'TF-IDF'])
175
- st.subheader("TF-IDF 상위 단어")
176
- st.dataframe(df_tfidf.style.format({'TF-IDF': '{:.4f}'}), height=400)
177
-
178
- topic_results.append({
179
- 'topic_num': idx + 1,
180
- 'lda_words': [word for word, _ in lda_top_words],
181
- 'tfidf_words': [word for word, _ in tfidf_top_words],
182
- 'weight': lda_output[:, idx].mean() * 100 # 퍼센트로 변환
183
- })
184
 
185
- # 토픽 비중 그래프
186
- st.header("토픽 비중 그래프")
187
- df_weights = pd.DataFrame({
188
- '토픽': [f'토픽 {i+1}' for i in range(num_topics)],
189
- '비중': [result['weight'] for result in topic_results]
190
- })
191
 
192
- colors = generate_colors(num_topics)
193
 
194
- # 차트 생성
195
- base = alt.Chart(df_weights).encode(
196
- x=alt.X('토픽:N', axis=alt.Axis(labelAngle=0)),
197
- y=alt.Y('비중:Q', axis=alt.Axis(format=',.1f'))
198
- )
199
 
200
- bars = base.mark_bar().encode(
201
- color=alt.Color('토픽:N', scale=alt.Scale(range=colors))
202
- )
203
 
204
- text = base.mark_text(
205
- align='center',
206
- baseline='middle',
207
- dy=-10 # 텍스트를 약간 위로 이동
208
- ).encode(
209
- text=alt.Text('비중:Q', format='.1f')
210
- )
211
 
212
- chart = (bars + text).properties(
213
- width=600,
214
- height=400,
215
- title='문서 내 토픽 비중 (%)'
216
- ).configure_axis(
217
- labelFontSize=12,
218
- titleFontSize=14
219
- ).configure_title(
220
- fontSize=16,
221
- font='Arial',
222
- anchor='middle',
223
- color='gray'
224
- )
225
 
226
- st.altair_chart(chart, use_container_width=True)
227
 
 
 
 
 
228
 
229
- # Claude API를 사용하여 토픽 해석
230
- if api_key:
231
- anthropic = Anthropic(api_key=api_key)
232
-
233
- st.header("Claude의 토픽 해석")
234
- with st.spinner("토픽 해석 중..."):
235
- prompt = f"""{HUMAN_PROMPT} 다음은 LDA 토픽 모델링 결과로 나온 각 토픽의 정보입니다. 이를 바탕으로 전체 토픽을 종합적으로 해석해주세요:
236
-
237
- {", ".join([f"토픽 {info['topic_num']} (비중: {info['weight']:.1f}%)" for info in topic_results])}
238
-
239
- 각 토픽의 주요 단어:
240
- """
241
- for info in topic_results:
242
- prompt += f"""
243
- 토픽 {info['topic_num']} (비중: {info['weight']:.1f}%):
244
- LDA 상위 단어: {', '.join(info['lda_words'])}
245
- TF-IDF 상위 단어: {', '.join(info['tfidf_words'])}
246
- """
247
 
248
- prompt += """
249
- 정보를 바탕으로 다음 형식에 맞춰 답변주세요:
250
-
251
- 1. 전체 문서의 주제 요약 (3-4문장):
252
- [여기에 전체 문서의 주제를 종합적으로 설명해주세요.토픽 비중 고려하여 중요도를 반영해주세요.]
253
-
254
- 2. 각 토픽 요:
255
- [각 토픽에 대해 다음 형식으로 요약해주세요]
256
- - 토픽 [번호] ([LDA 기반 토픽명]): [비중]%
257
- 토픽명 설명: [토픽명이 이렇게 지어진 이유를 1-2문장으로 설명해주세요. LDA와 TF-IDF 상위 단어들이 어떻게 이 토픽명과 연관되는지 설명하세요.]
258
- 토픽 설명: [1-2문장으로 토픽의 전반적인 내용을 설명해주세요.]
259
- 예시 응답: "[이 토픽과 관련된 가상의 구성원 발언 예시를 넣어주세요. -한다 체를 지켜주세요]"
260
-
261
- 주의사항:
262
- 1. 토픽명은 "LDA 기반 [구체적인 토픽명]" 형식으로 작성해주세요. 예를 들어, "구성원들의 성장과 개인적인 역량개발 노력" 또는 "리더들의 노력과 조직의 전폭적인 지원" 등입니다.
263
- 2. 토픽명은 단순히 단어를 나열하는 것이 아니라, 토픽의 핵심 주제나 의미를 잘 나타내는 구체적인 문구로 만들어주세요.
264
- 3. 토픽명 설명에서는 그러한 토픽명이 선택되었는지, LDA와 TF-IDF 상위 단어들과의 연관성을 설명해주세요.
265
-
266
- 위 형식에 맞춰 답변해주세요. 사용자가 쉽게 복사하여 사용할 수 있도록 간결하고 명확하게 작성해."""
267
-
268
- response = anthropic.completions.create(
269
- model="claude-2.1",
270
- max_tokens_to_sample=3000,
271
- prompt=f"{prompt}\n\n{AI_PROMPT}",
272
- )
273
-
274
- st.subheader("토픽 모델링 종합 결과")
275
- st.text_area("결과를 복사하여 사용하세요:", value=response.completion, height=500)
276
- else:
277
- st.warning("Claude API 키가 설정되지 않았습니다. https://console.anthropic.com/settings/keys 접속하여 API 키를급받으시면 토픽명해석을 제공받으실 있습니다.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
 
279
- st.markdown('</div>', unsafe_allow_html=True)
 
 
 
 
 
 
 
 
9
  import os
10
  import altair as alt
11
  import colorsys
12
+ import networkx as nx
13
+ from pyvis.network import Network
14
+ import streamlit.components.v1 as components
15
 
16
+ # Streamlit 페이지 설정
17
+ st.set_page_config(layout="wide", page_title="📊 토픽모델링 Tool for SK", page_icon="📊")
 
 
 
 
18
 
19
  # KoNLPy 형태소 분석기 초기화
20
  @st.cache_resource
 
28
 
29
  @st.cache_data
30
  def preprocess_text(text, stop_words):
31
+ text = re.sub(r'[^가-힣\s]', '', str(text))
32
  nouns = okt.nouns(text)
33
  processed = [word for word in nouns if word not in stop_words and len(word) > 1]
34
  return ' '.join(processed)
35
 
 
36
  def generate_colors(n):
37
  HSV_tuples = [(x * 1.0 / n, 0.5, 0.9) for x in range(n)]
38
  return ['#%02x%02x%02x' % tuple(int(x*255) for x in colorsys.hsv_to_rgb(*hsv)) for hsv in HSV_tuples]
39
 
40
+ # 네트워크 그래프 생성 함수
41
+ def create_network_graph(topic_results, num_words=30):
42
+ G = nx.Graph()
43
+ colors = generate_colors(len(topic_results))
44
+
45
+ for idx, topic in enumerate(topic_results):
46
+ words = topic['lda_words'][:num_words]
47
+ color = colors[idx]
48
+
49
+ # 노드 추가
50
+ for word in words:
51
+ if not G.has_node(word):
52
+ G.add_node(word, color=color, size=10)
53
+
54
+ # 엣지 추가
55
+ for i in range(len(words)):
56
+ for j in range(i+1, len(words)):
57
+ if G.has_edge(words[i], words[j]):
58
+ G[words[i]][words[j]]['weight'] += 1
59
+ else:
60
+ G.add_edge(words[i], words[j], weight=1)
61
+
62
+ return G
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
+ # 네트워크 그래프 시각화 함수
65
+ def visualize_network(G):
66
+ nt = Network(height="500px", width="100%", bgcolor="#222222", font_color="white")
67
+ nt.from_nx(G)
68
+ nt.show("network.html")
69
+
70
+ # HTML 파일을 읽어 Streamlit에 표시
71
+ with open("network.html", 'r', encoding='utf-8') as f:
72
+ html_string = f.read()
73
+ components.html(html_string, height=500)
74
+
75
+ # 헤더
76
  st.markdown("""
77
+ <div style="background-color: #f1f1f1; padding: 10px; text-align: right;">
78
+ mySUNI 행복 College 행복담당조직 Meet-Up
 
 
79
  </div>
80
  """, unsafe_allow_html=True)
81
 
 
 
82
  st.title('토픽모델링 Tool for SK')
83
 
84
+ # 사이드바 설정
85
  with st.sidebar:
86
  st.header('설정')
87
 
88
+ # Claude API
89
+ api_key = st.text_input("Claude API 입력하세요", type="password")
90
+ if not api_key:
91
+ api_key = os.environ.get("ANTHROPIC_API_KEY")
92
 
93
+ # 불용어
94
+ stop_words_input = st.text_area("불용어 목록 (쉼표로 구분)", ', '.join(default_stop_words))
95
+ stop_words = [word.strip() for word in stop_words_input.split(',') if word.strip()]
96
+
97
+ # 파일 업로드
98
+ uploaded_file = st.file_uploader("CSV 파일을 업로드하세요", type="csv")
 
 
 
 
 
 
 
99
 
100
+ if uploaded_file is not None:
101
+ try:
102
  df = pd.read_csv(uploaded_file)
103
  text_column = st.selectbox("텍스트 컬럼을 선택하세요", df.columns)
 
 
104
  num_topics = st.slider("토픽 수를 선택하세요", 2, 20, 5)
 
105
  if st.button("토픽 모델링 실행"):
106
  st.session_state.run_analysis = True
107
  else:
108
  st.session_state.run_analysis = False
109
+ except pd.errors.EmptyDataError:
110
+ st.error("업로드된 CSV 파일이 비어있거나 올바르지 않습니다. 다시 확인해주세요.")
111
+ except Exception as e:
112
+ st.error(f"파일을 읽는 중 오류가 발생했습니다: {str(e)}")
113
 
114
+ # 메인 컨텐츠
115
  if 'run_analysis' in st.session_state and st.session_state.run_analysis:
116
  if uploaded_file is not None:
117
+ try:
118
+ # 데이터 읽기
119
+ df = pd.read_csv(uploaded_file)
120
+ st.write("데이터 미리보기:")
121
+ st.write(df.head())
122
 
123
+ # 텍스트 전처리
124
+ with st.spinner("텍스트 전처리 중..."):
125
+ df['processed_text'] = df[text_column].apply(lambda x: preprocess_text(x, stop_words))
126
 
127
+ # 토픽 모델링
128
+ with st.spinner("토픽 모델링 실행 중..."):
129
+ vectorizer = CountVectorizer(max_df=0.95, min_df=2)
130
+ doc_term_matrix = vectorizer.fit_transform(df['processed_text'])
131
 
132
+ lda = LatentDirichletAllocation(n_components=num_topics, random_state=42)
133
+ lda_output = lda.fit_transform(doc_term_matrix)
134
 
135
+ # TF-IDF 계산
136
+ tfidf_vectorizer = TfidfVectorizer(max_df=0.95, min_df=2)
137
+ tfidf_matrix = tfidf_vectorizer.fit_transform(df['processed_text'])
138
 
139
+ # 결과 출력
140
+ st.header("토픽 모델링 결과")
141
+ feature_names = vectorizer.get_feature_names_out()
142
 
143
+ topic_results = []
144
 
145
+ for idx, topic in enumerate(lda.components_):
146
+ st.subheader(f"토픽 {idx + 1}")
147
+
148
+ # LDA 상위 단어와 TF-IDF 상위 단어를 나란히 표시
149
+ col1, col2 = st.columns(2)
150
+
151
+ with col1:
152
+ # LDA 상위 단어 테이블
153
+ lda_top_words = [(feature_names[i], topic[i]) for i in topic.argsort()[:-11:-1]]
154
+ df_lda = pd.DataFrame(lda_top_words, columns=['단어', 'LDA 점수'])
155
+ st.subheader("LDA 상위 단어")
156
+ st.dataframe(df_lda.style.format({'LDA 점수': '{:.4f}'}), height=400)
157
+
158
+ with col2:
159
+ # 토픽별 TF-IDF 계산
160
+ topic_docs = lda_output[:, idx].argsort()[::-1][:100] # 상위 100개 문서 선택
161
+ topic_tfidf = tfidf_matrix[topic_docs].mean(axis=0).A1
162
+ tfidf_top_words = [(feature_names[i], topic_tfidf[i]) for i in topic_tfidf.argsort()[:-11:-1]]
163
+ df_tfidf = pd.DataFrame(tfidf_top_words, columns=['단어', 'TF-IDF'])
164
+ st.subheader("TF-IDF 상위 단어")
165
+ st.dataframe(df_tfidf.style.format({'TF-IDF': '{:.4f}'}), height=400)
166
+
167
+ topic_results.append({
168
+ 'topic_num': idx + 1,
169
+ 'lda_words': [word for word, _ in lda_top_words],
170
+ 'tfidf_words': [word for word, _ in tfidf_top_words],
171
+ 'weight': lda_output[:, idx].mean() * 100 # 퍼센트로 변환
172
+ })
173
 
174
+ # 토픽 비중 그래프
175
+ st.header("토픽 비중 그래프")
176
+ df_weights = pd.DataFrame({
177
+ '토픽': [f'토픽 {i+1}' for i in range(num_topics)],
178
+ '비중': [result['weight'] for result in topic_results]
179
+ })
180
 
181
+ colors = generate_colors(num_topics)
182
 
183
+ # 차트 생성
184
+ base = alt.Chart(df_weights).encode(
185
+ x=alt.X('토픽:N', axis=alt.Axis(labelAngle=0)),
186
+ y=alt.Y('비중:Q', axis=alt.Axis(format=',.1f'))
187
+ )
188
 
189
+ bars = base.mark_bar().encode(
190
+ color=alt.Color('토픽:N', scale=alt.Scale(range=colors))
191
+ )
192
 
193
+ text = base.mark_text(
194
+ align='center',
195
+ baseline='middle',
196
+ dy=-10 # 텍스트를 약간 위로 이동
197
+ ).encode(
198
+ text=alt.Text('비중:Q', format='.1f')
199
+ )
200
 
201
+ chart = (bars + text).properties(
202
+ width=600,
203
+ height=400,
204
+ title='문서 내 토픽 비중 (%)'
205
+ ).configure_axis(
206
+ labelFontSize=12,
207
+ titleFontSize=14
208
+ ).configure_title(
209
+ fontSize=16,
210
+ font='Arial',
211
+ anchor='middle',
212
+ color='gray'
213
+ )
214
 
215
+ st.altair_chart(chart, use_container_width=True)
216
 
217
+ # 네트워크 그래프 생성 및 시각화
218
+ st.header("토픽 단어 네트워크 그래프")
219
+ G = create_network_graph(topic_results)
220
+ visualize_network(G)
221
 
222
+ # Claude API를 사용하여 토픽 해석
223
+ if api_key:
224
+ anthropic = Anthropic(api_key=api_key)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
 
226
+ st.header("Claude의 토픽 해석")
227
+ with st.spinner("토픽석 중..."):
228
+ prompt = f"""{HUMAN_PROMPT} 다음은 LDA 토픽 모델링 결과로 나온 각 토픽의 정보입니다. 이를 바탕으로 전체 토픽을 종합적으로 해석해주세요:
229
+
230
+ {", ".join([f"토픽 {info['topic_num']} (비중: {info['weight']:.1f}%)" for info in topic_results])}
231
+
232
+ 각 토픽 단어:
233
+ """
234
+ for info in topic_results:
235
+ prompt += f"""
236
+ 토픽 {info['topic_num']} (비중: {info['weight']:.1f}%):
237
+ LDA 단어: {', '.join(info['lda_words'])}
238
+ TF-IDF 상위 단어: {', '.join(info['tfidf_words'])}
239
+ """
240
+
241
+ prompt += """
242
+ 정보를 바탕으로 다음 형식에 맞춰 답변해주세요:
243
+
244
+ 1. 전체 문서의약 (3-4문장):
245
+ [여기에 전체 문서의 주제를 종합적으로 설명해주세요. 각 토픽의 비중을 고려하여 중요도를 반영해주세요.]
246
+
247
+ 2. 각 토픽 요약:
248
+ [각 토픽에 대해 다음 형식으로 요약해주세요]
249
+ - 토픽 [번호] ([LDA 기반 토픽명]): [비중]%
250
+ • 토픽명 설명: [토픽명이 이렇게 지어진 이유를 1-2문장으로 설명해주세요. LDA와 TF-IDF 상위 단어들이 어떻게 이 토픽명과 연관되는지 설명하세요.]
251
+ • 토픽 설명: [1-2문장으로 토픽의 전반적인 내용을 설명해주세요.]
252
+ • 가상의 예시 응답: "[이 토픽 관련된 가상의 구성원 발언 예시를 넣어주세요. -한다 체를 지켜주세요]"
253
+
254
+ 주의사항:
255
+ 1. 토픽명은 "LDA 기반 [구체적인 토픽명]" 형식으로 작성해주세요. 예를 들어, "구성원들의 성장과 개인적인 역량개노력" 또는 "리더들의 노력조직의 전폭적인 지원" 등입니다.
256
+ 2. 토픽명은 단순히 단어를 나열하는 것이 아니라, 토픽의 핵심 주제나 의미를 잘 나타내는 구체적인 문구로 만들어주세요.
257
+ 3. 토픽명 설명에서는 왜 그러한 토픽명이 선택되었는지, LDA와 TF-IDF 상위 단어들과의 연관성을 설명해주세요.
258
+
259
+ 위 형식에 맞춰 답변해주세요. 사용자가 쉽게 복사하여 사용할 수 있도록 간결하고 명확하게 작성해주세요."""
260
+
261
+ response = anthropic.completions.create(
262
+ model="claude-2.1",
263
+ max_tokens_to_sample=3000,
264
+ prompt=f"{prompt}\n\n{AI_PROMPT}",response = anthropic.completions.create(
265
+ model="claude-2.1",
266
+ max_tokens_to_sample=3000,
267
+ prompt=f"{prompt}\n\n{AI_PROMPT}",
268
+ )
269
+
270
+ st.subheader("토픽 모델링 종합 결과")
271
+ st.text_area("결과를 복사하여 사용하세요:", value=response.completion, height=500)
272
+ else:
273
+ st.warning("Claude API 키가 설정되지 않았습니다. https://console.anthropic.com/settings/keys 에 접속하여 API 키를 발급받으시면 토픽명과 해석을 제공받으실 수 있습니다.")
274
 
275
+ except Exception as e:
276
+ st.error(f"분석 중 오류가 발생했습니다: {str(e)}")
277
+
278
+ # 페이지 하단에 추가 정보나 설명을 넣을 수 있습니다.
279
+ st.markdown("""
280
+ ---
281
+ © 2024 SK mySUNI 행복 College. Crystal_B . All rights reserved.
282
+ """)