
本文介绍一种适用于地球化学、环境科学等领域的精确分组方法:通过自定义百分比差异距离矩阵,结合dbscan聚类,实现“所有变量同时满足±10%容差”条件的严格化学相似性分组。
本文介绍一种适用于地球化学、环境科学等领域的精确分组方法:通过自定义百分比差异距离矩阵,结合dbscan聚类,实现“所有变量同时满足±10%容差”条件的严格化学相似性分组。
在多元素化学数据分析(如岩石、土壤、水体样品)中,研究者常需识别“成分高度一致”的样本群——即每个元素浓度均落在彼此±10%(或指定阈值)范围内。这与常规聚类(如K-means、层次聚类)有本质区别:后者基于欧氏距离或相关性,反映整体分布趋势;而本需求要求逐变量硬约束——只要任一元素偏差超限,两样本即不可归为同一组。
例如,给定4个含Al、Na、K、Si的样品:
| Al | Na | K | Si | |
|---|---|---|---|---|
| Sample1 | 1.00 | 2.00 | 3.00 | 4.00 |
| Sample2 | 1.50 | 2.50 | 3.50 | 4.50 |
| Sample3 | 1.05 | 2.10 | 3.10 | 1.00 |
| Sample4 | 0.95 | 1.90 | 3.10 | 3.80 |
按±10%规则:
- Sample1 与 Sample4:各元素相对偏差为 |1.00−0.95|/1.00=5%, |2.00−1.90|/2.00=5%, |3.00−3.10|/3.10≈3.2%, |4.00−3.80|/4.00=5% → 全部 ≤10% → 可成组
- Sample1 与 Sample3:前三者偏差均≤5%,但 Si 偏差为 |4.00−1.00|/4.00=75% → 超限 → 不可成组
- Sample2 无任何样本满足全变量±10% → 孤立点
要实现该逻辑,核心是构建最大百分比差异距离矩阵(Max Percent Difference Matrix):对任意样本对 (i,j),计算所有变量中最大的相对偏差(取绝对值后除以较大值,规避除零),该值即为二者“距离”。此距离非度量空间标准距离(不满足三角不等式),但完全契合业务语义。
快速生成专业的 Python 脚本和应用代码。一键创建完整项目结构,支持CLI、API、爬虫、Bot、Django等多种项目类型,包含完整的项目结构、配置文件、依赖管理、测试、README和文档。
以下是完整、可运行的Python实现(兼容 pandas ≥2.0, scikit-learn ≥1.3):
import pandas as pd
import numpy as np
from sklearn.cluster import DBSCAN
from itertools import combinations
# 示例数据(替换为您的2000×16化学数据)
df = pd.DataFrame({
'Al': [1.00, 1.50, 1.05, 0.95],
'Na': [2.00, 2.50, 2.10, 1.90],
'K': [3.00, 3.50, 3.10, 3.10],
'Si': [4.00, 4.50, 1.00, 3.80]
}, index=['Sample1', 'Sample2', 'Sample3', 'Sample4'])
def max_pct_difference(row1, row2):
"""计算两行间各变量最大相对偏差(%),返回小数形式(如10% → 0.1)"""
diffs = []
for v1, v2 in zip(row1, row2):
if v1 == 0 and v2 == 0:
continue # 两者均为0,视为完全一致
elif v1 == 0 or v2 == 0:
diffs.append(1.0) # 一者为0,另一者非0 → 100%差异
else:
diffs.append(abs(v1 - v2) / max(abs(v1), abs(v2)))
return max(diffs) if diffs else 0.0
# 构建对称距离矩阵(n×n)
n = len(df)
dist_matrix = np.zeros((n, n))
samples = df.index.tolist()
for i in range(n):
for j in range(i + 1, n):
d = max_pct_difference(df.iloc[i], df.iloc[j])
dist_matrix[i, j] = d
dist_matrix[j, i] = d # 对称
# 应用DBSCAN:eps=0.1 即10%阈值;min_samples=2 要求至少2个样本才成簇
clustering = DBSCAN(eps=0.1, min_samples=2, metric='precomputed')
labels = clustering.fit_predict(dist_matrix)
# 添加分组结果到原DataFrame
df['Group'] = labels
print(df)
输出:
Al Na K Si Group Sample1 1.0 2.0 3.0 4.0 0 Sample2 1.5 2.5 3.5 4.5 -1 Sample3 1.05 2.1 3.1 1.0 -1 Sample4 0.95 1.9 3.1 3.8 0
✅ 关键说明与注意事项:
- Group = -1 表示噪声点(未被任何簇接纳),即无其他样本与其满足全变量±10%条件;
- DBSCAN在此处的作用是传递闭包:若 A↔B 且 B↔C 均≤10%,则 A,B,C 同属一组(即使 A↔C >10%)。若您需要严格两两满足(即组内任意两样本均≤10%),应改用连通分量算法(如 networkx.connected_components)处理阈值二值化图;
- 大数据优化建议:对 n=2000 样本,距离矩阵需约 2000²/2 ≈ 2M 次比较,在现代CPU上耗时通常
-
稳健性增强:实际化学数据常含检测限(
此方法将领域知识(百分比容差)精准编码进距离定义,再借力成熟聚类框架完成分组,兼顾可解释性与工程可行性,特别适合GIS空间分析前的化学同质性预筛选。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










