当前位置:首页>python>Python绘制双层聚类网络关系图(附数据和代码)

Python绘制双层聚类网络关系图(附数据和代码)

  • 2026-10-11 08:37:53
Python绘制双层聚类网络关系图(附数据和代码)

©[悠悠智汇笔记] 版权所有

🙏请尊重劳动成果,守护每一份劳动心血;⚖️未经授权,不得以为任何方式转载、摘编或抄袭。🔄转载合作请后台联系授权,侵权必究。

本期分享一种基于多层网络与聚类分析的双层可视化图。图中上层为作者合作网络,下层为论文引用网络,不同颜色表示不同的研究主题或技术方向。通过聚类与布局算法,将同一主题下的节点组织成“烟花式”结构,使得主题分布更加清晰,同时通过层间连线展示作者与论文之间的关联关系,从而直观呈现知识生产与学术合作之间的互动结构。

01

导入库并设置全局参数

导入依赖库,并统一设置输入文件、输出图片、双层高度、视角角度、主题环形布局半径、簇扩散尺度、聚类阈值和边筛选比例

import pandas as pdimport networkx as nximport matplotlib.pyplotas pltimport numpy as npfrom collections import defaultdict, Counterfrom matplotlib.colorsimport to_rgbafrom matplotlib.patchesimportPolygon, PathPatchfrom matplotlib.pathimportPath# =========================================================# 参数区:全局视觉与逻辑控制中心# =========================================================NODE_FILE = "sci_nodes.csv"EDGE_FILE = "sci_edges.csv"OUTPUT_PNG = "double_layer_clustered_network_local.png"SHOW_LABELS = FalseSEED = 42TOP_Y = 4.9BOTTOM_Y = 0.0ALPHA_DEG = 8BETA_DEG = 4OUTER_RING_RX = 11.8OUTER_RING_RZ = 8.2INNER_RING_RX = 6.1INNER_RING_RZ = 4.5CLUSTER_CENTER_RADIUS_AUTHORS = 1.55CLUSTER_CENTER_RADIUS_PAPERS = 1.85AUTHOR_BURST_SCALE = 2.55PAPER_BURST_SCALE = 3.05AUTHOR_MAX_CLUSTER_SIZE = 14PAPER_MAX_CLUSTER_SIZE = 18RECUR_CLUSTER_MIN_SIZE = 4RECUR_CLUSTER_MAX_DEPTH = 4COLLAB_KEEP_RATIO = 0.42CITATION_KEEP_RATIO = 0.18

02

数据预处理

读取 sci_nodes.csv 和 sci_edges.csv,检查必备字段,并补齐缺失字段。如果本地节点表里没有 paper_count 或 citation_received,就根据边表自动计算。

# =========================================================# 读取本地数据:数据清洗与预处理# =========================================================defload_local_data(node_file, edge_file):    nodes_df = pd.read_csv(node_file)    edges_df = pd.read_csv(edge_file)    required_node_cols = {"id", "lvl"}    required_edge_cols = {"source", "target", "rel"}    if not required_node_cols.issubset(nodes_df.columns):        missing = required_node_cols - set(nodes_df.columns)        raise ValueError(f"nodes 文件缺少必要字段: {missing}")    if not required_edge_cols.issubset(edges_df.columns):        missing = required_edge_cols - set(edges_df.columns)        raise ValueError(f"edges 文件缺少必要字段: {missing}")    nodes_df = nodes_df.copy()    edges_df = edges_df.copy()    nodes_df["id"] = nodes_df["id"].astype(str)    edges_df["source"] = edges_df["source"].astype(str)    edges_df["target"] = edges_df["target"].astype(str)    edges_df["rel"] = edges_df["rel"].astype(str)    nodes_df["lvl"] = nodes_df["lvl"].astype(int)    optional_node_cols = [        "theme_true", "subtopic_true", "paper_count", "citation_received",        "year", "paper_type", "quality"    ]    forcolin optional_node_cols:        if col not in nodes_df.columns:            nodes_df[col] = ""    if nodes_df["paper_count"].replace("", np.nan).isna().all():        author_paper_count = defaultdict(int)        auth_edges = edges_df[edges_df["rel"] == "authorship"]        id_to_lvl = dict(zip(nodes_df["id"], nodes_df["lvl"]))        for_, row in auth_edges.iterrows():            src, tgt =row["source"], row["target"]            if src in id_to_lvl and tgt in id_to_lvl:                ifint(id_to_lvl[src]) == 2andint(id_to_lvl[tgt]) == 1:                    author_paper_count[src] += 1        nodes_df["paper_count"] = nodes_df["id"].map(author_paper_count).fillna(nodes_df["paper_count"])    if nodes_df["citation_received"].replace("", np.nan).isna().all():        cited_count = defaultdict(int)        cit_edges = edges_df[edges_df["rel"] == "citation"]        for_, row in cit_edges.iterrows():            cited_count[row["target"]] +=1        nodes_df["citation_received"] = nodes_df["id"].map(cited_count).fillna(nodes_df["citation_received"])    nodes_df["theme_true"] = nodes_df["theme_true"].fillna("").astype(str)    nodes_df["subtopic_true"] = nodes_df["subtopic_true"].fillna("").astype(str)    return nodes_df, edges_df

