期刊复现| Python 实现带边缘密度与残差检验的回归拟合图

论文地址:

https://www.nature.com/articles/s41467-024-46374-w

论文题目:

Genome-scale community modelling reveals conserved metabolic cross-feedings in epipelagic bacterioplankton communities

复现图片:

配色方案:

python 复制代码
COLOR_SCHEMES = {
    1: ['#4d4d4d', '#ff9f4b', '#1f77b4'],#本文选择该配色
    2: ['#1D3557', '#E63946', '#A8DADC'],
    3: ['#2B2D42', '#EF233C', '#56E39F'],
    4: ['#E9C46A', '#264653', '#E76F51'],
    5: ['#2A9D8F', '#BC4749', '#6C757D'],
    6: ['#7209B7', '#FFD60A', '#121212'],
    7: ['#003049', '#D62828', '#EAE2B7'],
    8: ['#06D6A0', '#9D4EDD', '#FF6B6B'],
    9: ['#212529', '#D90429', '#F8F9FA'],
    10: ['#606C38', '#DDA15E', '#BC6C25'],
    11: ['#023E8A', '#48CAE4', '#FF9F1C'],
    12: ['#8AB17D', '#B5838D', '#E5989B'],
    13: ['#1B4332', '#D4AF37', '#4A0404'],
    14: ['#00509D', '#CCFF33', '#000000'],
    15: ['#A2D2FF', '#FF0054', '#FFBD00'],
    16: ['#6C757D', '#FFD166', '#118AB2'],
    17: ['#5A189A', '#FB8500', '#2EC4B6'],
    18: ['#000000', '#FFFFFF', '#C9A227'],
    19: ['#240046', '#00B4D8', '#FF006E'],
    20: ['#583101', '#E36414', '#8ECAE6'],
    21: ['#2B2B2B', '#FFE66D', '#4ECDC4'],
    22: ['#4F3222', '#9EF01A', '#F0EFEB'],
    23: ['#40E0D0', '#DC143C', '#2F4F4F'],
    24: ['#3E2723', '#81C784', '#FFF3E0'],
    25: ['#191970', '#FF1493', '#FFFF00'],
    26: ['#22333B', '#AE2012', '#E09F3E'],
    27: ['#99E2B4', '#FFADAD', '#CDB4DB'],
    28: ['#414833', '#A47148', '#1A1A1A'],
    29: ['#4CC9F0', '#F72585', '#F9C74F'],
    30: ['#14213D', '#800020', '#D3D3D3'],
    31: ['#10B981', '#EF4444', '#F59E0B'],
    32: ['#0077B6', '#F4A261', '#E76F51'],
    33: ['#5D2E8E', '#FDB927', '#F0F0F0'],
    34: ['#132A13', '#D00000', '#ADB5BD'],
    35: ['#0021f3', '#c65d08', '#f5f5f5'],
    36: ['#004225', '#FF0000', '#343A40'],
    37: ['#3A0CA3', '#CCFF00', '#FF7B00'],
    38: ['#9F86C0', '#5E548E', '#57CC99'],
    39: ['#000000', '#880808', '#C0C0C0'],
    40: ['#F8F9FA', '#03045E', '#B5179E']
}

拆分子图:

图 1 散点回归图:该图展示两种测温方法测量结果的线性相关关系。横坐标为方法 1 测得温度,纵坐标为方法 2 测得温度,红色虚线为整体线性拟合线。不同颜色散点区分 A、B、C 三组样本,散点越贴近拟合线,代表两种检测方法的测量一致性越好。正式图注:图 1 两种测温方法测量结果的散点分布及线性拟合关系。

图 2 X 轴密度图:解读:该图为方法 1 测量数据的核密度分布曲线,不同颜色曲线分别对应三组样本。曲线峰值代表数据集中区间,可直观反映方法 1 测量值的分布范围、集中程度与离散特征。正式图注:图 2 测温方法 1 测量数据的核密度分布。

图 3 Y 轴密度图:解读:该图为方法 2 测量数据的核密度分布曲线,用于分析三组样本下方法 2 测量值的整体分布规律,判断数据是否集中、有无明显偏移或极端分布特征。正式图注:图 3 测温方法 2 测量数据的核密度分布。

图 4 残差箱线图:解读:该图为回归模型残差箱线图,残差表征两种方法的测量误差。箱体位置与宽度反映误差大小和稳定性,图中标注的显著性符号基于 Mann-Whitney U 检验,用于判定三组样本的测量误差是否存在统计学差异。正式图注:图 4 各组测量残差分布及组间显著性差异检验结果。

