soojeongcrystal commited on
Commit
75fbae2
·
verified ·
1 Parent(s): 11382a5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +133 -123
app.py CHANGED
@@ -286,137 +286,147 @@ st.markdown("""
286
 
287
  st.markdown('<h1 class="small-title">📊토픽모델링 for SK</h1>', unsafe_allow_html=True)
288
 
289
- # 3개의 탭 생성
290
- tab1, tab2, tab3 = st.tabs(["토픽 수 결정", "전체 분석", "조건부 분석"])
291
-
292
- # 탭 1: 토픽 수 결정
293
- with tab1:
294
- st.header("토픽 수 결정")
295
 
296
- if 'df' not in st.session_state:
297
- uploaded_file = st.file_uploader("CSV 파일을 업로드하세요", type="csv")
298
- if uploaded_file:
299
- df = pd.read_csv(uploaded_file)
300
- st.session_state['df'] = df
301
- st.write(df.head())
302
-
303
- if 'df' in st.session_state:
304
- df = st.session_state['df']
305
- text_column = st.selectbox("텍스트 컬럼을 선택하세요", df.columns)
306
- topic_range = st.slider("토픽 수 범위 선택", min_value=2, max_value=20, value=(2, 10))
307
-
308
- if st.button("Perplexity 및 Coherence 계산"):
309
- perplexities, coherences = calculate_perplexity_coherence(df, text_column, default_stop_words, range(topic_range[0], topic_range[1] + 1))
310
- plot_perplexity_coherence(range(topic_range[0], topic_range[1] + 1), perplexities, coherences)
311
-
312
- # 탭 2: 전체 분석
313
- with tab2:
314
- st.header("전체 분석")
315
 
316
- if 'df' in st.session_state:
317
- df = st.session_state['df']
318
- text_column = st.selectbox("텍스트 컬럼을 선택하세요", df.columns, key="전체분석")
319
- num_topics = st.slider("토픽 수를 선택하세요", 2, 20, 5)
320
-
321
- if st.button("토픽 모델링 실행"):
322
- topic_results, lda, lda_output, tfidf_matrix, feature_names = perform_topic_modeling(df, text_column, num_topics, default_stop_words)
323
-
324
- # 토픽 요약
325
- display_topic_summary(topic_results)
326
-
327
- # 토픽 비중 그래프
328
- st.header("토픽 비중 그래프")
329
- df_weights = pd.DataFrame({
330
- '토픽': [f'토픽 {i+1}' for i in range(num_topics)],
331
- '비중': [result['weight'] for result in topic_results]
332
- })
333
-
334
- colors = generate_colors(num_topics)
335
-
336
- chart = alt.Chart(df_weights).mark_bar().encode(
337
- x=alt.X('토픽:N', axis=alt.Axis(labelAngle=0)),
338
- y=alt.Y('비중:Q', axis=alt.Axis(format=',.1f')),
339
- color=alt.Color('토픽:N', scale=alt.Scale(range=colors))
340
- ).properties(
341
- width=600,
342
- height=400,
343
- title='문서 내 토픽 비중 (%)'
344
- )
345
-
346
- text = chart.mark_text(
347
- align='center',
348
- baseline='bottom',
349
- dy=-5
350
- ).encode(
351
- text=alt.Text('비중:Q', format='.1f')
352
- )
353
-
354
- st.altair_chart(chart + text, use_container_width=True)
355
-
356
- # 네트워크 그래프
357
- G = create_network_graph(topic_results, num_words=20)
358
- img_bytes = plot_network_graph(G)
359
- st.image(img_bytes, caption="토픽별 상위 20개 단어 네트워크", use_column_width=True)
360
-
361
- # 토픽 할당 데이터 다운로드
362
- df['topic'] = lda_output.argmax(axis=1) + 1
363
- download_topic_assignment(df)
364
-
365
- # 종합 해석
366
- api_key = st.text_input("Claude API 키를 입력하세요", type="password")
367
- if api_key:
368
- st.subheader("토픽 종합 해석")
369
- with st.spinner("Claude AI로 토픽 해석 중..."):
370
- interpretation = interpret_topics_full(api_key, topic_results)
371
- st.text_area("해석 결과", value=interpretation, height=300)
372
-
373
- # 탭 3: 조건부 분석
374
- with tab3:
375
- st.header("조건부 분석")
376
 
377
- if 'df' in st.session_state:
378
- df = st.session_state['df']
379
- text_column = st.selectbox("텍스트 컬럼을 선택하세요", df.columns, key="조건부분석")
380
- condition_column = st.selectbox("조건부 분석에 사용할 변수를 선택하세요", df.columns)
381
-
382
- if pd.api.types.is_numeric_dtype(df[condition_column]):
383
- analysis_method = st.radio("분석 방법 선택", ["범위 선택", "임계값 기준"])
384
-
385
- if analysis_method == "범위 선택":
386
- min_val, max_val = df[condition_column].min(), df[condition_column].max()
387
- condition = st.slider(f"{condition_column} 범위 선택", float(min_val), float(max_val), (float(min_val), float(max_val)))
388
- else:
389
- threshold = st.number_input(f"{condition_column} 임계값 설정", min_value=float(df[condition_column].min()), max_value=float(df[condition_column].max()), value=float((df[condition_column].min() + df[condition_column].max()) / 2))
390
- comparison = st.radio("비교 기준", ["이상", "이하"])
391
- condition = (comparison, threshold)
392
- else:
393
- unique_values = df[condition_column].unique()
394
- condition = st.multiselect(f"{condition_column} 값 선택", unique_values, default=unique_values)
395
 
396
- num_topics = st.slider("토픽 수를 선택하세요", 2, 20, 5, key="조건부토픽수")
 
 
397
 
398
- if st.button("조건부 토픽 모델링 실행"):
399
- if isinstance(condition, tuple):
400
- if condition[0] == "이상":
401
- filtered_df = df[df[condition_column] >= condition[1]]
402
- else:
403
- filtered_df = df[df[condition_column] <= condition[1]]
404
- else:
405
- filtered_df = df[df[condition_column].between(*condition)] if isinstance(condition, tuple) else df[df[condition_column].isin(condition)]
406
 
407
- topic_results, lda, lda_output, tfidf_matrix, feature_names = perform_topic_modeling(filtered_df, text_column, num_topics, default_stop_words)
 
 
 
 
 
 
 
 
 
 
 
 
408
 
409
- # 토픽 요약
410
- display_topic_summary(topic_results)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
411
 
412
- # 네트워크 그래프
413
- G = create_network_graph(topic_results, num_words=20)
414
- img_bytes = plot_network_graph(G)
415
- st.image(img_bytes, caption="토픽별 상위 20개 단어 네트워크", use_column_width=True)
416
-
417
- # 토픽 할당 데이터 다운로드
418
- filtered_df['topic'] = lda_output.argmax(axis=1) + 1
419
- download_topic_assignment(filtered_df)
 
 
 
 
 
 
 
 
 
 
 
420
 
421
  # 푸터 추가
422
  st.markdown("""
 
286
 
287
  st.markdown('<h1 class="small-title">📊토픽모델링 for SK</h1>', unsafe_allow_html=True)
288
 
289
+ # 사이드바 설정
290
+ with st.sidebar:
291
+ st.header('설정하기')
 
 
 
292
 
293
+ api_key = st.text_input("Claude API 키를 입력하세요", type="password")
294
+ if not api_key:
295
+ api_key = os.environ.get("ANTHROPIC_API_KEY")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296
 
297
+ st.caption("Claude API가 있으면 토픽 종합 해석까지 가능합니다. 공백으로 두면 기본적인 결과만 나옵니다.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
 
299
+ stop_words_input = st.text_area("불용어 목록 (쉼표로 구분)", ', '.join(default_stop_words))
300
+ stop_words = [word.strip() for word in stop_words_input.split(',') if word.strip()]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
 
302
+ st.caption("결과를 보고 업데이트해주세요.")
303
+
304
+ uploaded_file = st.file_uploader("CSV 파일을 업로드하세요", type="csv")
305
 
306
+ st.caption("csv-UTF 형식을 사용해주세요!")
 
 
 
 
 
 
 
307
 
308
+ # 데이터 로드 초기 설정
309
+ if uploaded_file is not None:
310
+ try:
311
+ df = pd.read_csv(uploaded_file)
312
+ if df.empty:
313
+ st.error("CSV 파일에 데이터가 없습니다.")
314
+ else:
315
+ st.success("파일이 성공적으로 업로드되었습니다.")
316
+ st.subheader("데이터 미리보기")
317
+ st.write(df.head())
318
+
319
+ text_column = st.selectbox("텍스트 컬럼을 선택하세요", df.columns)
320
+ num_topics = st.slider("토픽 수를 선택하세요", 2, 20, 5)
321
 
322
+ # 분석 방법 선택
323
+ analysis_type = st.radio("분석 방법 선택", ["전체 분석", "조건부 분석"])
324
+
325
+ # 토픽 수 검토 탭
326
+ st.sidebar.header('토픽 수 검토')
327
+ topic_range = st.sidebar.slider("토픽 수 범위 선택", min_value=2, max_value=20, value=(2, 10))
328
+
329
+ if st.sidebar.button("Perplexity 및 Coherence 계산"):
330
+ perplexities, coherences = calculate_perplexity_coherence(df, text_column, stop_words, range(topic_range[0], topic_range[1] + 1))
331
+ plot_perplexity_coherence(range(topic_range[0], topic_range[1] + 1), perplexities, coherences)
332
+
333
+ if analysis_type == "전체 분석":
334
+ st.header("전체 데이터 분석 결과")
335
+
336
+ with st.spinner("토픽 모델링 실행 중..."):
337
+ topic_results, lda, lda_output, tfidf_matrix, feature_names = perform_topic_modeling(df, text_column, num_topics, stop_words)
338
+
339
+ # 토픽 요약
340
+ display_topic_summary(topic_results)
341
+
342
+ # 토픽 비중 그래프
343
+ st.header("토픽 비중 그래프")
344
+ df_weights = pd.DataFrame({
345
+ '토픽': [f'토픽 {i+1}' for i in range(num_topics)],
346
+ '비중': [result['weight'] for result in topic_results]
347
+ })
348
+
349
+ colors = generate_colors(num_topics)
350
+
351
+ chart = alt.Chart(df_weights).mark_bar().encode(
352
+ x=alt.X('토픽:N', axis=alt.Axis(labelAngle=0)),
353
+ y=alt.Y('비중:Q', axis=alt.Axis(format=',.1f')),
354
+ color=alt.Color('토픽:N', scale=alt.Scale(range=colors))
355
+ ).properties(
356
+ width=600,
357
+ height=400,
358
+ title='문서 내 토픽 비중 (%)'
359
+ )
360
+
361
+ text = chart.mark_text(
362
+ align='center',
363
+ baseline='bottom',
364
+ dy=-5
365
+ ).encode(
366
+ text=alt.Text('비중:Q', format='.1f')
367
+ )
368
+
369
+ st.altair_chart(chart + text, use_container_width=True)
370
+
371
+ # 네트워크 그래프
372
+ st.header("토픽 단어 네트워크 그래프")
373
+ G = create_network_graph(topic_results, num_words=20)
374
+ img_bytes = plot_network_graph(G)
375
+ st.image(img_bytes, caption="토픽별 상위 20개 단어 네트워크", use_column_width=True)
376
+
377
+ # 토픽 할당 데이터 다운로드
378
+ df['topic'] = lda_output.argmax(axis=1) + 1
379
+ download_topic_assignment(df)
380
+
381
+ # 종합 해석
382
+ if api_key:
383
+ st.subheader("토픽 종합 해석")
384
+ with st.spinner("Claude AI로 토픽 해석 중..."):
385
+ interpretation = interpret_topics_full(api_key, topic_results)
386
+ st.text_area("해석 결과", value=interpretation, height=300)
387
+
388
+ elif analysis_type == "조건부 분석":
389
+ st.header("조건부 분석 결과")
390
+
391
+ condition_column = st.selectbox("조건부 분석에 사용할 변수를 선택하세요", df.columns)
392
+ if pd.api.types.is_numeric_dtype(df[condition_column]):
393
+ min_val, max_val = df[condition_column].min(), df[condition_column].max()
394
+ st.write(f"{condition_column}의 범위: {min_val:.2f} ~ {max_val:.2f}")
395
+
396
+ analysis_method = st.radio("분석 방법 선택", ["범위 선택", "임계값 기준"])
397
+
398
+ if analysis_method == "범위 선택":
399
+ condition = st.slider(f"{condition_column} 범위 선택", float(min_val), float(max_val), (float(min_val), float(max_val)))
400
+ else: # 임계값 기준
401
+ threshold = st.number_input(f"{condition_column} 임계값 설정", min_value=float(min_val), max_value=float(max_val), value=float((min_val + max_val) / 2))
402
+ comparison = st.radio("비교 기준", ["이상", "이하"])
403
+ condition = (comparison, threshold)
404
+
405
+ is_numeric = True
406
+ else:
407
+ unique_values = df[condition_column].unique()
408
+ condition = st.multiselect(f"{condition_column} 값 선택", unique_values, default=unique_values)
409
+ is_numeric = False
410
 
411
+ if st.button("토픽 모델링 실행"):
412
+ st.session_state.run_analysis = True
413
+ st.session_state.text_column = text_column
414
+ st.session_state.num_topics = num_topics
415
+ st.session_state.analysis_type = analysis_type
416
+ if analysis_type == "조건부 분석":
417
+ st.session_state.condition_column = condition_column
418
+ st.session_state.condition = condition
419
+ st.session_state.is_numeric = is_numeric
420
+ else:
421
+ st.session_state.run_analysis = False
422
+ except pd.errors.EmptyDataError:
423
+ st.error("업로드된 CSV 파일이 비어있습니다. 다시 확인해주세요.")
424
+ except UnicodeDecodeError:
425
+ st.error("파일 인코딩에 문제가 있습니다. UTF-8 인코딩으로 저장된 CSV 파일을 사용해주세요.")
426
+ except Exception as e:
427
+ st.error(f"파일을 읽는 중 오류가 발생했습니다: {str(e)}")
428
+ else:
429
+ st.info("CSV 파일을 업로드해주세요.")
430
 
431
  # 푸터 추가
432
  st.markdown("""