You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

隐式数据集下物品相似度可视化方案咨询(Python/JS)

物品相似度聚类可视化的可行方案(Python/JS)

我来给你梳理几个适合处理4万级物品相似度聚类可视化的方案,不管用Python还是JavaScript都有靠谱的工具,下面一步步给你讲:


Python 首选:PyVis 交互式网络可视化

PyVis是我处理大规模网络图时经常用的工具,专门做交互式的网页版网络图,对付几万节点的场景很合适——只要你提前过滤掉低相似度的边,生成的页面可以直接打开,支持拖拽、缩放,hover还能看具体的相似度数值,非常直观。

操作步骤:

  1. 数据预处理:把你那两种格式的相似度数据统一转换成(物品A, 相似物品B, 相似度值)的结构,然后设置一个阈值(比如只保留相似度>0.3的边),不然4万节点全连起来图会密得像一团浆糊,根本没法看。
  2. 构建网络:用PyVis初始化网络,添加节点和边,边的权重可以控制线条粗细。
  3. 生成交互式页面:运行代码后会生成一个HTML文件,直接在浏览器打开就能操作。

示例代码:

先装库:

pip install pyvis

然后写代码:

from pyvis.network import Network

# 这里模拟你的数据,实际可以从CSV/JSON文件读取
edges = [
    ("A", "C", 0.12),
    ("A", "R", 0.42),
    ("A", "Y", 0.34),
    ("A", "J", 0.62),
    ("B", "A", 0.16),
    ("B", "Y", 0.83),
    ("C", "J", 0.23),
    # 更多数据...
]

# 过滤低相似度边,这里设0.3为阈值,你可以根据实际情况调整
threshold = 0.3
filtered_edges = [e for e in edges if e[2] > threshold]

# 初始化网络,设置深色背景更清晰
net = Network(notebook=False, height="800px", width="100%", bgcolor="#222222", font_color="white")

# 添加节点和边(节点会自动去重)
for item, sim_item, sim_val in filtered_edges:
    net.add_node(item, label=item)
    net.add_node(sim_item, label=sim_item)
    # 边的value控制粗细,title是hover时显示的内容
    net.add_edge(item, sim_item, value=sim_val, title=f"相似度: {sim_val:.2f}")

# 启用Barnes-Hut布局,让节点自动聚类
net.barnes_hut()
# 生成HTML文件
net.write_html("item_similarity_cluster.html")

小提示:

对于4万节点,一定要严格过滤边——比如每个物品只保留Top10相似的,或者只保留相似度前5%的关联,不然渲染出来的图根本没法交互。


Python 备选:Plotly 交互式网络图

如果你想要更美观、可定制的可视化,Plotly的网络图是个不错的选择,它的交互性也很强,还能直接在Jupyter Notebook里展示。

示例代码:

先装库:

pip install plotly pandas networkx

代码如下:

import plotly.graph_objects as go
import pandas as pd
import networkx as nx

# 整理数据为DataFrame
edges_df = pd.DataFrame(edges, columns=["source", "target", "similarity"])
# 过滤低相似度边
edges_df = edges_df[edges_df["similarity"] > 0.3]

# 获取所有节点
nodes = list(set(edges_df["source"].tolist() + edges_df["target"].tolist()))

# 用NetworkX生成力导向布局坐标
G = nx.from_pandas_edgelist(edges_df, "source", "target", edge_attr="similarity")
pos = nx.spring_layout(G, k=0.15)  # k值越小,节点间距越大

# 准备图的轨迹
fig = go.Figure()

# 添加边的轨迹
fig.add_trace(go.Scatter(
    x=[], y=[],
    line=dict(width=0.5, color='#888'),
    hoverinfo='none',
    mode='lines'))

# 添加节点的轨迹(带标签)
fig.add_trace(go.Scatter(
    x=[], y=[],
    mode='markers+text',
    text=nodes,
    textposition="top center",
    marker=dict(
        showscale=True,
        colorscale='YlGnBu',
        size=10,
        colorbar=dict(
            thickness=15,
            title='节点连接数',
            xanchor='left',
            titleside='right'
        )),
    hoverinfo='text'))

# 填充边和节点的坐标
for edge in G.edges(data=True):
    x0, y0 = pos[edge[0]]
    x1, y1 = pos[edge[1]]
    fig.data[0]['x'] += (x0, x1, None)
    fig.data[0]['y'] += (y0, y1, None)

for node in nodes:
    x, y = pos[node]
    fig.data[1]['x'] += (x,)
    fig.data[1]['y'] += (y,)

# 调整布局样式,隐藏坐标轴
fig.update_layout(
    showlegend=False,
    hovermode='closest',
    margin=dict(b=20,l=5,r=5,t=40),
    xaxis=dict(showgrid=False, zeroline=False, showticklabels=False),
    yaxis=dict(showgrid=False, zeroline=False, showticklabels=False)
)