图 5 完整组合图:解读:该综合图整合了相关性、数据分布与误差分析结果。主体为两种测温方法的散点拟合图,上方与右侧分别对应两组数据的核密度分布,内嵌箱线图展示残差及组间差异,可全面评价方法的一致性与可靠性。正式图注:图 5 两种测温方法测量结果的综合分析图。

完整代码:

python 复制代码
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.gridspec import GridSpec
from scipy import stats
import pandas as pd
import matplotlib
import os

# ====================== 自动创建文件夹 ======================
save_dir = "图表"
if not os.path.exists(save_dir):
    os.makedirs(save_dir)

# ====================== 全局字体样式 ======================
matplotlib.rcParams['pdf.fonttype'] = 42
matplotlib.rcParams['ps.fonttype'] = 42
plt.rcParams['font.family'] = 'serif'
plt.rcParams['font.serif'] = ['Times New Roman']
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['mathtext.fontset'] = 'stix'
plt.rcParams['font.size'] = 12

# ====================== 配色方案 ======================
COLOR_SCHEMES = {
    1: ['#4d4d4d', '#ff9f4b', '#1f77b4']
}
# 选择配色方案
scheme_index = 1
colors = COLOR_SCHEMES[scheme_index]
c_a, c_b, c_c = colors[0], colors[1], colors[2]
bandwidth = 0.05

# ====================== 读取数据 + 线性回归 ======================
df = pd.read_excel("data.xlsx")
all_x = df['X'].values
all_y = df['Y'].values
slope_fit, intercept_fit, r_value, p_value, std_err = stats.linregress(all_x, all_y)

# ====================== 分组数据处理 ======================
def process_group(group_name):
    sub_df = df[df['Group'] == group_name]
    if sub_df.empty:
        return np.array([]), np.array([]), np.array([])
    x = sub_df['X'].values
    y = sub_df['Y'].values
    pred = slope_fit * x + intercept_fit
    resid = y - pred
    return x, y, resid

grp_a_x, grp_a_y, resid_a = process_group('A')
grp_b_x, grp_b_y, resid_b = process_group('B')
grp_c_x, grp_c_y, resid_c = process_group('C')
all_resid = np.concatenate([resid_a, resid_b, resid_c])

# ====================== 显著性标记函数 ======================
def get_stars(p):
    if p < 0.0001: return "****"
    elif p < 0.001: return "***"
    elif p < 0.01: return "**"
    elif p < 0.05: return "*"
    else: return "ns"

def add_sig(ax, d1, d2, y1, y2, level):
    stat, p = stats.mannwhitneyu(d1, d2, alternative='two-sided')
    star = get_stars(p)
    global_max = np.max(all_resid)
    if global_max < 2: global_max = 2
    x_line = global_max + 2.0 + (level - 1) * 8.0
    ax.plot([x_line, x_line+1, x_line+1, x_line], [y1, y1, y2, y2], lw=1.2, c='k')
    ax.text(x_line+1.5, (y1+y2)/2, star, ha='left', va='center', rotation=270, fontsize=10, fontweight='bold')

# ===================================== 图1:散点回归图 =====================================
fig, ax = plt.subplots(figsize=(8, 8))
ax.scatter(grp_a_x, grp_a_y, c=c_a, alpha=0.3, s=20, label='Group A', edgecolors='none')
ax.scatter(grp_b_x, grp_b_y, c=c_b, alpha=0.3, s=20, label='Group B', edgecolors='none')
ax.scatter(grp_c_x, grp_c_y, c=c_c, alpha=0.6, s=30, label='Group C', edgecolors='none')
line_x = np.linspace(-65, 65, 100)
line_y = slope_fit * line_x + intercept_fit
ax.plot(line_x, line_y, 'r--', lw=2.5, zorder=10)
ax.set_xlabel('Temperature Method 1 (℃)', fontsize=14, fontweight='bold')
ax.set_ylabel('Temperature Method 2 (℃)', fontsize=14, fontweight='bold')
ax.legend(loc='upper left', fontsize=12, frameon=True, framealpha=0.9, edgecolor='black')
ax.set_xlim(-70, 70)
ax.set_ylim(-70, 70)
ax.spines[['top', 'right']].set_visible(False)
ax.minorticks_on()
plt.tight_layout()
plt.savefig(os.path.join(save_dir, "1_散点回归图.png"), dpi=300, bbox_inches='tight')
plt.close()

