"""키워드 분석 오버뷰 — 요약 카드, 테이블, 차트.""" import html import pandas as pd import plotly.graph_objects as go import streamlit as st from core.charts import POLARITY_COLORS def render_keyword_overview(keywords_list: list[dict]): """키워드 분석 오버뷰 카드.""" total_keywords = len(keywords_list) total_sentences = sum(kw.get("total_sentences", 0) for kw in keywords_list) total_brand_mentions = sum(kw.get("brand_mentioned_count", 0) for kw in keywords_list) total_no_brand = total_sentences - total_brand_mentions brand_mention_rate = (total_brand_mentions / total_sentences * 100) if total_sentences > 0 else 0 # Brand sentiment aggregation brand_pos = sum((kw.get("brand_sentiment") or {}).get("positive", 0) for kw in keywords_list) brand_neu = sum((kw.get("brand_sentiment") or {}).get("neutral", 0) for kw in keywords_list) brand_neg = sum((kw.get("brand_sentiment") or {}).get("negative", 0) for kw in keywords_list) brand_total = brand_pos + brand_neu + brand_neg brand_pos_pct = (brand_pos / brand_total * 100) if brand_total > 0 else 0 brand_neg_pct = (brand_neg / brand_total * 100) if brand_total > 0 else 0 # Top keyword-brand associations top_associations = [] for kw in sorted(keywords_list, key=lambda x: x.get("brand_mentioned_count", 0), reverse=True)[:3]: keyword = kw.get("keyword", "") total = kw.get("total_sentences", 0) brand_count = kw.get("brand_mentioned_count", 0) if total > 0 and brand_count > 0: rate = brand_count / total * 100 top_associations.append(f'"{keyword}" {brand_count:,}건 ({rate:.0f}%)') assoc_text = " | ".join(top_associations) if top_associations else "데이터 없음" # Brand sentiment bar brand_sent_bar = "" if brand_total > 0: bp = brand_pos / brand_total * 100 bn = brand_neg / brand_total * 100 bne = 100 - bp - bn brand_sent_bar = f"""
""" st.markdown(f"""
분석 키워드: {total_keywords}개 전체 문장: {total_sentences:,}건
🏠 브랜드 언급: {total_brand_mentions:,}건 ({brand_mention_rate:.1f}%) — 긍정 {brand_pos_pct:.0f}% / 부정 {brand_neg_pct:.0f}% 비브랜드: {total_no_brand:,}건 ({100 - brand_mention_rate:.1f}%)
{brand_sent_bar}
키워드-브랜드 연관 상위: {html.escape(assoc_text)}
""", unsafe_allow_html=True) def render_summary_table(keywords_list: list[dict]): """키워드별 감성 요약 테이블.""" st.markdown("**키워드별 감성 요약**") rows = [] for kw in keywords_list: keyword = kw.get("keyword", "") total = kw.get("total_sentences", 0) ks = kw.get("keyword_sentiment", {}) pos = ks.get("positive", 0) neu = ks.get("neutral", 0) neg = ks.get("negative", 0) brand_count = kw.get("brand_mentioned_count", 0) neg_rate = (neg / total * 100) if total > 0 else 0 pos_rate = (pos / total * 100) if total > 0 else 0 # Brand sentiment breakdown brand_sent = kw.get("brand_sentiment") or {} brand_pos = brand_sent.get("positive", 0) brand_neg = brand_sent.get("negative", 0) brand_neg_rate = (brand_neg / brand_count * 100) if brand_count > 0 else 0 rows.append({ "키워드": keyword, "총 문장": total, "긍정": pos, "중립": neu, "부정": neg, "부정률": f"{neg_rate:.1f}%", "긍정률": f"{pos_rate:.1f}%", "브랜드 멘션": brand_count, "브랜드 긍정": brand_pos if brand_count > 0 else "-", "브랜드 부정": brand_neg if brand_count > 0 else "-", "브랜드 부정률": f"{brand_neg_rate:.1f}%" if brand_count > 0 else "-", }) if rows: df = pd.DataFrame(rows) df = df.sort_values("부정", ascending=False) st.dataframe(df, use_container_width=True, hide_index=True) else: st.info("데이터가 없습니다") def render_sentiment_chart(keywords_list: list[dict]): """키워드 감성 비교 bar chart (부정 비율 순).""" st.markdown("**키워드 감성 비교 차트**") # Sort by negative count descending sorted_kws = sorted( keywords_list, key=lambda x: x.get("keyword_sentiment", {}).get("negative", 0), reverse=True, )[:20] # Top 20 keywords = [kw.get("keyword", "") for kw in sorted_kws] positives = [kw.get("keyword_sentiment", {}).get("positive", 0) for kw in sorted_kws] neutrals = [kw.get("keyword_sentiment", {}).get("neutral", 0) for kw in sorted_kws] negatives = [kw.get("keyword_sentiment", {}).get("negative", 0) for kw in sorted_kws] fig = go.Figure() fig.add_trace(go.Bar( name="부정", x=keywords, y=negatives, marker_color=POLARITY_COLORS["negative"], )) fig.add_trace(go.Bar( name="중립", x=keywords, y=neutrals, marker_color=POLARITY_COLORS["neutral"], )) fig.add_trace(go.Bar( name="긍정", x=keywords, y=positives, marker_color=POLARITY_COLORS["positive"], )) fig.update_layout( barmode="stack", height=400, margin=dict(l=20, r=20, t=30, b=80), legend=dict(orientation="h", yanchor="bottom", y=1.02), xaxis_tickangle=-45, ) st.plotly_chart(fig, use_container_width=True, config={"displayModeBar": False})