You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何计算生物分析仪多峰数据的每个峰下面积

嘿,这个需求完全可以实现!我结合你现有的代码,给你拆解几个实用的方案,直接就能改到你的脚本里~

核心思路

要计算峰下面积,关键是确定每个峰的左右边界(也就是峰从哪里开始上升,到哪里回到基线),然后对这个区间的x、y值做积分。你的x轴是等间隔的(0.05秒),刚好适合用Python里的积分工具快速计算。

步骤1:确定峰的边界

首先,你已经用peakutils.indexes拿到了峰的索引,接下来要给每个峰划分对应的区间。这里分两种常用场景:

场景1:峰之间分离清晰(无重叠)

直接用相邻峰的中点作为边界,简单高效:

n_peaks = len(indices)
peak_bounds = []

for i in range(n_peaks):
    # 左边界:第一个峰从数据起始开始,其他取当前峰与前一个峰的中点
    left_idx = 0 if i == 0 else (indices[i-1] + indices[i]) // 2
    # 右边界:最后一个峰到数据末尾,其他取当前峰与后一个峰的中点
    right_idx = len(y)-1 if i == n_peaks-1 else (indices[i] + indices[i+1]) // 2
    
    peak_bounds.append( (left_idx, right_idx) )

场景2:峰有重叠或基线有波动

这种情况建议找相邻峰之间的**谷底(最小值点)**作为边界,更精准:

n_peaks = len(indices)
peak_bounds = []

for i in range(n_peaks):
    if i == 0:
        left_idx = 0  # 第一个峰左边界从数据开头开始
    else:
        # 找前一个峰到当前峰之间的最小值索引
        between_range = range(indices[i-1], indices[i])
        min_pos = indices[i-1] + np.argmin(y[between_range])
        left_idx = min_pos
    
    if i == n_peaks-1:
        right_idx = len(y)-1  # 最后一个峰右边界到数据末尾
    else:
        # 找当前峰到后一个峰之间的最小值索引
        between_range = range(indices[i], indices[i+1])
        min_pos = indices[i] + np.argmin(y[between_range])
        right_idx = min_pos
    
    peak_bounds.append( (left_idx, right_idx) )

步骤2:计算峰下面积

Python里有两个常用的积分工具,都适合你的等间隔数据:

方法1:梯形积分(numpy.trapz)

简单快速,适合大多数场景:

import numpy as np

peak_areas = []
for i, (left, right) in enumerate(peak_bounds):
    # 提取当前峰的x、y区间
    x_segment = x[left:right+1]
    y_segment = y[left:right+1]
    
    # 计算面积(因为x等间隔,也可以简化成 np.trapz(y_segment)*0.05)
    area = np.trapz(y_segment, x_segment)
    peak_areas.append({
        "peak_x": x[indices[i]],
        "peak_y": y[indices[i]],
        "area": area
    })

方法2:辛普森积分(scipy.integrate.simpson)

精度更高,适合需要更准确结果的场景:

from scipy.integrate import simpson

peak_areas = []
for i, (left, right) in enumerate(peak_bounds):
    x_segment = x[left:right+1]
    y_segment = y[left:right+1]
    
    area = simpson(y_segment, x_segment)
    peak_areas.append({
        "peak_x": x[indices[i]],
        "peak_y": y[indices[i]],
        "area": area
    })

额外优化:基线校正

如果你的数据有基线偏移(比如y值不是从0开始),建议先校正基线,计算峰的净面积:

# 假设前100个点是基线区域,计算基线均值
baseline = np.mean(y[:100])
y_corrected = y - baseline

# 之后用y_corrected代替y计算面积即可

完整整合后的代码

把上面的逻辑放到你的create_plot函数里,最终版本大概是这样:

import numpy as np
import peakutils
from scipy.integrate import simpson

def create_plot(sheet_name):
    sample = book.sheet_by_name(sheet_name)
    data = [[sample.cell_value(r, c) for r in range(sample.nrows)] for c in range(sample.ncols)]
    y = data[2][18:len(data[2]) - 2]
    x = np.arange(32, 138.05, 0.05)
    indices = peakutils.indexes(y, thres=0.35, min_dist=0.1)
    
    # 步骤1:确定峰的边界(这里用谷底法,适合重叠峰)
    n_peaks = len(indices)
    peak_bounds = []
    for i in range(n_peaks):
        if i == 0:
            left_idx = 0
        else:
            between_range = range(indices[i-1], indices[i])
            min_pos = indices[i-1] + np.argmin(y[between_range])
            left_idx = min_pos
        
        if i == n_peaks-1:
            right_idx = len(y)-1
        else:
            between_range = range(indices[i], indices[i+1])
            min_pos = indices[i] + np.argmin(y[between_range])
            right_idx = min_pos
        
        peak_bounds.append( (left_idx, right_idx) )
    
    # 步骤2:计算每个峰的面积(辛普森积分+基线校正)
    baseline = np.mean(y[:100])  # 按需调整基线区域
    y_corrected = y - baseline
    
    peak_areas = []
    for i, (left, right) in enumerate(peak_bounds):
        x_segment = x[left:right+1]
        y_segment = y_corrected[left:right+1]
        
        area = simpson(y_segment, x_segment)
        peak_areas.append({
            "peak_index": indices[i],
            "peak_time": x[indices[i]],
            "peak_absorbance": y[indices[i]],
            "net_area": area
        })
    
    return y, x, indices, peak_areas

注意事项

  • 如果你的峰非常密集或者重叠严重,可以考虑用scipy.signal.find_peaks替代peakutils.indexes,它有更多参数(比如prominence)来更精准地识别峰。
  • 边界确定的逻辑可以根据你的实际数据调整,比如如果基线有漂移,可能需要用滑动窗口计算基线。

内容的提问来源于stack exchange,提问作者Harbus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 09:11:23