By yangyulei, 25 May, 2026

一、流程说明

1.批量统计多个榧属物种高质量蛋白序列长度,通过Mann-Whitney U检验与Cliff's Delta效应量,对比各物种与参考物种香榧的蛋白长度差异

2.绘制小提琴箱线图,横轴榧属不同物种,纵轴蛋白序列长度log10aa

 

[Cliff's Delta效应量]

非参数的效应量指标,用来比较两组数据分布差异到底有多大

使用条件:

1.两独立样本:Cliff's delta 主要用于两个独立组的比较(例如实验组和对照组),不能用于配对样本或单一样本,两组样本量可以不等,也没有最小样本量的严格限制

2.不要求正态分布,不要求方差齐性:可以是连续数据(如反应时、分数)、顺序数据(如等级排名、里克特量表),甚至是数据中存在极端异常值

3.Cliff's delta允许两组分布形状完全不同,比如一组严重偏态,一组均匀分布,当两组的分布形态差异过大时,虽然完全可以计算,但在解读时需要更谨慎。一个显著的delta值可能同时反映了“位置的偏移”和“形状的差异”,不一定单纯是总体水平的移动

4.当数据中有很多相同的数值时,Cliff's delta依然可用,但是此时效应量的绝对值可能会被轻微低估

分级:

二、环境配置

conda install -c conda-forge pandas numpy matplotlib seaborn scipy
conda install -c bioconda biopython

三、核心代码

from pathlib import Path
from Bio import SeqIO
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from scipy.stats import mannwhitneyu

FINAL_DIR = Path("/data/result/species_proteome")
target_species = ['fargesii', 'nucifera', 'taxifolia', 'jackii', 'Tgra']
OUTPUT_PDF = "/data/result/Protein_Length_Distribution.pdf"
OUTPUT_PNG = "/data/result/Protein_Length_Distribution.png"
OUTPUT_CSV = "/data/result/Effect_Size_Results.csv"

# 蛋白长度数据收集
def collect_length_data():
    data = []
    for sp in target_species:
        pep_file = FINAL_DIR / sp / f"{sp}.final.filtered.pep.fa"
        if not pep_file.exists():
            print(f"[警告] 缺失文件:{pep_file}")
            continue
        for record in SeqIO.parse(pep_file, "fasta"):
            data.append({"Species": sp, "Length (aa)": len(record.seq)})
    return pd.DataFrame(data)

# 统计检验,U检验与效应量
def run_stat_test(df):
    tgra_lengths = df[df['Species'] == 'Tgra']['Length (aa)'].values
    results = []
    for sp in ['fargesii', 'nucifera', 'taxifolia', 'jackii']:
        sp_lengths = df[df['Species'] == sp]['Length (aa)'].values
        # 曼惠特尼U检验
        stat, p_val = mannwhitneyu(sp_lengths, tgra_lengths, alternative='two-sided')
        # 计算Cliff's Delta效应量
        cliffs_d = (2.0 * stat) / (len(sp_lengths) * len(tgra_lengths)) - 1.0
        abs_d = abs(cliffs_d)
        # 效应量分级
        level = "Negligible" if abs_d < 0.147 else "Small" if abs_d < 0.330 else "Medium" if abs_d < 0.474 else "Large"
        results.append({"Species": sp, "p_value": p_val, "Cliff_Delta": round(cliffs_d,4), "Effect_Level": level})
    res_df = pd.DataFrame(results)
    res_df.to_csv(OUTPUT_CSV, index=False)
    return dict(zip(res_df['Species'], res_df.to_dict('records')))

#可视化绘图,小提琴箱线图
def plot_distribution(df, effect_dict):
    plt.figure(figsize=(11, 6.5))
    # 自定义色板
    palette = {'fargesii':'#9499C8','nucifera':'#4E79A7','jackii':'#76B7B2','taxifolia':'#59A14F','Tgra':'#FC8D62'}
    # 绘制小提琴图
    sns.violinplot(x="Species", y="Length (aa)", data=df, palette=palette, cut=0, inner=None)
    # 叠加箱线图
    sns.boxplot(x="Species", y="Length (aa)", data=df, width=0.08, color="white", showcaps=False, showfliers=False)
    # 对数坐标轴
    plt.yscale('log')
    # 标注中位数、效应量
    medians = df.groupby('Species')['Length (aa)'].median()
    for i, sp in enumerate(target_species):
        plt.text(i-0.06, medians[sp], f"{medians[sp]:.0f}", va='center', ha='right')
    # 图表美化
    plt.title("Protein Length Distribution Comparison (Torreya & T. grandis)", fontweight='bold')
    plt.xlabel("Species", fontweight='bold')
    plt.ylabel("Protein Length (Log10 Scale)", fontweight='bold')
    sns.despine()
    plt.tight_layout()
    # 保存图表
    plt.savefig(OUTPUT_PDF, dpi=300)
    plt.savefig(OUTPUT_PNG, dpi=300)

# 主程序
if __name__ == "__main__":
    df = collect_length_data()
    if not df.empty:
        print(df.groupby("Species")["Length (aa)"].describe()[['count','mean','50%']].astype(int))
        effect_dict = run_stat_test(df)
        plot_distribution(df, effect_dict)
        print("[完成] 数据统计与绘图结束!")

四、结果输出

1.统计结果文件

/data/result/Effect_Size_Results.csv,包含榧属各物种与香榧对比的p 值,Cliff's Delta 值,效应量等级

2.分析图

PDF 格式:/data/result/Protein_Length_Distribution.pdf

PNG 格式:/data/result/Protein_Length_Distribution.png

【金山文档 | WPS云文档】 可视化绘图:榧属物种蛋白长度分布与Mann-Whitney U & Cliff's Delta效应量统计检验 https://www.kdocs.cn/l/cgD0AlPtLE7t