如何使用 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 光谱进行聚类的最佳方法。
首先,我遍历所有光谱并编译每个光谱的mean
和mean 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 对红外光谱数据进行聚类的主要内容,如果未能解决你的问题,请参考以下文章