如何用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
相关产品推荐
相关产品推荐

