如何使用 Python 对红外光谱数据进行聚类

Posted

技术标签:

【中文标题】如何使用 Python 对红外光谱数据进行聚类【英文标题】:How to Cluster Infrared Spectroscopy Data with Python 【发布时间】:2021-02-18 22:00:53 【问题描述】:

我一直在使用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)

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

【问题讨论】:

请澄清:所有的列是什么?数据点是一行还是一列?许多Transmission_i 列? 您要么应该使用业内公认的方法[用于红外扫描],要么尝试不同的方法,看看哪种方法适合您:DBSCAN、t-SNE、Kmeans、层次聚类。不同的距离度量也可能会有所帮助。 嘿@felice,所有的列都类似于我放_i 表示它是传输数据的许多列之一。数据是由 Wavenumber 列(x 轴)和传输列(y 轴)表示的一条线,其中每一行是一个点,但该列是我想要聚类的数据。这有帮助,还是有更多的困惑? 嘿@SergeyBushmanov,我会尝试不同的方法,但我很确定我的问题是代码无法正常工作。许多传输列数组非常相似(数组中每个项目的数字略有不同),但他们仍然认为它是噪声而不是集群。 您能否为我们提供一个可重现的最小示例,例如代码中有两个数据点的数据框? 【参考方案1】:

我能够得到一种有效的方法,但我并不完全相信这是对 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]

之后,我制作了一个二维数组,用于在 2D 模式下运行 DBSCAN。

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

然后我使用eps 参数的任意百分比差异值运行 DBSCAN 聚类方法。

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

然后clusters.labels_ 返回一个数组,其中包含我的 DataFrame 中光谱数的长度。它工作得相当好,但它相当独特,集群可能会更好。一些更精细的调整会有所帮助。

【讨论】:

【参考方案2】:

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

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(缺点:需要预先设置簇的数量)和自组织图(缺点:软簇而不是硬簇)效果很好。例如,您可以找到一个示例 here,用于对高光谱数据进行聚类。

【讨论】:

这不起作用,它给了我一个标签数组,它是每个光谱点数的长度。我需要标签数组是光谱数(列)的长度。我能够找到一个可行但不是理想的聚类技术的解决方案,我会将其发布为答案,也许您可​​以帮助改进它。 然后你需要转置你的输入向量,例如与df.values.T。这行得通吗?

以上是关于如何使用 Python 对红外光谱数据进行聚类的主要内容,如果未能解决你的问题,请参考以下文章

稀疏数据集上的光谱聚类

Python中的光谱聚类和多维缩放

使用光谱聚类对看不见的点进行聚类

什么是一阶导数光谱法,他的基本原理和操作是如何进行的?

光谱异常样本检测分析

使用 python 和 DBSCAN 对高维数据进行聚类