03

数据转换

将节点表和边表转换成networkx图。包括三种关系图:作者合作图、论文引用图、层间署名图;同时也构建一张包含全部节点和边的混合图。

# =========================================================# 构建图:将表格转换为 NetworkX 拓扑对象# =========================================================defbuild_weighted_relation_graph(nodes_df, edges_df, relation):    H = nx.Graph()    if relation == "collaboration":        valid_nodes = set(nodes_df.loc[nodes_df["lvl"] == 2, "id"])        rel_edges = edges_df[edges_df["rel"] == "collaboration"]    elif relation == "citation":        valid_nodes = set(nodes_df.loc[nodes_df["lvl"] == 1, "id"])        rel_edges = edges_df[edges_df["rel"] == "citation"]    elif relation == "authorship":        valid_nodes = set(nodes_df["id"])        rel_edges = edges_df[edges_df["rel"] == "authorship"]    else:        raise ValueError("Unknown relation")    fornin valid_nodes:        lvl = int(nodes_df.loc[nodes_df["id"] == n, "lvl"].iloc[0])        H.add_node(n, lvl=lvl)    for_, row in rel_edges.iterrows():        u, v =row["source"], row["target"]        if u not in valid_nodes or v not in valid_nodes:            continue        if H.has_edge(u, v):            H[u][v]["weight"] += 1        else:            H.add_edge(u, v, weight=1)    return Hdefbuild_full_graph(nodes_df, edges_df):    G = nx.Graph()    for_, row in nodes_df.iterrows():        G.add_node(row["id"], lvl=int(row["lvl"]))    for_, row in edges_df.iterrows():        u, v =row["source"], row["target"]        if G.has_edge(u, v):            G[u][v]["weight"] += 1        else:            G.add_edge(u, v, weight=1)    return G

04

分层递归聚类

用递归方式把作者合作图和论文引用图自动划分成多个社区。社区太大就继续拆,直到大小合适或者达到最大递归深度。

# =========================================================# 递归聚类:基于模块化度算法自动社区划分# =========================================================def recursive_detect_clusters(        H,        prefix,        max_cluster_size=16,        min_cluster_size=4,        max_depth=4):    final_groups = []    defsplit_nodes(nodes, depth):        sub = H.subgraph(nodes).copy()        iflen(nodes) <= max_cluster_size:            final_groups.append(set(nodes))            return        ifsub.number_of_edges() == 0:            fornin nodes:                final_groups.append({n})            return        ifdepth>=max_depth:            final_groups.append(set(nodes))            return        comms = list(nx.community.greedy_modularity_communities(sub, weight="weight"))        iflen(comms) <= 1:            final_groups.append(set(nodes))            return        forcin comms:            iflen(c) > max_cluster_size and len(c) >= min_cluster_size * 2:                split_nodes(list(c), depth + 1)            else:                final_groups.append(set(c))    forcompin nx.connected_components(H):        split_nodes(list(comp), 0)    final_groups = sorted(final_groups, key=lambda x: (-len(x), sorted(list(x))[0]))    cluster_map = {}    rows = []    for i, group in enumerate(final_groups, start=1):        cid = f"{prefix}{i}"        members = sorted(list(group))        fornin members:            cluster_map[n] = cid        rows.append({            "cluster_id": cid,            "member_count": len(members),            "members": ",".join(members)        })    return cluster_map, pd.DataFrame(rows) 

