GitHub Action
Sync from GitHub
ef78361
Raw
History Blame Contribute Delete
3.86 kB
"""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)