当前位置:首页>python>Python绘制顶刊同款散点拟合回归组合图(附数据和代码)

Python绘制顶刊同款散点拟合回归组合图(附数据和代码)

  • 2026-09-22 15:19:00
Python绘制顶刊同款散点拟合回归组合图(附数据和代码)

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

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

最近在Nature子刊看到一幅散点图加回归拟合图,挺好看的,分享给大家。图的横轴和纵轴分别表示两个连续变量(原文里是基因组大小和基因数量),用不同符号或颜色区分不同数据来源或类别,并叠加一条拟合趋势线来展示变量之间的整体关系。这类图在科研和数据分析中非常常见,主要用途是:(1)观察两个变量之间是否存在相关性;(2)比较不同数据集或分组在该关系中的分布差异;(3)识别异常值或偏离趋势的样本;(4)辅助判断数据的一致性、质量和潜在偏差。所以,它本质上是一种用于探索变量间关系并比较多组数据模式的组合图。

原图来源:https://www.nature.com/articles/s41467-024-46374-w.pdf?spm=5176.28103460.0.0.25b963081qupfF&file=s41467-024-46374-w.pdf

01

环境配置

导入绘图、数据处理和统计所需的全部库,并集中定义全局参数(如颜色、分组顺序、坐标轴范围等)。这样只需修改一处,就能统一控制整张图的风格和输入。

# ==========================================
# 1. 全局配置:在这里改一次,全图生效
# ==========================================
import
 random
import
 numpy as np
import
 pandas as pd
import
 matplotlib.pyplot as plt
import
 matplotlib as mpl
import
 scipy.optimize as sciopt
import
 seaborn as sns
from
 mpl_toolkits.axes_grid1.inset_locator import inset_axes
from
 statannotations.Annotator import Annotator