05

聚类主题映射

先根据theme_true把每个聚类归到某个主题,如果没有明确主题,就用层间作者—论文关系推断。然后再为每个主题分配颜色。

# =========================================================# 聚类 -> 主题映射# =========================================================defbuild_cluster_theme_maps(nodes_df, edges_df, author_cluster_map, paper_cluster_map):    id_to_theme = dict(zip(nodes_df["id"], nodes_df["theme_true"]))    if (nodes_df["theme_true"] != "").any():        cluster_theme_map = {}        forcidinsorted(set(author_cluster_map.values())):            members = [n for n, c in author_cluster_map.items() if c == cid]            themes = [id_to_theme[n] for n in members if id_to_theme.get(n, "") != ""]            cluster_theme_map[cid] = Counter(themes).most_common(1)[0][0] if themes else cid        forcidinsorted(set(paper_cluster_map.values())):            members = [n for n, c in paper_cluster_map.items() if c == cid]            themes = [id_to_theme[n] for n in members if id_to_theme.get(n, "") != ""]            cluster_theme_map[cid] = Counter(themes).most_common(1)[0][0] if themes else cid        return cluster_theme_map    author_clusters = sorted(set(author_cluster_map.values()))    paper_clusters = sorted(set(paper_cluster_map.values()))    author_theme_map = {}    for i, ac in enumerate(author_clusters, start=1):        author_theme_map[ac] = f"T{i:02d}"    ac_pc_weights = defaultdict(int)    auth_edges = edges_df[edges_df["rel"] == "authorship"]    for_, row in auth_edges.iterrows():        a, p =row["source"], row["target"]        ac = author_cluster_map.get(a)        pc = paper_cluster_map.get(p)        if ac isnotNoneand pc isnotNone:            ac_pc_weights[(ac, pc)] += 1    paper_theme_map = {}    forpcin paper_clusters:        linked = [(ac, w) for (ac, pc2), w in ac_pc_weights.items() if pc2 == pc]        if linked:            best_ac = sorted(linked, key=lambda x: x[1], reverse=True)[0][0]            paper_theme_map[pc] = author_theme_map[best_ac]        else:            paper_theme_map[pc] = pc    cluster_theme_map = {}    cluster_theme_map.update(author_theme_map)    cluster_theme_map.update(paper_theme_map)    return cluster_theme_mapdefbuild_theme_colors(cluster_theme_map):    palette = (        list(plt.cm.tab20.colors)        + list(plt.cm.Set3.colors)        + list(plt.cm.Paired.colors)    )    themes = sorted(set(cluster_theme_map.values()))    theme_color_map = {theme: palette[i % len(palette)] for i, theme in enumerate(themes)}    cluster_color_map = {cid: theme_color_map[t] for cid, t in cluster_theme_map.items()}    return theme_color_map, cluster_color_map

06

确定主题位置

先把主题放到外环和内环上,再让同一主题下的多个社区围着这个主题锚点排开。这样整体画面更稳定。 

