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

如何用R基于特定结构的学生成绩数据绘制Sankey Diagram

如何用桑基图可视化学生跨课程成绩分组的变化?

问题说明

我有一组结构清晰的学生跨课程成绩数据,记录了每个学生在不同课程中的成绩分组(Top third/Middle third/Bottom third)。想要绘制一幅桑基图,直观展示学生的成绩分组在三门课程(Eng101 → Calc101 → Hist101)之间的流转变化。

原始数据集

Course     St_ID    Achievement
Eng101     St_A     Top third
Eng101     St_B     Top third
Eng101     St_C     Middle third
Eng101     St_D     Middle third    
Eng101     St_E     Bottom third
Eng101     St_F     Bottom third
Calc101    St_A     Top third
Calc101    St_B     Bottom third
Calc101    St_C     Bottom third
Calc101    St_D     Top third
Calc101    St_E     Middle third
Calc101    St_F     Middle third
Hist101    St_A     Bottom third
Hist101    St_B     Bottom third
Hist101    St_C     Middle third
Hist101    St_D     Top third
Hist101    St_E     Middle third
Hist101    St_F     Top third

预期效果

生成的桑基图需包含三列节点,每列对应一门课程,列内节点分别对应三个成绩分组;节点间的连线粗细代表从前者流转到后者的学生数量,清晰呈现成绩分组的变化路径。


实现方案(Python)

步骤1:数据预处理

桑基图需要的是分组间的流转计数数据,所以先把原始的学生级数据转换成统计后的流转数据:

import pandas as pd

# 构造数据集
data = [
    ["Eng101", "St_A", "Top third"],
    ["Eng101", "St_B", "Top third"],
    ["Eng101", "St_C", "Middle third"],
    ["Eng101", "St_D", "Middle third"],
    ["Eng101", "St_E", "Bottom third"],
    ["Eng101", "St_F", "Bottom third"],
    ["Calc101", "St_A", "Top third"],
    ["Calc101", "St_B", "Bottom third"],
    ["Calc101", "St_C", "Bottom third"],
    ["Calc101", "St_D", "Top third"],
    ["Calc101", "St_E", "Middle third"],
    ["Calc101", "St_F", "Middle third"],
    ["Hist101", "St_A", "Bottom third"],
    ["Hist101", "St_B", "Bottom third"],
    ["Hist101", "St_C", "Middle third"],
    ["Hist101", "St_D", "Top third"],
    ["Hist101", "St_E", "Middle third"],
    ["Hist101", "St_F", "Top third"],
]
df = pd.DataFrame(data, columns=["Course", "St_ID", "Achievement"])

# 按学生ID整理出每个学生的跨课程成绩路径
student_paths = df.pivot(index="St_ID", columns="Course", values="Achievement")

# 统计相邻课程间的分组流转人数
eng_calc_flows = student_paths.groupby(["Eng101", "Calc101"]).size().reset_index(name="count")
calc_hist_flows = student_paths.groupby(["Calc101", "Hist101"]).size().reset_index(name="count")

步骤2:用Plotly生成交互式桑基图

Plotly的桑基图API简单易用,支持交互式操作(比如hover查看具体数值):

import plotly.graph_objects as go

# 定义所有节点:格式为「课程名: 成绩分组」
nodes = [
    "Eng101: Top third", "Eng101: Middle third", "Eng101: Bottom third",
    "Calc101: Top third", "Calc101: Middle third", "Calc101: Bottom third",
    "Hist101: Top third", "Hist101: Middle third", "Hist101: Bottom third"
]
# 给每个节点分配索引
node_index = {node: idx for idx, node in enumerate(nodes)}

# 整理流转数据:起始节点索引、目标节点索引、流转人数
sources = []
targets = []
values = []

# 处理Eng101到Calc101的流转
for _, row in eng_calc_flows.iterrows():
    sources.append(node_index[f"Eng101: {row['Eng101']}"])
    targets.append(node_index[f"Calc101: {row['Calc101']}"])
    values.append(row["count"])

# 处理Calc101到Hist101的流转
for _, row in calc_hist_flows.iterrows():
    sources.append(node_index[f"Calc101: {row['Calc101']}"])
    targets.append(node_index[f"Hist101: {row['Hist101']}"])
    values.append(row["count"])

# 绘制桑基图
fig = go.Figure(data=[go.Sankey(
    node=dict(
        pad=15,  # 节点间距
        thickness=20,  # 节点厚度
        line=dict(color="black", width=0.5),
        label=nodes
    ),
    link=dict(
        source=sources,
        target=targets,
        value=values
    )
)])

fig.update_layout(title_text="学生跨课程成绩分组流转桑基图", font_size=10)
fig.show()

步骤3:用Matplotlib生成静态桑基图

如果需要静态图表,可以使用Matplotlib的Sankey模块:

import matplotlib.pyplot as plt
from matplotlib.sankey import Sankey

fig = plt.figure(figsize=(12, 6))
ax = fig.add_subplot(1, 1, 1)

# 绘制第一段:Eng101 → Calc101
sankey1 = Sankey(
    ax=ax, scale=0.1, unit="students",
    flows=[2, 2, 2, -2, -2, -2],  # 正数值为流入,负数值为流出
    labels=[
        "Eng101: Top", "Eng101: Middle", "Eng101: Bottom",
        "Calc101: Top", "Calc101: Middle", "Calc101: Bottom"
    ],
    orientations=[1, 1, 1, -1, -1, -1]  # 节点方向:1=向上,-1=向下
)

# 添加第二段:Calc101 → Hist101
sankey2 = sankey1.add(
    flows=[2, 2, 2, -2, -2, -2],
    labels=["", "", "", "Hist101: Top", "Hist101: Middle", "Hist101: Bottom"],
    orientations=[1, 1, 1, -1, -1, -1],
    prior=0, connect=(3, 0), (4, 1), (5, 2)  # 连接前一段的节点
)

# 完成绘制并展示
diagrams = sankey2.finish()
plt.title("学生跨课程成绩分组流转桑基图")
plt.show()

内容的提问来源于stack exchange,提问作者Alokin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 23:24:53