Spaces:
Sleeping
Sleeping
| """ํค์๋ ๋ถ์ ์ค๋ฒ๋ทฐ โ ์์ฝ ์นด๋, ํ ์ด๋ธ, ์ฐจํธ.""" | |
| 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""" | |
| <div style="display:flex;height:6px;border-radius:3px;overflow:hidden;margin-top:4px;max-width:300px;"> | |
| <div style="width:{bp}%;background:#10B981;"></div> | |
| <div style="width:{bne}%;background:#D1D5DB;"></div> | |
| <div style="width:{bn}%;background:#EF4444;"></div> | |
| </div>""" | |
| st.markdown(f""" | |
| <div style="background: linear-gradient(135deg, #F8FAFC 0%, #ECFDF5 100%); border: 1px solid #A7F3D0; | |
| border-radius: 12px; padding: 16px; margin-bottom: 16px;"> | |
| <div style="display: flex; gap: 24px; flex-wrap: wrap; font-size: 13px; color: #374151;"> | |
| <span>๋ถ์ ํค์๋: <strong>{total_keywords}๊ฐ</strong></span> | |
| <span>์ ์ฒด ๋ฌธ์ฅ: <strong>{total_sentences:,}๊ฑด</strong></span> | |
| </div> | |
| <div style="display:flex;gap:20px;flex-wrap:wrap;font-size:12px;color:#374151;margin-top:8px;"> | |
| <span>๐ ๋ธ๋๋ ์ธ๊ธ: <strong>{total_brand_mentions:,}๊ฑด</strong> ({brand_mention_rate:.1f}%) | |
| — <span style="color:#10B981;">๊ธ์ {brand_pos_pct:.0f}%</span> | |
| / <span style="color:#EF4444;">๋ถ์ {brand_neg_pct:.0f}%</span> | |
| </span> | |
| <span style="color:#6B7280;">๋น๋ธ๋๋: <strong>{total_no_brand:,}๊ฑด</strong> ({100 - brand_mention_rate:.1f}%)</span> | |
| </div>{brand_sent_bar} | |
| <div style="font-size: 11px; color: #9CA3AF; margin-top: 6px;"> | |
| ํค์๋-๋ธ๋๋ ์ฐ๊ด ์์: {html.escape(assoc_text)} | |
| </div> | |
| </div> | |
| """, 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}) | |