# =========================================================# 主题锚点:多环圆形布局# =========================================================defbuild_theme_anchors_multiring(theme_list, outer_rx=11.8, outer_rz=8.2, inner_rx=6.1, inner_rz=4.5):    theme_list = sorted(theme_list)    n = len(theme_list)    anchors = {}    ifn<=6:        for i, theme in enumerate(theme_list):            angle = 2 * np.pi * i / n            anchors[theme] = np.array([outer_rx * np.cos(angle), outer_rz * np.sin(angle)])        return anchors    n_inner = max(3, int(round(n * 0.35)))    n_outer = n - n_inner    outer_themes = theme_list[:n_outer]    inner_themes = theme_list[n_outer:]    for i, theme in enumerate(outer_themes):        angle = 2 * np.pi * i / n_outer        anchors[theme] = np.array([outer_rx * np.cos(angle), outer_rz * np.sin(angle)])    for i, theme in enumerate(inner_themes):        angle = 2 * np.pi * i / n_inner + np.pi / n_inner        anchors[theme] = np.array([inner_rx * np.cos(angle), inner_rz * np.sin(angle)])    return anchors# =========================================================# 每个主题下多个簇的中心# =========================================================defbuild_cluster_centers(cluster_map, cluster_theme_map, theme_anchors, radius=1.2):    theme_to_clusters = defaultdict(list)    cluster_counts = defaultdict(int)    forn, cid in cluster_map.items():        cluster_counts[cid] += 1    forcidinsorted(set(cluster_map.values())):        theme_to_clusters[cluster_theme_map[cid]].append(cid)    cluster_centers = {}    fortheme, cids in theme_to_clusters.items():        anchor = theme_anchors[theme]        cids = sorted(cids, key=lambda x: cluster_counts[x], reverse=True)        m = len(cids)        ifm==1:            cluster_centers[cids[0]] = anchor.copy()        else:            for j, cid in enumerate(cids):                angle = 2 * np.pi * j / m                offset= radius * np.array([np.cos(angle), np.sin(angle)])                cluster_centers[cid] = anchor +offset    return cluster_centers

07

确定节点位置

将簇内节点散开,不要全部挤成一团。先按subtopic_true分组,再让每组节点沿几条“手臂”向外扩散。

# =========================================================# 烟花式布局# =========================================================def firework_cluster_layout(    H,    cluster_map,    cluster_centers,    node_subtopic_map=None,    burst_scale=1.3,    seed=42):    rng = np.random.default_rng(seed)    pos_xz = {}    deg = dict(H.degree(weight="weight"))    unique_clusters = sorted(set(cluster_map.values()))    for idx, cid in enumerate(unique_clusters):        members = [n for n, c in cluster_map.items() if c == cid]        center = cluster_centers[cid]        iflen(members)== 1:            pos_xz[members[0]] = center            continue        groups = defaultdict(list)        fornin members:            st = node_subtopic_map.get(n, f"{cid}_g0") if node_subtopic_map elsef"{cid}_g0"            groups[st].append(n)        group_keys = sorted(groups.keys())        n_groups = len(group_keys)        group_centers = {}        ifn_groups==1:            group_centers[group_keys[0]] = center        else:            group_ring = (0.92 + 0.22 * np.sqrt(len(members))) * burst_scale            for j, g in enumerate(group_keys):                angle = 2 * np.pi * j / n_groups + 0.25                offset= group_ring * np.array([np.cos(angle), np.sin(angle)])                group_centers[g] = center +offset        for j, g in enumerate(group_keys):            g_nodes = sorted(groups[g], key=lambda x: deg.get(x, 0), reverse=True)            g_center = group_centers[g]            base_angle = 2 * np.pi * j / max(n_groups, 1)            n = len(g_nodes)            core_n = max(1, int(round(n * 0.14)))            core_nodes = g_nodes[:core_n]            outer_nodes = g_nodes[core_n:]            fornodein core_nodes:                ang = rng.uniform(0, 2 * np.pi)                rad = rng.uniform(0.08, 0.24) * burst_scale                pos_xz[node] = g_center + rad * np.array([np.cos(ang), np.sin(ang)])            if outer_nodes:                n_arms = min(6, max(3, int(np.ceil(len(outer_nodes) / 6))))                arm_spread = 0.52                for t, node in enumerate(outer_nodes):                    arm = t % n_arms                    layer = t // n_arms                    arm_angle = base_angle + (arm - (n_arms - 1) / 2) * arm_spread + rng.normal(0, 0.05)                    radius = (0.48 + layer * 0.36 + rng.uniform(-0.06, 0.08)) * burst_scale                    base_vec = np.array([np.cos(arm_angle), np.sin(arm_angle)])                    tang_vec = np.array([-np.sin(arm_angle), np.cos(arm_angle)])                    tang_shift = rng.uniform(-0.12, 0.12) * burst_scale                    pos_xz[node] = g_center + radius * base_vec + tang_shift * tang_vec    return pos_xz

