Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -16,19 +16,20 @@ def load_okt():
|
|
| 16 |
|
| 17 |
okt = load_okt()
|
| 18 |
|
| 19 |
-
# 불용어 목록
|
| 20 |
-
|
| 21 |
|
| 22 |
@st.cache_data
|
| 23 |
-
def preprocess_text(text):
|
| 24 |
-
# 특수 문자 제거
|
| 25 |
-
text = re.sub(r'[^\
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
#
|
| 31 |
-
processed = [word for word
|
|
|
|
| 32 |
return ' '.join(processed)
|
| 33 |
|
| 34 |
# Streamlit 앱 설정
|
|
@@ -42,6 +43,10 @@ api_key = st.sidebar.text_input("Claude API 키를 입력하세요", type="passw
|
|
| 42 |
if not api_key:
|
| 43 |
api_key = os.environ.get("ANTHROPIC_API_KEY")
|
| 44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
# 파일 업로드
|
| 46 |
uploaded_file = st.sidebar.file_uploader("CSV 파일을 업로드하세요", type="csv")
|
| 47 |
|
|
@@ -60,7 +65,7 @@ if uploaded_file is not None:
|
|
| 60 |
if st.sidebar.button("토픽 모델링 실행"):
|
| 61 |
# 텍스트 전처리
|
| 62 |
with st.spinner("텍스트 전처리 중..."):
|
| 63 |
-
df['processed_text'] = df[text_column].apply(preprocess_text)
|
| 64 |
|
| 65 |
# 토픽 모델링
|
| 66 |
with st.spinner("토픽 모델링 실행 중..."):
|
|
@@ -81,10 +86,18 @@ if uploaded_file is not None:
|
|
| 81 |
for idx, topic in enumerate(lda.components_):
|
| 82 |
st.subheader(f"토픽 {idx + 1}")
|
| 83 |
top_features_ind = topic.argsort()[:-11:-1]
|
| 84 |
-
top_features = [(feature_names[i], topic[i], tfidf_matrix[:, tfidf_vectorizer.vocabulary_[feature_names[i]]].mean()) for i in top_features_ind]
|
| 85 |
|
| 86 |
-
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
# 토픽 비중 그래프
|
| 90 |
st.header("토픽 비중 그래프")
|
|
@@ -119,3 +132,32 @@ if uploaded_file is not None:
|
|
| 119 |
st.write(response.completion)
|
| 120 |
else:
|
| 121 |
st.warning("Claude API 키가 설정되지 않았습니다. 토픽 해석을 제공받을 수 없습니다.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
okt = load_okt()
|
| 18 |
|
| 19 |
+
# 기본 불용어 목록
|
| 20 |
+
default_stop_words = ['이', '그', '저', '것', '수', '등', '들', '및', '에서', '그리고', '그래서', '또는', '그런데']
|
| 21 |
|
| 22 |
@st.cache_data
|
| 23 |
+
def preprocess_text(text, stop_words):
|
| 24 |
+
# 숫자 및 특수 문자 제거
|
| 25 |
+
text = re.sub(r'[^가-힣\s]', '', text)
|
| 26 |
+
|
| 27 |
+
# 형태소 분석 및 명사 추출
|
| 28 |
+
nouns = okt.nouns(text)
|
| 29 |
+
|
| 30 |
+
# 불용어 제거 및 길이가 1인 단어 제거
|
| 31 |
+
processed = [word for word in nouns if word not in stop_words and len(word) > 1]
|
| 32 |
+
|
| 33 |
return ' '.join(processed)
|
| 34 |
|
| 35 |
# Streamlit 앱 설정
|
|
|
|
| 43 |
if not api_key:
|
| 44 |
api_key = os.environ.get("ANTHROPIC_API_KEY")
|
| 45 |
|
| 46 |
+
# 불용어 설정
|
| 47 |
+
stop_words_input = st.sidebar.text_area("불용어 목록 (쉼표로 구분)", ', '.join(default_stop_words))
|
| 48 |
+
stop_words = [word.strip() for word in stop_words_input.split(',') if word.strip()]
|
| 49 |
+
|
| 50 |
# 파일 업로드
|
| 51 |
uploaded_file = st.sidebar.file_uploader("CSV 파일을 업로드하세요", type="csv")
|
| 52 |
|
|
|
|
| 65 |
if st.sidebar.button("토픽 모델링 실행"):
|
| 66 |
# 텍스트 전처리
|
| 67 |
with st.spinner("텍스트 전처리 중..."):
|
| 68 |
+
df['processed_text'] = df[text_column].apply(lambda x: preprocess_text(x, stop_words))
|
| 69 |
|
| 70 |
# 토픽 모델링
|
| 71 |
with st.spinner("토픽 모델링 실행 중..."):
|
|
|
|
| 86 |
for idx, topic in enumerate(lda.components_):
|
| 87 |
st.subheader(f"토픽 {idx + 1}")
|
| 88 |
top_features_ind = topic.argsort()[:-11:-1]
|
|
|
|
| 89 |
|
| 90 |
+
# LDA 상위 단어 테이블
|
| 91 |
+
lda_top_words = [(feature_names[i], topic[i]) for i in top_features_ind]
|
| 92 |
+
df_lda = pd.DataFrame(lda_top_words, columns=['단어', 'LDA 점수'])
|
| 93 |
+
st.subheader("LDA 상위 단어")
|
| 94 |
+
st.table(df_lda.style.format({'LDA 점수': '{:.4f}'}))
|
| 95 |
+
|
| 96 |
+
# TF-IDF 상위 단어 테이블
|
| 97 |
+
tfidf_top_words = [(feature_names[i], tfidf_matrix[:, tfidf_vectorizer.vocabulary_[feature_names[i]]].mean()) for i in top_features_ind]
|
| 98 |
+
df_tfidf = pd.DataFrame(tfidf_top_words, columns=['단어', 'TF-IDF'])
|
| 99 |
+
st.subheader("TF-IDF 상위 단어")
|
| 100 |
+
st.table(df_tfidf.style.format({'TF-IDF': '{:.4f}'}))
|
| 101 |
|
| 102 |
# 토픽 비중 그래프
|
| 103 |
st.header("토픽 비중 그래프")
|
|
|
|
| 132 |
st.write(response.completion)
|
| 133 |
else:
|
| 134 |
st.warning("Claude API 키가 설정되지 않았습니다. 토픽 해석을 제공받을 수 없습니다.")
|
| 135 |
+
|
| 136 |
+
# 기존 코드 맨 아래에 추가
|
| 137 |
+
|
| 138 |
+
# CSS를 사용하여 푸터 스타일 정의
|
| 139 |
+
footer_style = """
|
| 140 |
+
<style>
|
| 141 |
+
.footer {
|
| 142 |
+
position: fixed;
|
| 143 |
+
left: 0;
|
| 144 |
+
bottom: 0;
|
| 145 |
+
width: 100%;
|
| 146 |
+
background-color: #f1f1f1;
|
| 147 |
+
color: black;
|
| 148 |
+
text-align: center;
|
| 149 |
+
padding: 10px;
|
| 150 |
+
font-size: 14px;
|
| 151 |
+
}
|
| 152 |
+
</style>
|
| 153 |
+
"""
|
| 154 |
+
|
| 155 |
+
# 푸터 HTML
|
| 156 |
+
footer_html = footer_style + """
|
| 157 |
+
<div class="footer">
|
| 158 |
+
mySUNI 행복 College
|
| 159 |
+
</div>
|
| 160 |
+
"""
|
| 161 |
+
|
| 162 |
+
# 푸터 렌더링
|
| 163 |
+
st.markdown(footer_html, unsafe_allow_html=True)
|