# 显示图
fig.show()

JavaScript 方案:D3.js 力导向图

如果需要把可视化嵌入到网页里,D3.js是绝对的首选——它的力导向图可以高效处理大规模数据,并且样式完全自定义,想怎么调就怎么调。

示例代码(HTML+JS):

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>物品相似度聚类</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <style>
        .node {
            stroke: #fff;
            stroke-width: 1.5px;
            cursor: pointer;
        }
        .link {
            stroke: #999;
            stroke-opacity: 0.6;
        }
        .node-label {
            font-size: 12px;
            fill: #333;
        }
    </style>
</head>
<body>
    <svg width="960" height="600"></svg>

    <script>
        // 模拟你的数据,实际可以从后端接口或JSON文件加载
        const edges = [
            {"source": "A", "target": "C", "similarity": 0.12},
            {"source": "A", "target": "R", "similarity": 0.42},
            {"source": "A", "target": "Y", "similarity": 0.34},
            {"source": "A", "target": "J", "similarity": 0.62},
            {"source": "B", "target": "A", "similarity": 0.16},
            {"source": "B", "target": "Y", "similarity": 0.83},
            {"source": "C", "target": "J", "similarity": 0.23}
        ];

        // 过滤低相似度边,只保留>0.3的
        const filteredEdges = edges.filter(d => d.similarity > 0.3);

        // 构建节点集合(自动去重)
        const nodesMap = new Map();
        filteredEdges.forEach(edge => {
            if (!nodesMap.has(edge.source)) nodesMap.set(edge.source, {id: edge.source});
            if (!nodesMap.has(edge.target)) nodesMap.set(edge.target, {id: edge.target});
        });
        const nodes = Array.from(nodesMap.values());

        // 初始化SVG和力导向模拟
        const svg = d3.select("svg"),
            width = +svg.attr("width"),
            height = +svg.attr("height");

        const simulation = d3.forceSimulation(nodes)
            .force("link", d3.forceLink(filteredEdges)
                .id(d => d.id)
                .distance(d => 100 / d.similarity)) // 相似度越高,节点距离越近
            .force("charge", d3.forceManyBody().strength(-300)) // 节点间排斥力
            .force("center", d3.forceCenter(width / 2, height / 2)); // 居中

        // 添加边
        const link = svg.append("g")
            .selectAll("line")
            .data(filteredEdges)
            .join("line")
            .attr("class", "link")
            .attr("stroke-width", d => d.similarity * 5); // 相似度越高,线条越粗

        // 添加节点
        const node = svg.append("g")
            .selectAll("circle")
            .data(nodes)
            .join("circle")
            .attr("class", "node")
            .attr("r", 6)
            .attr("fill", "#69b3a2")
            .call(d3.drag() // 支持拖拽节点
                .on("start", dragstarted)
                .on("drag", dragged)
                .on("end", dragended));

        // 添加节点标签
        const label = svg.append("g")
            .selectAll("text")
            .data(nodes)
            .join("text")
            .attr("class", "node-label")
            .text(d => d.id)
            .attr("x", 8)
            .attr("y", "0.31em");

        // 模拟tick事件,更新节点和边的位置
        simulation.on("tick", () => {
            link
                .attr("x1", d => d.source.x)
                .attr("y1", d => d.source.y)
                .attr("x2", d => d.target.x)
                .attr("y2", d => d.target.y);

            node
                .attr("cx", d => d.x)
                .attr("cy", d => d.y);

            label
                .attr("x", d => d.x)
                .attr("y", d => d.y);
        });

        // 拖拽相关函数
        function dragstarted(event, d) {
            if (!event.active) simulation.alphaTarget(0.3).restart();
            d.fx = d.x;
            d.fy = d.y;
        }

        function dragged(event, d) {
            d.fx = event.x;
            d.fy = event.y;
        }

        function dragended(event, d) {
            if (!event.active) simulation.alphaTarget(0);
            d.fx = null;
            d.fy = null;
        }
    </script>
</body>
</html>

小提示:

对于4万节点的场景,一定要在后端做数据过滤,只把高相似度的边传给前端,不然浏览器会直接卡崩。比如每个物品只返回Top5相似的,或者过滤掉相似度<0.4的边。


通用注意事项

  1. 数据过滤是核心:4万节点的全连接图完全无法可视化,必须通过阈值或Top-N的方式过滤掉不重要的关联。
  2. 力导向布局最适合聚类:不管用哪个工具,力导向布局都会自动把关联紧密的节点聚在一起,直观呈现聚类分布。
  3. 优先选交互式工具:静态图对于大规模数据毫无意义,交互式工具让用户可以缩放、拖拽、查看细节,才是有效的可视化。

内容的提问来源于stack exchange,提问作者Kemal Can Kara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 03:32:49