08

布局投影成双层 3D 视觉

将作者层和论文层从x-z平面投到最终二维画布上,同时提供背景平面绘制、边筛选和层内曲线边绘制工具。

# =========================================================# 3D -> 2D 投影# =========================================================defproject_point(x, y, z, alpha_deg=8, beta_deg=4):    alpha = np.radians(alpha_deg)    beta = np.radians(beta_deg)    xp = x * np.cos(beta) + z * np.sin(beta)    yp = -x * np.sin(beta) * np.sin(alpha) + y * np.cos(alpha) + z * np.cos(beta) * np.sin(alpha)    return np.array([xp, yp])defproject_multilevel_positions(nodes_df, author_pos_xz, paper_pos_xz, alpha_deg=8, beta_deg=4):    pos_2d = {}    for_, row in nodes_df.iterrows():        n = row["id"]        lvl = int(row["lvl"])        iflvl==2:            x, z = author_pos_xz[n]            y = TOP_Y        else:            x, z = paper_pos_xz[n]            y = BOTTOM_Y        pos_2d[n] = project_point(x, y, z, alpha_deg, beta_deg)    return pos_2d# =========================================================# 背景平面# =========================================================defdraw_plane(ax, x_min, x_max, z_min, z_max, y_level, facecolor, alpha):    corners_3d = [        (x_min, y_level, z_min),        (x_max, y_level, z_min),        (x_max, y_level, z_max),        (x_min, y_level, z_max),    ]    corners_2d = [project_point(x, y, z, ALPHA_DEG, BETA_DEG) for x, y, z in corners_3d]    poly = Polygon(corners_2d, closed=True, facecolor=facecolor, edgecolor="none", alpha=alpha, zorder=0)    ax.add_patch(poly)# =========================================================# 边筛选# =========================================================defselect_top_edges(edges_with_score, keep_ratio):    if not edges_with_score:        return []    edges_with_score = sorted(edges_with_score, key=lambda x: x[2], reverse=True)    keep_n = max(1, int(len(edges_with_score) * keep_ratio))    return edges_with_score[:keep_n]# =========================================================# 层内部曲线边# =========================================================defdraw_curved_intralayer_edges(ax, edge_items, pos, node_theme, node_color, same_alpha, cross_alpha, bend_ratio):    for i, (u, v, score, width) in enumerate(edge_items):        p0 = np.array(pos[u], dtype=float)        p2 = np.array(pos[v], dtype=float)        vec = p2 - p0        dist = np.linalg.norm(vec)        ifdist==0:            continue        perp = np.array([-vec[1], vec[0]], dtype=float)        perp_norm = np.linalg.norm(perp)        ifperp_norm==0:            continue        perp = perp / perp_norm        sign = 1 if (i % 2 == 0) else -1        ctrl = (p0 + p2) / 2 + sign * bend_ratio * dist * perp        verts = [p0, ctrl, p2]        codes = [Path.MOVETO, Path.CURVE3, Path.CURVE3]        path = Path(verts, codes)        if node_theme[u] == node_theme[v]:            color = to_rgba(node_color[u], same_alpha)        else:            color = (0.45, 0.45, 0.45, cross_alpha)        patch = PathPatch(path, facecolor="none", edgecolor=color, lw=width, zorder=1)        ax.add_patch(patch)

09

绘图

最后,将前面所有结果拼起来:算节点大小、画层间直线、画层内曲线边、画光晕和实体节点、保存图片,并导出聚类结果表。

