"""키워드 분석 오버뷰 — 요약 카드, 테이블, 차트."""
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})