第三章 数据处理:TGA原始数据导出、数据清洗与预处理、基线校正方法
做TGA动力学分析,最磨人的其实不是后面的拟合计算。
而是拿到原始数据后,那一堆「脏活累活」。
我刚开始带团队的时候,有个小伙子拿着热重曲线直接跑动力学模型,结果出来的活化能忽高忽低,完全没法看。后来一查,基线都没校正,数据里还混着仪器启动时的噪声。嗯,从那以后,我定了个规矩:数据处理不过关,不准进动力学计算环节。
3.1 TGA原始数据导出——别小看这一步
不同厂家的热重仪器,导出格式五花八门。
TA Instruments 给的是 .txt 或 .xls,Netzsch 喜欢用 .csv 或 .ngb-dta,PerkinElmer 则是 .txt 带表头。我个人习惯是:统一转成纯文本 CSV,编码用 UTF-8。为什么?因为后面用 Python 读数据时,最不容易出乱码。
举个例子,我常用的导出设置是这样的:
# 导出格式示例(TA Instruments Q50)
温度单位: °C
时间单位: min
重量单位: mg
数据间隔: 0.5 s/point
导出列: Time, Temp, Weight, Weight%
你想想看,如果间隔设得太密(比如0.1秒一个点),一次实验下来几万个点,后面处理起来电脑都卡。我建议等间隔采样,0.5~1秒一个点就够用了。
3.2 数据清洗与预处理——把「垃圾」筛出去
原始数据里总有些「不干净」的东西。
比如仪器刚启动时温度还没稳定,那段数据就是废的。再比如样品突然蹦了一下(热膨胀或气泡破裂),曲线上会多个毛刺。这些都得处理掉。
我一般分三步走:
- 截取有效区间:去掉升温开始前的恒温段,也去掉反应结束后的平台段。只保留从室温到最终分解完成的那一段。
- 剔除明显异常点:用滑动窗口法,计算每个点前后5个点的均值,如果偏差超过3倍标准差,就标记为异常点并剔除。
- 平滑处理:我偏爱 Savitzky-Golay 滤波器,窗口选 7~11 点,多项式阶数设 2 或 3。既能去噪声,又不损失峰的形状。
下面是我常用的 Python 清洗代码片段:
import pandas as pd
import numpy as np
from scipy.signal import savgol_filter
# 读取原始数据
df = pd.read_csv('tga_raw.csv')
# 1. 截取有效区间(温度从 50°C 到 700°C)
df = df[(df['Temp'] >= 50) & (df['Temp'] <= 700)]
# 2. 剔除异常点(基于重量变化率)
weight_diff = np.abs(np.diff(df['Weight']))
outliers = np.where(weight_diff > 3 * weight_diff.std())[0]
df = df.drop(df.index[outliers])
# 3. Savitzky-Golay 平滑
df['Weight_smooth'] = savgol_filter(df['Weight'], window_length=9, polyorder=2)
这段代码我用了好几年,基本没出过问题。但要注意——窗口长度必须是奇数,不然会报错。我曾经因为这个卡了半小时,后来才发现是 window_length 设成了 8。
3.3 基线校正方法——把「背景」去掉
基线漂移是TGA里最烦人的问题之一。
说白了,就是仪器本身的热效应、浮力效应、坩埚重量变化等,叠加在样品信号上。如果不校正,你算出来的失重台阶可能偏大或偏小,活化能自然不准。
我常用的基线校正方法有三种:
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 空白基线扣除法 | 有空白实验数据时 | 最准确,物理意义清晰 | 需要额外做一次空白实验 |
| 多项式拟合法 | 基线缓慢漂移 | 简单快速,无需额外实验 | 对剧烈漂移效果差 |
| 分段线性校正 | 基线有多个拐点 | 灵活,适应复杂基线 | 需要手动选点 |
我个人最推荐空白基线扣除法。 在同样条件下(相同升温速率、相同气氛、相同坩埚)做一次空跑,然后把样品曲线减去空白曲线。这样仪器自身的漂移就被干净地剔除了。
如果实在没有空白数据,那就用多项式拟合。我一般选 3~5 阶多项式,拟合反应开始前和结束后的平台段,然后外插到整个温度区间。代码实现如下:
from numpy.polynomial import polynomial as P
# 选取反应前(50-150°C)和反应后(600-700°C)的平台段
mask_before = (df['Temp'] >= 50) & (df['Temp'] <= 150)
mask_after = (df['Temp'] >= 600) & (df['Temp'] <= 700)
mask_baseline = mask_before | mask_after
# 多项式拟合基线
coeffs = P.polyfit(df.loc[mask_baseline, 'Temp'],
df.loc[mask_baseline, 'Weight'],
deg=4)
baseline = P.polyval(df['Temp'], coeffs)
# 校正后的重量
df['Weight_corrected'] = df['Weight'] - baseline + baseline.iloc[0]
这里有个坑:多项式阶数别太高。阶数一高,基线会在中间区域乱抖,反而引入假信号。我一般不超过 5 阶。
3.4 本章知识体系一览
下面这张图,是我自己梳理的数据处理流程。每次带新人,我都先让他们看这张图,心里有个全局概念再动手。
你看,整个流程其实就三步:导出→清洗→校正。但每一步都有细节,细节里藏着坑。我见过太多人急着跑动力学模型,结果数据没处理好,算出来的活化能忽大忽小,还以为是模型选错了。其实问题就出在数据预处理上。
好了,数据处理这部分就聊到这儿。记住一句话:垃圾进,垃圾出。数据预处理花的时间,会在后面的动力学分析里加倍还给你。
公众号:蓝海资料掘金营,微信deep3321