# =========================================================# 绘图# =========================================================def draw_double_layer_clustered_network(    nodes_df,    edges_df,    full_graph,    pos,    author_cluster_map,    paper_cluster_map,    cluster_theme_map,    cluster_color_map,    author_pos_xz,    paper_pos_xz,    output_png="double_layer_clustered_network_local.png",    show_labels=False):    fig, ax = plt.subplots(figsize=(18, 12), facecolor="#E9E9E9")    ax.set_facecolor("#E9E9E9")    node_theme = {}    node_color = {}    for_, row in nodes_df.iterrows():        n = row["id"]        lvl = int(row["lvl"])        cid = author_cluster_map[n] if lvl == 2 else paper_cluster_map[n]        node_theme[n] = cluster_theme_map[cid]        node_color[n] = cluster_color_map[cid]    authors = nodes_df[nodes_df["lvl"] == 2]["id"].tolist()    papers = nodes_df[nodes_df["lvl"] == 1]["id"].tolist()    G_auth = build_weighted_relation_graph(nodes_df, edges_df, "authorship")    G_col = build_weighted_relation_graph(nodes_df, edges_df, "collaboration")    G_cit = build_weighted_relation_graph(nodes_df, edges_df, "citation")    id_to_paper_count = dict(zip(nodes_df["id"], nodes_df["paper_count"]))    id_to_cit = dict(zip(nodes_df["id"], nodes_df["citation_received"]))    author_deg = dict(G_col.degree(weight="weight"))    paper_deg = dict(G_cit.degree(weight="weight"))    author_sizes = {}    for a in authors:        paper_count = id_to_paper_count.get(a, 0)        try:            paper_count = 0 if paper_count == ""elsefloat(paper_count)        except:            paper_count = 0        author_sizes[a] = 35 + 6.5 * paper_count + 15.5 * np.sqrt(max(1, author_deg.get(a, 1)))    paper_sizes = {}    for p in papers:        cit = id_to_cit.get(p, 0)        try:            cit = 0 if cit == ""elsefloat(cit)        except:            cit = 0        paper_sizes[p] = 25 + 2.5 * cit + 10.2 * np.sqrt(max(1, paper_deg.get(p, 1)))    all_author_x = [author_pos_xz[n][0] for n in authors]    all_author_z = [author_pos_xz[n][1] for n in authors]    all_paper_x = [paper_pos_xz[n][0] for n in papers]    all_paper_z = [paper_pos_xz[n][1] for n in papers]    x_min = min(min(all_author_x), min(all_paper_x)) - 3.6    x_max = max(max(all_author_x), max(all_paper_x)) + 3.6    z_min = min(min(all_author_z), min(all_paper_z)) - 3.2    z_max = max(max(all_author_z), max(all_paper_z)) + 3.2    draw_plane(ax, x_min, x_max, z_min, z_max, BOTTOM_Y, "#F6F6F6", 0.82)    draw_plane(ax, x_min, x_max, z_min, z_max, TOP_Y, "#DCE7F2", 0.40)    left_top = project_point(x_min, TOP_Y, z_max, ALPHA_DEG, BETA_DEG)    left_bottom = project_point(x_min, BOTTOM_Y, z_max, ALPHA_DEG, BETA_DEG)    ax.text(left_top[0] - 0.45, left_top[1] + 0.18, "Author Collaboration Layer",            fontsize=15, fontweight="bold", color="#355A7A")    ax.text(left_bottom[0] - 0.45, left_bottom[1] - 0.35, "Paper Citation Layer",            fontsize=15, fontweight="bold", color="#7A3E5E")    auth_edgelist = list(G_auth.edges())    auth_colors = []    auth_widths = []    foru, v in auth_edgelist:        if node_theme[u] == node_theme[v]:            auth_colors.append(to_rgba(node_color[u], 0.30))        else:            auth_colors.append((0.50, 0.50, 0.50, 0.05))        auth_widths.append(0.07 + 0.12 * np.log1p(G_auth[u][v]["weight"]))    nx.draw_networkx_edges(        G_auth, pos,        edgelist=auth_edgelist,        edge_color=auth_colors,        width=auth_widths,        ax=ax    )    citation_edge_items = []    foru, v in G_cit.edges():        target_influence = 0if id_to_cit.get(v, "") == ""elsefloat(id_to_cit.get(v, 0))        source_influence = 0if id_to_cit.get(u, "") == ""elsefloat(id_to_cit.get(u, 0))        score = 1.0 + 0.16 * np.log1p(target_influence) + 0.07 * np.log1p(source_influence)        if paper_cluster_map[u] == paper_cluster_map[v]:            score += 0.22        width = 0.18 + 0.42 * score        citation_edge_items.append((u, v, score, width))    citation_edge_items = select_top_edges(citation_edge_items, CITATION_KEEP_RATIO)    draw_curved_intralayer_edges(        ax=ax,        edge_items=citation_edge_items,        pos=pos,        node_theme=node_theme,        node_color=node_color,        same_alpha=0.34,        cross_alpha=0.08,        bend_ratio=0.12    )    collab_edge_items = []    foru, v in G_col.edges():        score = float(G_col[u][v]["weight"])        if author_cluster_map[u] == author_cluster_map[v]:            score += 0.25        width = 0.28 + 0.55 * np.log1p(G_col[u][v]["weight"])        collab_edge_items.append((u, v, score, width))    collab_edge_items = select_top_edges(collab_edge_items, COLLAB_KEEP_RATIO)    draw_curved_intralayer_edges(        ax=ax,        edge_items=collab_edge_items,        pos=pos,        node_theme=node_theme,        node_color=node_color,        same_alpha=0.42,        cross_alpha=0.10,        bend_ratio=0.10    )    nx.draw_networkx_nodes(        full_graph, pos,        nodelist=papers,        node_color=[node_color[n] for n in papers],        node_size=[paper_sizes[n] * 1.95 for n in papers],        alpha=0.08,        linewidths=0,        ax=ax    )    nx.draw_networkx_nodes(        full_graph, pos,        nodelist=authors,        node_color=[node_color[n] for n in authors],        node_size=[author_sizes[n] * 2.10 for n in authors],        alpha=0.09,        linewidths=0,        ax=ax    )    nx.draw_networkx_nodes(        full_graph, pos,        nodelist=papers,        node_color=[node_color[n] for n in papers],        node_size=[paper_sizes[n] for n in papers],        edgecolors="#2A2A2A",        linewidths=0.24,        alpha=0.97,        node_shape="o",        ax=ax    )    nx.draw_networkx_nodes(        full_graph, pos,        nodelist=authors,        node_color=[node_color[n] for n in authors],        node_size=[author_sizes[n] for n in authors],        edgecolors="#111111",        linewidths=0.42,        alpha=0.98,        node_shape="s",        ax=ax    )    if show_labels:        labels = {}        fornin authors:            if author_sizes[n] > 95:                labels[n] = n        fornin papers:            if paper_sizes[n] > 55:                labels[n] = n        nx.draw_networkx_labels(            full_graph, pos,            labels=labels,            font_size=6.8,            ax=ax        )    ax.axis("off")    plt.tight_layout()    plt.savefig(output_png, dpi=300, bbox_inches="tight", facecolor="#E9E9E9")    plt.show()

🌿 今日的分享就到这里啦~如果这些内容有为你带来帮助,欢迎轻点右下角的【👍赞】和【👀在看】,也欢迎分享给更多需要的人,感恩~

THE

END

数据和代码怎么获取?

点击关注后,后台回复关键词:

2026_map_017可直接获取完整的示例数据和代码

如有帮助,您的点赞、评论、转发是我持续创作的动力~

猜您喜欢
往期精选▼

1. Python绘制双层关系网络图(附数据和代码)

2. Python绘制SHAP+蜂巢图(附数据和代码)

3. Python绘制绝美分组环形柱状图(附数据和代码)

#数据可视化#科研绘图 #环状热图 #Python #Matplotlib #Networkx

最新文章

随机文章