我在编程方面是个新手,但使用 GIS 软件在空间上绘制测试了多个变量(n=16 个元素)的样本(n=2000)。我正在尝试使用 Python 或 R(或实际上的任何语言)根据所有测试元素的相对相似性 (%) 来统计划分化学相关的基团。
使用值的 + 或 - 10% 的示例:
Al Na K Si
Sample1 1 2 3 4
Sample2 1.5 2.5 3.5 4.5
Sample3 1.05 2.1 3.1 1
Sample4 0.95 1.9 3.1 3.8
想要的结果:
我查看了一些线程,但似乎没有一个线程将所有变量关联在一起,并且只会根据一个线程进行分组。
由于您想要按值进行比较,因此 DBSCAN 或 K 均值等标准聚类方法将不起作用,因为它们使用点之间的聚合距离。
为此,您可以通过将每个项目与所有其他项目进行比较来找到每个值的最大差异(以百分比形式)来进行暴力破解。函数
max_pct_difference
计算最大百分比差异。这些 X 到 Y 的差异创建了您感兴趣的“距离”矩阵的下半部分。
通过预先计算距离矩阵,您可以在 DBSCAN 等聚类算法中使用它。这会将距离小于参数 epsilon 的相似项目分组在一起。但您必须注意,并非组中的所有项目的距离都小于 10%。这是因为您可以得到这样的样本:A 在 B 的 10% 范围内,B 在 C 的 10% 范围内,但 A 和 C 不在 10% 范围内:
A 1.0 1.0 1.0 2.1
B 1.0 1.0 1.0 2.3
C 1.0 1.0 1.0 2.4
这里有一些代码可以帮助您入门:
import pandas as pd
from itertools import combinations_with_replacement
df = pd.DataFrame(
{'Al': {'Sample1': 1.0, 'Sample2': 1.5, 'Sample3': 1.05, 'Sample4': 0.95},
'Na': {'Sample1': 2.0, 'Sample2': 2.5, 'Sample3': 2.1, 'Sample4': 1.9},
'K': {'Sample1': 3.0, 'Sample2': 3.5, 'Sample3': 3.1, 'Sample4': 3.1},
'Si': {'Sample1': 4.0, 'Sample2': 4.5, 'Sample3': 1.0, 'Sample4': 3.8}}
)
def max_pct_difference(s1: tuple, s2: tuple):
max_diff = 0.0
for x, y in zip(s1[1:], s2[1:]):
# handle division by zero
if x == 0 and y == 0:
continue
diff = abs(x - y) / max(x, y)
max_diff = max(diff, max_diff)
return diff
pairs = []
for s1, s2 in combinations_with_replacement(df.itertuples(), 2):
max_diff = max_difference(s1, s2)
# append s1, s2 and then s2, s1 to make the matrix symmetric
pairs.append((s1.Index, s2.Index, max_diff))
if s1.Index != s2.Index:
pairs.append((s2.Index, s1.Index, max_diff))
# this creates the distance matrix
dist_df = pd.DataFrame(pairs, columns=['X', 'Y', 'd']).set_index(['X','Y']).unstack(level=1)
创建距离矩阵后,您可以使用 DBSCAN 查找距离阈值 epsilon 内的簇/组。对于参数,如果您想保留至少有 2 个点接近的所有组,则需要设置
eps=0.1
并将每个簇的最小点数设置为 2。对于度量,我们将使用 "precomputed"
,因为我们已经以自定义方式预先计算了距离。
from sklearn.cluster import DBSCAN
dbs = DBSCAN(eps=0.1, min_samples=2, metric='precomputed')
# get the group label for each item in the distance data frame
groups = dbs.fit_predict(dist_df)
# assign the group label to the corresponding sample
df['Group'] = groups
print(df)
# prints:
# Al Na K Si Group
# Sample1 1.00 2.0 3.0 4.0 0
# Sample2 1.50 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
此示例中只有一个组,即组
0
,它被分配给样本 1 和 4。标有组 -1 的行没有组。