CONFIG
 = {
    "seed"
: 10,  # 随机种子,保证每次运行生成的图、点的抖动一模一样
    "input_file"
: "data.csv",
    "colors"
: ["
#3498db", "#e84393", "#546e7a"],      # 蓝, 粉, 灰
    "hue_order"
: ["SAG", "MAG", "WGS"],
    "plot_limits"
: {
        "xlim"
: (3 * 10 ** 5, 3 * 10 ** 7),
        "ylim"
: (400, 15000),
        "xlabel"
: "Genome total size [bp] (corrected)",
        "ylabel"
: "Number of predicted CDS (corrected)"
    }
}

02

数据准备

读取 CSV 数据(若不存在则生成模拟数据),在对数空间中对基因组大小和基因数量做线性拟合,并计算每个样本的残差——用于后续分析“谁比预期多/少基因”。

# ==========================================
# 2. 数据处理:读取数据 + 计算残差(用于小图)
# ==========================================
def prepare_data(file_path):
    ""
"把原始数据读进来,并算出拟合线需要的残差"""
    try
:
        df = pd.read_csv(file_path, index_col=0)
    except FileNotFoundError:
        # 如果你还没准备好 csv,这里会生成假数据让你先能跑通
        data = {
            'length'
: np.random.uniform(10 ** 5, 10 ** 7, 500),
            'number_of_cds'
: np.random.uniform(500, 10000, 500),
            'type'
: np.random.choice(CONFIG["hue_order"], 500)
        }
        df = pd.DataFrame(data)

    # 线性回归公式:y = ax + b (在 log 空间里跑)
    def linear_func(x, a, b):
        return
 a * x + b

    # 算对数,因为生物数据通常服从幂律分布
    log_x = np.log(df["length"])
    log_y = np.log(df["number_of_cds"])

    # 拟合:得到斜率 a 和 截距 b
    popt, _ = sciopt.curve_fit(linear_func, log_x, log_y)

    # 计算残差:实际值和预测值差了多少
    df["log_cds"] = log_y
    df["log_length"] = log_x
    df["cds_fit"] = np.exp(linear_func(log_x, *popt))
    df["residuals_log"] = log_y - np.log(df["cds_fit"])

    return
 df, popt

03

主图绘制

用 seaborn 的 jointplot 画出带边缘密度图的散点图,设置对数坐标、网格、回归线,并美化图例(加入样本量 N=xxx),整体展示变量间的宏观关系。

# ==========================================
# 3. 绘图:主图(散点+边缘密度+回归线)
# ==========================================
def plot_main_joint(df, popt):
    g = sns.jointplot(
        data=df, x="length", y="number_of_cds", hue="type",
        kind="scatter", alpha=0.7, height=6,
        palette=CONFIG["colors"], hue_order=CONFIG["hue_order"],
        joint_kws=dict(s=25, edgecolor='w', linewidth=0.5),
        marginal_kws=dict(bw_adjust=0.2, fill=True)
    )

    # 消除顶部边缘图 Y 轴的科学计数法(如 1e-7)
    g.ax_marg_x.ticklabel_format(axis='y', style='plain')

    g.ax_joint.set(xscale="log", yscale="log", **CONFIG["plot_limits"])
    g.ax_joint.grid(True, which="major", ls="--", alpha=0.5, zorder=0)

    # 画回归线:在 log 空间生成点,再转回原始坐标
    x_range = np.exp(np.linspace(df["log_length"].min(), df["log_length"].max(), 100))
    y_fit = np.exp(popt[0] * np.log(x_range) + popt[1])
    g.ax_joint.plot(x_range, y_fit, color="#34495e", ls="--", lw=1.5, zorder=5)

    # 修改图例:加入样本量 N=xxx
    counts = df["type"].value_counts()
    handles, labels = g.ax_joint.get_legend_handles_labels()
    new_labels = [f"MHQ {l} (N={counts.get(l, 0)})" for l in labels if l in counts]
    g.ax_joint.legend(handles=handles, labels=new_labels, loc="upper left", fontsize=9)

    return
 g

04

小图绘制

在主图右下角嵌入一个横向箱线图,显示三类数据的残差分布,并自动添加统计显著性星号;同时用白色圆角背景提升可读性,避免与主图元素混杂。

# ==========================================
# 4. 绘图:右下角箱线图(显示残差分布 + 显著性标注)
def add_inset_boxplot(joint_grid, df):
    ax_ins = inset_axes(
        joint_grid.ax_joint,
        width="40%", height="22%",
        loc="lower right",
        bbox_to_anchor=(-0.03, 0.05, 1, 1),
        bbox_transform=joint_grid.ax_joint.transAxes
    )

    box_colors = [CONFIG["colors"][2], CONFIG["colors"][1], CONFIG["colors"][0]]
    box_args = dict(
        data=df, x="residuals_log", y="type", orient="h",
        order=["WGS", "MAG", "SAG"], palette=box_colors,
        linewidth=1, fliersize=1.5
    )
    sns.boxplot(**box_args, ax=ax_ins)

    pairs = [("WGS", "MAG"), ("WGS", "SAG"), ("MAG", "SAG")]
    annot = Annotator(ax_ins, pairs, **box_args)
    annot.configure(test="Mann-Whitney", text_format="star", loc="inside", verbose=0)
    annot.apply_and_annotate()

    ax_ins.set(xlabel=None, ylabel=None, yticks=[])
    ax_ins.set_title("Residuals (log-scale)", fontsize=9, fontweight='bold', pad=12)
    for
 s in ax_ins.spines.values():
        s.set_visible(False)

    rect = mpl.patches.FancyBboxPatch(
        (0, 0), 1, 1, transform=ax_ins.transAxes,
        facecolor="white", edgecolor="#bdc3c7", alpha=0.8,
        boxstyle="round,pad=0.1,rounding_size=0.05", clip_on=False, zorder=0
    )
    ax_ins.add_patch(rect)

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

THE

END

数据和代码怎么获取?

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

2025_map_005可直接获取完整的示例数据和代码

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

猜您喜欢
往期精选▼

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

2. Python绘制Nature期刊同款环状热图(附数据和代码)

3. Python绘制网络相关性热图

4. Python绘制Nature同款绝美雷达图(附数据和代码)

5. 99%的人不知道!Python的这个经典库竟能画出science同款火山图(附完整数据和代码)

#数据可视化#科研绘图 #组合图 #Python #Matplotlib

最新文章

随机文章