如何用 Python 聚类红外光谱数据

How to Cluster Infrared Spectroscopy Data with Python

我一直在研究使用 sklearn 聚类方法对红外光谱数据进行聚类。我在让聚类处理数据时遇到问题,因为我是新手,我不知道是我的编码方式有误还是我的方法有误。

我的数据,采用 Pandas DataFrame 格式,如下所示:

Index     Wavenumbers (cm-1)     %Transmission_i   ...
0         650                    100               ... 
.          .                      .                ...
.          .                      .                ...
.          .                      .                ...
n         4000                   95                ...

其中,所有光谱的 x 轴是 Wavenumbers (cm-1) 列,后续列 (%Transmission_i) 是实际数据。我想对这些列进行聚类(根据哪些光谱彼此最相似),因此我正在尝试此代码:

X        = np.array([list(df[x].values) for x in df.set_index(x)])
clusters = DBSCAN().fit(X)

其中 df 是我的 DataFrame,np 是 numpy(希望显而易见)。问题是当我打印出集群标签时,它只吐出 -1 什么也没有,这意味着我所有的数据都是噪音。事实并非如此,当我绘制数据时,我可以清楚地看到一些光谱看起来非常相似(它们应该如此)。

如何正确聚类相似的光谱?

编辑: 这是一个最小的工作示例。

import numpy as np
import pandas as pd
import sklearn as sk
import matplotlib.pyplot as plt
from sklearn.cluster import DBSCAN

x = 'x-vals'

def cluster_data(df):

    avg_list = []
    dif_list = []
    for col in df:
        if x == col:
            continue
        avg_list.append(np.mean(df[col].values))
        dif_list.append(np.mean(np.diff(df[col].values)))

    a = sk.preprocessing.normalize([avg_list], norm='max')[0]
    b = sk.preprocessing.normalize([dif_list], norm='max')[0]

    X = []
    for i,j in zip(a,b):
        X.append([i,j])

    X = np.array(X)
    clusters = DBSCAN(eps=0.2).fit(X)

    return clusters.labels_

def plot_clusters(df, clusters):
    colors = ['red', 'green', 'blue', 'black', 'pink']
    i      = 0
    for col in df:
        if col == x:
            continue
        color = colors[clusters[i]]
        plt.plot(df[x], df[col], color=color)
        i +=1
    plt.show()


x1  = np.linspace(-np.pi, np.pi, 201)
y1  = np.sin(x1) + 1
y2  = np.cos(x1) + 1
y3  = np.zeros_like(x1) + 2
y4  = np.zeros_like(x1) + 1.9
y5  = np.zeros_like(x1) + 1.8
y6  = np.zeros_like(x1) + 1.7
y7  = np.zeros_like(x1) + 1
y8  = np.zeros_like(x1) + 0.9
y9  = np.zeros_like(x1) + 0.8
y10 = np.zeros_like(x1) + 0.7

df  = pd.DataFrame({'x-vals':x1, 'y1':y1, 'y2':y2, 'y3':y3, 'y4':y4,
                    'y5':y5, 'y6':y6, 'y7':y7, 'y8':y8, 'y9':y9,
                    'y10':y10})

clusters = cluster_data(df)

plot_clusters(df, clusters)

这会产生以下图,其中红色是簇,粉红色是噪声。

首先,转置您的数据框,以便您按照标准将数据点作为行。它应该是这样的:

Index    650    660    ...    4000
0        100    98     ...    95
1        .      .      ...    .
.        .      .      ...    .
n        .      .      ...    .

然后你得到你的 X 这样的聚类:

X = df.values

接下来,你集群:

from sklearn.cluster import DBSCAN
cluster = DBSCAN().fit(X)
print(cluster.labels_)

作为光谱数据的推荐,kmeans(缺点:需要事先设置簇数)和self-organizing maps(缺点:软簇而不是硬簇)效果很好。例如,您找到一个示例 here 用于对高光谱数据进行聚类。

我能够找到一个有效的方法,但我不完全相信这是聚类 IR 光谱的最佳方法。

首先,我运行 遍历了所有光谱,并编制了每个光谱的 meanmean of the first derivative 列表。 mean 应该代表光谱的垂直位置,而 mean of the first derivative 应该代表光谱的形状。

avg_list = []
dif_list = []
for col in df:
    if x == col:
       continue
    avg_list.append(np.mean(df[col].values))
    dif_list.append(np.mean(np.dif(df[col].values)))

然后我标准化每个列表,这样我就可以根据百分比变化选择一个 eps 值。

a = sk.preprocessing.normalize([avg_list], norm='max')[0]
b = sk.preprocessing.normalize([diff_list], norm='max')[0]

之后,我在二维模式下为 运行nning DBSCAN 创建了一个二维数组。

X = []
for i,j in zip(a,b):
    X.append([i,j])

然后我 运行 DBSCAN 聚类方法,eps 参数具有任意百分比差异值。

X        = np.array(X)
clusters = DBSCAN(eps=0.2).fit(X)

然后 clusters.labels_ returns 一个数组,其长度为我的 DataFrame 中光谱的数量。它工作得相当好,但它是相当排他的,集群可能会更好。一些更精细的调整会有所帮助。