# ===================================== 图2:X轴密度图 =====================================
fig, ax = plt.subplots(figsize=(8, 3))
sns.kdeplot(grp_a_x, fill=True, color=c_a, alpha=0.3, lw=1, bw_adjust=bandwidth)
sns.kdeplot(grp_b_x, fill=True, color=c_b, alpha=0.3, lw=1, bw_adjust=bandwidth)
sns.kdeplot(grp_c_x, fill=True, color=c_c, alpha=0.3, lw=1, bw_adjust=bandwidth)
ax.set_xlabel('Temperature Method 1 (℃)', fontweight='bold')
ax.set_yticks([])
ax.spines[['top', 'right', 'left']].set_visible(False)
plt.tight_layout()
plt.savefig(os.path.join(save_dir, "2_X轴密度图.png"), dpi=300, bbox_inches='tight')
plt.close()

# ===================================== 图3:Y轴密度图=====================================
fig, ax = plt.subplots(figsize=(8, 3))
sns.kdeplot(grp_a_y, fill=True, color=c_a, alpha=0.3, lw=1, bw_adjust=bandwidth)
sns.kdeplot(grp_b_y, fill=True, color=c_b, alpha=0.3, lw=1, bw_adjust=bandwidth)
sns.kdeplot(grp_c_y, fill=True, color=c_c, alpha=0.3, lw=1, bw_adjust=bandwidth)
ax.set_xlabel('Temperature Method 2 (℃)', fontweight='bold')
ax.set_yticks([])
ax.spines[['top', 'right', 'left']].set_visible(False)
plt.tight_layout()
plt.savefig(os.path.join(save_dir, "3_Y轴密度图.png"), dpi=300, bbox_inches='tight')
plt.close()

# ===================================== 图4:残差箱线图 =====================================
fig, ax = plt.subplots(figsize=(8, 4))
box_data = [resid_c, resid_b, resid_a]
positions = [1,2,3]
box_colors = [c_c, c_b, c_a]
bplot = ax.boxplot(box_data, vert=False, patch_artist=True, positions=positions, widths=0.5,
                   flierprops=dict(marker='d', markersize=4, markerfacecolor='black', alpha=0.5),
                   boxprops=dict(lw=1.5), medianprops=dict(lw=1.5, color='black'),
                   whiskerprops=dict(lw=1.5), capprops=dict(lw=1.5))
for patch, color in zip(bplot['boxes'], box_colors):
    patch.set_facecolor(color)
    patch.set_alpha(0.8)
ax.set_title("Residuals", fontweight='bold')
ax.set_yticks([])
add_sig(ax, resid_c, resid_b, 1,2,1)
add_sig(ax, resid_b, resid_a, 2,3,2)
add_sig(ax, resid_c, resid_a, 1,3,3)
ax.set_xlim(ax.get_xlim()[0], ax.get_xlim()[1]*1.3)
ax.minorticks_on()
plt.tight_layout()
plt.savefig(os.path.join(save_dir, "4_残差箱线图.png"), dpi=300, bbox_inches='tight')
plt.close()

# ===================================== 图5:完整组合大图 =====================================
fig = plt.figure(figsize=(10,10))
gs = GridSpec(2,2, figure=fig, hspace=0.05, wspace=0.05, height_ratios=[1,5], width_ratios=[5,1])
ax_main = fig.add_subplot(gs[1,0])
ax_top = fig.add_subplot(gs[0,0], sharex=ax_main)
ax_right = fig.add_subplot(gs[1,1], sharey=ax_main)

ax_main.scatter(grp_a_x, grp_a_y, c=c_a, alpha=0.3, s=20, label='Group A', edgecolors='none')
ax_main.scatter(grp_b_x, grp_b_y, c=c_b, alpha=0.3, s=20, label='Group B', edgecolors='none')
ax_main.scatter(grp_c_x, grp_c_y, c=c_c, alpha=0.6, s=30, label='Group C', edgecolors='none')
ax_main.plot(line_x, line_y, 'r--', lw=2.5, zorder=10)
ax_main.set_xlabel('Temperature Method 1 (℃)', fontsize=14, fontweight='bold')
ax_main.set_ylabel('Temperature Method 2 (℃)', fontsize=14, fontweight='bold')
ax_main.legend(loc='upper left', fontsize=12, frameon=True, framealpha=0.9, edgecolor='black')
ax_main.set_xlim(-70,70)
ax_main.set_ylim(-70,70)

