Spaces:
Sleeping
Sleeping
| """UMAP ํ ํฝ ๋งต ์๊ฐํ (Plotly scatter). | |
| snapshot.coordinates: [{cluster_id, x, y, size, label}] | |
| clusters: [{id, cluster_label, attention_score, citation_density, opportunity_score, fanout_count, ...}] | |
| """ | |
| import streamlit as st | |
| import plotly.graph_objects as go | |
| def render_topic_map(clusters: list[dict], snapshot: dict | None, frame: str = "all"): | |
| """Render UMAP 2D scatter from snapshot coordinates + cluster metadata.""" | |
| if not snapshot or not snapshot.get("coordinates"): | |
| st.info("UMAP ์ขํ ๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค. ํด๋ฌ์คํฐ๋ง ์คํ ํ ์์ฑ๋ฉ๋๋ค.") | |
| return | |
| # Frame-specific guide | |
| if frame == "demand": | |
| st.markdown(""" | |
| ๊ฐ ์ ์ ํ๋์ **Demand ํ ํฝ** (ChatGPT sub-query ๊ทธ๋ฃน)์ ๋๋ค. | |
| - **์ ํฌ๊ธฐ**: ํด๋น ํ ํฝ์ fanout ์ (ํด์๋ก ์๋น์๊ฐ ์์ฃผ ๋ฌป๋ ํ ํฝ) | |
| - **์ ์์**: ๊ธฐํ ์ ์ (๋นจ๊ฐ = ๊ธฐํ ํผ, ๋ ธ๋ = ๋ณดํต) | |
| - **๊ฐ๊น์ด ์๋ ์ **: ์ ์ฌํ ๊ฒ์ ์๋์ ํ ํฝ | |
| """) | |
| elif frame == "supply": | |
| st.markdown(""" | |
| ๊ฐ ์ ์ ํ๋์ **Supply ํ ํฝ** (Gemini citation quote ๊ทธ๋ฃน)์ ๋๋ค. | |
| - **์ ํฌ๊ธฐ**: ํด๋น ํ ํฝ์ ์ธ์ฉ ์ (ํด์๋ก AI๊ฐ ์์ฃผ ์ธ์ฉํ๋ ํ ํฝ) | |
| - **์ ์์**: ๊ธฐํ ์ ์ (๋นจ๊ฐ = ๊ธฐํ ํผ, ๋ ธ๋ = ๋ณดํต) | |
| - **๊ฐ๊น์ด ์๋ ์ **: ์ ์ฌํ ์ธ์ฉ ์ฃผ์ ์ ํ ํฝ | |
| """) | |
| else: | |
| st.markdown(""" | |
| ๊ฐ ์ ์ ํ๋์ **ํ ํฝ**(AI ์ถ๊ฐ ์ง๋ฌธ ๊ทธ๋ฃน)์ ๋๋ค. | |
| - **์ ํฌ๊ธฐ**: ํด๋น ํ ํฝ์ AI ์ถ๊ฐ ์ง๋ฌธ ์ (ํด์๋ก AI๊ฐ ์์ฃผ ๋ฌป๋ ํ ํฝ) | |
| - **์ ์์**: ๊ธฐํ ์ ์ (๋นจ๊ฐ = ๊ธฐํ ํผ, ๋ ธ๋ = ๋ณดํต) | |
| - **๊ฐ๊น์ด ์๋ ์ **: ์ ์ฌํ ์ฃผ์ ์ ํ ํฝ | |
| """) | |
| count_label = "Citations" if frame == "supply" else "Fanouts" | |
| coords = snapshot["coordinates"] | |
| # Build cluster lookup by id | |
| cluster_map = {c["id"]: c for c in clusters} | |
| # Merge coordinate data with cluster metadata | |
| xs, ys, sizes, colors, hover_texts = [], [], [], [], [] | |
| for pt in coords: | |
| cid = pt.get("cluster_id") | |
| meta = cluster_map.get(cid, {}) | |
| xs.append(pt["x"]) | |
| ys.append(pt["y"]) | |
| fanout_count = pt.get("size", meta.get("fanout_count", 10)) | |
| # Normalize size for display (min 5, max 40) | |
| norm_size = max(5, min(40, fanout_count / 5)) | |
| sizes.append(norm_size) | |
| opp = float(meta.get("opportunity_score", 0) or 0) | |
| colors.append(opp) | |
| label = meta.get("cluster_label") or f"Cluster {pt.get('label', '?')}" | |
| attn = float(meta.get("attention_score", 0) or 0) | |
| density = float(meta.get("citation_density", 0) or 0) | |
| hover_texts.append( | |
| f"<b>{label}</b><br>" | |
| f"Attention: {attn:.4f}<br>" | |
| f"Density: {density:.4f}<br>" | |
| f"Opportunity: {opp:.4f}<br>" | |
| f"{count_label}: {fanout_count}" | |
| ) | |
| fig = go.Figure() | |
| fig.add_trace(go.Scatter( | |
| x=xs, | |
| y=ys, | |
| mode="markers", | |
| marker=dict( | |
| size=sizes, | |
| color=colors, | |
| colorscale="YlOrRd", | |
| colorbar=dict(title="Opportunity"), | |
| opacity=0.7, | |
| line=dict(width=0.5, color="#333"), | |
| ), | |
| text=hover_texts, | |
| hoverinfo="text", | |
| )) | |
| fig.update_layout( | |
| title="AI ํ ํฝ ๋งต (UMAP 2D Projection)", | |
| xaxis=dict(title="UMAP-1", showgrid=False, zeroline=False), | |
| yaxis=dict(title="UMAP-2", showgrid=False, zeroline=False), | |
| height=600, | |
| template="plotly_white", | |
| hoverlabel=dict(bgcolor="white", font_size=12), | |
| ) | |
| st.plotly_chart(fig, use_container_width=True, config={"displayModeBar": False}) | |
| # Algorithm params info | |
| params = snapshot.get("algorithm_params") | |
| if params: | |
| with st.expander("๋ถ์ ์ค์ (๊ธฐ์ ์์ธ)"): | |
| st.json(params) | |