基于matplotlib的饼图绘制

数据可视化

数据分析以及可视化的最终目标就是化繁为简,直观地呈现数据的特征和差异,以便论文里能够简单清晰地讲清楚。饼图虽然简单,但是有时候也很有用。

在前面对四面体模拟的数据进行了三维以及二维的可视化。

基于相对距离求解点的空间坐标

本文记录一个立体几何相关的基于距离求解坐标的函数。 先直接看效果: 该部分代码使用Grok生成后进行测试修改迭代完成。提示词如下: 测试过程中出现了一些问题,依次反馈: AttributeError: module 'numpy' has no attribute 'sqrt26sqrt' Valu...

基于三维坐标系变换的数据分布范围平面可视化

要判断一个东西是否在一个三维区域的内部还是外部是很简单的,但是要直观地展示出来这种三维空间的内外关系,需要一些技巧。这里举个例子。 先看结果: 如上图所示,目标分子使用散点表示,如果在四面体内部则标记为绿色,如果在外部则标记为红色。这样能够已经能够比较好地表示内外的情况了。但是pdf不像html支持...

用户希望能够使用更简单的指标或者数值来表示来评估四面体对包裹的小分子的约束能力。所以这里基于前述步骤产生的results表格,利用matplotlib进一步绘制。经过讨论后确定了一个简单的指标就是 inside 占比,更进一步的可以查看这个占比随模拟时间的变化,因为分子模拟受初值和随机化影响,他们通过RMSD分析确定100ns之后的趋于稳定,所以这里可以统计这个时间点之后的占比。

先看效果:

1236239062.svg

再放代码:

import numpy as np
import pandas as pd
from glob import glob
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
%config InlineBackend.figure_format='svg'
fps = glob("out/*.xlsx")
for fp in fps:
df = pd.read_excel(fp, index_col=0)
# 将 Frame 转换为 ns
df['Time_ns'] = df['Frame'] * 0.1
# --- 数据准备:折线图所需数据 ---
bin_size_frame = 100
df['bin'] = (df['Frame'] // bin_size_frame) * bin_size_frame
bin_summary = df.groupby('bin')['inside'].agg(['sum', 'count']).reset_index()
bin_summary.rename(columns={'sum': 'true_count', 'count': 'total_count'}, inplace=True)
bin_summary['proportion_true'] = bin_summary['true_count'] / bin_summary['total_count']
bin_summary['Time_ns'] = bin_summary['bin'] * 0.1
# --- 数据准备:饼图所需数据 ---
# 筛选 Time_ns >= 100 的数据
df_filtered = df[df['Time_ns'] >= 100].copy()
if not df_filtered.empty:
# 计算 inside 为 True 和 False 的数量
inside_counts = df_filtered['inside'].value_counts()
# 使用 reindex 确保 'True' 和 'False' 都在索引中,如果缺失则填充 0
# 这是解决 KeyError 的关键
inside_counts = inside_counts.reindex([True, False], fill_value=0)
# 准备圆饼图的数据
labels = ['Inside', 'Not Inside']
sizes = [inside_counts[True], inside_counts[False]]
colors = ["#17e135", "#e14141"] # 可以自定义颜色
else:
print("没有 Time_ns >= 100 ns 的数据,饼图将为空或显示错误。")
labels = []
sizes = []
colors = []
# --- 合并绘图 ---
# 创建一个 1 行 2 列的子图布局
fig, axes = plt.subplots(1, 2, figsize=(12, 4)) # 调整整体 figure 大小以容纳两个图
# 左侧子图:折线图
sns.lineplot(data=bin_summary, x='Time_ns', y='proportion_true', marker='o', ax=axes[0])
axes[0].set_title(f'Proportion of "inside" over Time (bin size = {bin_size_frame * 0.1} ns)')
axes[0].set_xlabel('Time (ns)')
axes[0].set_ylabel('Proportion of "inside"')
axes[0].set_ylim(-.1, 1.1)
axes[0].grid(True, linestyle='--', alpha=0.7)
# 右侧子图:饼图
if not df_filtered.empty: # 只有在有数据时才绘制饼图
axes[1].pie(sizes, labels=labels, colors=colors, autopct='%1.1f%%', startangle=90, explode=(0.05, 0))
axes[1].set_title('Proportion of "inside" for Time >= 100 ns')
axes[1].axis('equal') # 确保饼图是圆形
else:
axes[1].text(0.5, 0.5, "No data for Time >= 100 ns", horizontalalignment='center', verticalalignment='center', transform=axes[1].transAxes)
axes[1].set_xticks([])
axes[1].set_yticks([])
axes[1].set_title('Proportion of "inside" for Time >= 100 ns')
plt.suptitle(fp.split(".xlsx")[0].split("\\")[1].replace("_results", ""))
plt.tight_layout() # 自动调整子图参数,使之紧密地排列在图的区域内
plt.show()

饼图绘制的命令本身很简单,但是需要注意某一类为0的情况。以上代码都是在 gemini 2.5 flash 帮助下完成。