sns.kdeplot(grp_a_x, ax=ax_top, color=c_a, fill=True, alpha=0.3, lw=1, bw_adjust=bandwidth)
sns.kdeplot(grp_b_x, ax=ax_top, color=c_b, fill=True, alpha=0.3, lw=1, bw_adjust=bandwidth)
sns.kdeplot(grp_c_x, ax=ax_top, color=c_c, fill=True, alpha=0.3, lw=1, bw_adjust=bandwidth)
ax_top.set_yticks([])
ax_top.set_xlabel("")
ax_top.set_ylabel("")

sns.kdeplot(y=grp_a_y, ax=ax_right, color=c_a, fill=True, alpha=0.3, lw=1, bw_adjust=bandwidth)
sns.kdeplot(y=grp_b_y, ax=ax_right, color=c_b, fill=True, alpha=0.3, lw=1, bw_adjust=bandwidth)
sns.kdeplot(y=grp_c_y, ax=ax_right, color=c_c, fill=True, alpha=0.3, lw=1, bw_adjust=bandwidth)
ax_right.set_xticks([])
ax_right.set_xlabel("")
ax_right.set_ylabel("")

ax_inset = ax_main.inset_axes([0.5, 0.05, 0.45, 0.25])
bplot_inset = ax_inset.boxplot(box_data, vert=False, patch_artist=True, positions=positions, widths=0.5,
                               flierprops=dict(marker='d', markersize=4, markerfacecolor='black', alpha=0.5),
                               boxprops=dict(lw=1.5), medianprops=dict(lw=1.5, color='black'),
                               whiskerprops=dict(lw=1.5), capprops=dict(lw=1.5))
for patch, color in zip(bplot_inset['boxes'], box_colors):
    patch.set_facecolor(color)
    patch.set_alpha(0.8)
ax_inset.set_title("Residuals", fontweight='bold')
ax_inset.set_yticks([])
add_sig(ax_inset, resid_c, resid_b, 1,2,1)
add_sig(ax_inset, resid_b, resid_a, 2,3,2)
add_sig(ax_inset, resid_c, resid_a, 1,3,3)
ax_inset.set_xlim(ax_inset.get_xlim()[0], ax_inset.get_xlim()[1]*1.3)

all_axes = [ax_main, ax_top, ax_right, ax_inset]
for ax in all_axes:
    for sp in ax.spines.values():
        sp.set_lw(1.5)
    ax.minorticks_on()
    ax.tick_params(which='major', width=1.5, length=4)
    ax.tick_params(which='minor', width=1, length=2)

ax_main.spines[['top','right']].set_visible(False)
ax_top.spines[['top','right','left']].set_visible(False)
ax_right.spines[['top','right','bottom']].set_visible(False)
ax_top.tick_params(axis='x', labelbottom=False)
ax_right.tick_params(axis='y', labelleft=False)

plt.savefig(os.path.join(save_dir, "5_完整组合图.png"), dpi=300, bbox_inches='tight')
plt.close()

print("全部 5 张图已生成完毕!保存在文件夹:", save_dir)

数据获取:

评论+私信获取

VX获取:期刊复现| Python 实现带边缘密度与残差检验的回归拟合图

参考来源:python+遥感学习日志

相关推荐
deepin_sir2 小时前
14 - 面向对象编程
开发语言·python
知识分享小能手2 小时前
Flask入门学习教程,从入门到精通,Flask智能租房——列表页 知识点详解(7)
python·学习·flask
极客小云2 小时前
【从 while 循环到可视化智能体:深入拆解 Agent Loop、Codex 风格工具调用、OpenClaw 与 Hermes 背后的技术细节】
数据库·python·大模型·agent·codex·openclaw·hermes
Larcher2 小时前
Python List、切片与大模型:从入门到实践的优雅之旅
python·ai编程
用户6337197359012 小时前
_winapi.CreateProcess....FileNotFoundError: [WinError 2] 系统找不到指定的文件
python
清水白石0082 小时前
Python 数据建模指南:dataclass、TypedDict 与 Pydantic 的选型博弈
前端·javascript·python
小郑加油2 小时前
python_综合训练
开发语言·python
葬送的代码人生2 小时前
Notebook环境下的List、Slice与LLM大冒险
python·jupyter·api
多彩电脑2 小时前
Kivy的事件向方法传递的event是什么?
开发语言·python