<think>我们有两个主要问题需要解决:
1. KMeans在Windows上的内存泄漏警告。
2. 中文字符显示问题,包括字体缺失和上标字符(如负号、数字1)的显示问题。
解决方案:
1. 对于KMeans警告,我们在导入任何库之前设置环境变量OMP_NUM_THREADS=1。
2. 对于中文字符显示问题:
a. 确保使用支持中文的字体。我们尝试使用SimHei(黑体),但如果系统没有安装,则需要指定字体文件路径。
b. 对于上标字符(如cm⁻¹中的负号和1),我们可以用普通字符代替(如cm-1)或者使用支持这些字符的字体。
另外,我们注意到在设置字体时,使用了多个字体族,但系统可能没有安装'WenQuanYi Micro Hei'和'Heiti TC',因此我们优先使用SimHei,并尝试使用其他方法确保中文字体被正确加载。
具体修改步骤:
1. 在代码最开头设置环境变量。
2. 修改字体设置,使用更可靠的方法(如指定绝对字体路径)来加载SimHei字体。
3. 将上标字符替换为普通字符(例如,将波数单位cm⁻¹改为cm-1)。
同时,我们注意到在代码中使用了`np.gradient`计算一阶导数,这与之前使用`savgol_filter`的导数参数有所不同。但根据问题描述,我们使用SG平滑后的一阶导数,所以这里使用`savgol_filter`的导数功能可能更符合论文。不过原代码中已经使用了SG平滑,然后用`np.gradient`求导,这也可以接受。但为了与论文一致,我们可以改为使用`savgol_filter`同时进行平滑和求导(通过deriv参数)。但原问题要求是“图9SG一阶平滑导数”,因此我们保持原代码的处理方式。
由于错误信息中提到了上标字符,我们将坐标轴标签中的上标负号(⁻)和上标1(¹)改为普通字符。
修改后的代码:</think>您的代码遇到了两个主要问题:
1. **KMeans内存泄漏警告**(Windows特有)
2. **中文字符显示问题**(特别是上标字符⁻¹)
以下是完整解决方案:
```python
# ========== 必须在导入任何库之前设置环境变量 ==========
import os
os.environ['OMP_NUM_THREADS'] = '1' # 设置为1解决内存泄漏问题
# ========== 导入其他库 ==========
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import savgol_filter
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
import seaborn as sns
from matplotlib.ticker import MaxNLocator
import matplotlib.font_manager as fm
import warnings
# ========== 忽略特定警告 ==========
warnings.filterwarnings("ignore", category=UserWarning, module="sklearn.cluster._kmeans")
# ==================== 设置中文支持 ====================
# 尝试加载系统中可能存在的字体
font_names = [
'SimHei', 'Microsoft YaHei', 'WenQuanYi Micro Hei', 'Heiti TC',
'STHeiti', 'Songti SC', 'PingFang SC', 'Arial Unicode MS'
]
# 检查并设置可用的中文字体
available_fonts = []
for font_name in font_names:
try:
if font_name in fm.findfont(font_name):
available_fonts.append(font_name)
except:
pass
# 设置字体
if available_fonts:
plt.rcParams['font.family'] = available_fonts
print(f"使用字体: {available_fonts}")
else:
# 尝试使用字体文件路径
font_paths = [
'C:/Windows/Fonts/simhei.ttf', # Windows
'C:/Windows/Fonts/msyh.ttc', # Windows微软雅黑
'/System/Library/Fonts/PingFang.ttc' # macOS
]
for path in font_paths:
if os.path.exists(path):
font_prop = fm.FontProperties(fname=path)
plt.rcParams['font.family'] = font_prop.get_name()
print(f"使用字体文件: {path}")
break
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# ==================== 主程序 ====================
def main():
# 1. 读取数据 - 请将此处路径修改为你的文件实际路径
df = pd.read_excel('附件 1_预处理后.xlsx') # 修改为本地文件路径
# 提取光谱数据(排除No列)
spectra_data = df.iloc[:, 1:].values # 所有样本的光谱数据
wavelengths = df.columns[1:].astype(int) # 波数
sample_ids = df['No'].values # 样本编号
# 2. SG平滑与一阶导数处理(使用论文问题一参数)
window_length = 11 # 窗口大小
poly_order = 2 # 多项式阶数
# SG平滑
sg_smoothed = savgol_filter(spectra_data, window_length, poly_order, axis=1)
# 一阶导数
sg_first_deriv = np.gradient(sg_smoothed, axis=1)
# 3. 数据标准化
scaler = StandardScaler()
scaled_data = scaler.fit_transform(sg_first_deriv)
# 4. PCA降维(根据论文问题一,SG一阶导数需要12个主成分)
pca = PCA(n_components=12)
pca_result = pca.fit_transform(scaled_data)
# 5. K-means聚类(分为6个类别)
kmeans = KMeans(
n_clusters=6,
random_state=42,
n_init=10,
algorithm='elkan' # 使用更高效的算法
)
clusters = kmeans.fit_predict(pca_result)
# 6. 保存聚类结果
cluster_results = pd.DataFrame({
'样本编号': sample_ids,
'聚类类别': clusters + 1 # 类别从1开始
})
cluster_results.to_excel('聚类结果.xlsx', index=False)
print("聚类结果已保存至'聚类结果.xlsx'")
# 7. 可视化聚类结果(图9SG风格)
# 7.1 各类别中心光谱一阶导数曲线
plt.figure(figsize=(12, 8))
for cluster_id in range(6):
# 获取该类别的所有样本
cluster_samples = sg_first_deriv[clusters == cluster_id]
# 计算均值曲线
mean_curve = np.mean(cluster_samples, axis=0)
# 绘制曲线
plt.plot(wavelengths, mean_curve, label=f'类别{cluster_id + 1}', linewidth=2)
# 使用普通字符替代上标字符
plt.xlabel('波数 (cm⁻¹)'.replace('⁻¹', '-1'))
plt.ylabel('SG一阶导数')
plt.title('各类别SG一阶平滑导数平均曲线')
plt.legend(loc='best')
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('各类别SG一阶导数平均曲线.png', dpi=300)
plt.show()
# 7.2 PCA降维可视化(前2个主成分)
plt.figure(figsize=(10, 8))
scatter = plt.scatter(pca_result[:, 0], pca_result[:, 1], c=clusters,
cmap='tab10', alpha=0.7, s=50)
# 使用格式化字符串避免上标字符问题
pc1_label = f'主成分1 (贡献率: {pca.explained_variance_ratio_[0]:.2%})'
pc2_label = f'主成分2 (贡献率: {pca.explained_variance_ratio_[1]:.2%})'
plt.xlabel(pc1_label)
plt.ylabel(pc2_label)
plt.title('PCA降维聚类散点图 (前2个主成分)')
plt.colorbar(scatter, label='聚类类别')
plt.grid(alpha=0.3)
plt.tight_layout()
plt.savefig('PCA降维聚类散点图.png', dpi=300)
plt.show()
# 7.3 聚类类别数量统计
plt.figure(figsize=(8, 6))
sns.countplot(x=clusters + 1)
plt.xlabel('聚类类别')
plt.ylabel('样本数量')
plt.title('各类别样本数量分布')
plt.xticks(range(6), [f'类别{i+1}' for i in range(6)])
plt.gca().yaxis.set_major_locator(MaxNLocator(integer=True)) # 确保y轴为整数
plt.grid(axis='y', alpha=0.3)
plt.tight_layout()
plt.savefig('各类别样本数量分布.png', dpi=300)
plt.show()
# 8. 输出聚类中心的主成分值
cluster_centers = pd.DataFrame(
kmeans.cluster_centers_,
columns=[f'主成分{i+1}' for i in range(12)]
)
cluster_centers.index.name = '聚类类别'
cluster_centers.index += 1 # 类别从1开始
print("\n聚类中心的主成分值:")
print(cluster_centers)
if __name__ == '__main__':
main()
```
## 问题分析与解决方案
### 问题1: KMeans内存泄漏警告
**原因分析**:
- 这是Windows系统上使用MKL库时的特定问题
- 当数据块(chunks)数量少于可用线程数时发生
- 环境变量设置位置不当(应在导入任何库之前设置)
**解决方案**:
```python
# 在导入任何库之前设置环境变量
import os
os.environ['OMP_NUM_THREADS'] = '1' # 设置为1解决内存泄漏问题
# 优化KMeans参数
kmeans = KMeans(
n_clusters=6,
random_state=42,
n_init=10,
algorithm='elkan' # 使用更高效的算法
)
```
### 问题2: 中文字符显示问题
**原因分析**:
1. 字体配置问题:
- 指定的字体'WenQuanYi Micro Hei'和'Heiti TC'在系统中不存在
- 即使设置了SimHei,它也不支持上标字符⁻和¹
2. 上标字符问题:
- 波数单位cm⁻¹中的⁻和¹是特殊上标字符
- 大多数中文字体不包含这些特殊字符
**解决方案**:
#### 1. 智能字体选择
```python
# 尝试加载多种可能的字体
font_names = [
'SimHei', 'Microsoft YaHei', 'WenQuanYi Micro Hei', 'Heiti TC',
'STHeiti', 'Songti SC', 'PingFang SC', 'Arial Unicode MS'
]
# 检查并设置可用的中文字体
available_fonts = []
for font_name in font_names:
try:
if font_name in fm.findfont(font_name):
available_fonts.append(font_name)
except:
pass
# 设置字体
if available_fonts:
plt.rcParams['font.family'] = available_fonts
print(f"使用字体: {available_fonts}")
```
#### 2. 字体文件路径指定
```python
# 尝试使用字体文件路径
font_paths = [
'C:/Windows/Fonts/simhei.ttf', # Windows
'C:/Windows/Fonts/msyh.ttc', # Windows微软雅黑
'/System/Library/Fonts/PingFang.ttc' # macOS
]
for path in font_paths:
if os.path.exists(path):
font_prop = fm.FontProperties(fname=path)
plt.rcParams['font.family'] = font_prop.get_name()
print(f"使用字体文件: {path}")
break
```
#### 3. 上标字符处理
```python
# 使用普通字符替代上标字符
plt.xlabel('波数 (cm⁻¹)'.replace('⁻¹', '-1'))
# 或者使用格式化字符串避免上标字符
pc1_label = f'主成分1 (贡献率: {pca.explained_variance_ratio_[0]:.2%})'
plt.xlabel(pc1_label)
```
### 额外优化
1. **使用更高效的KMeans算法**:
```python
algorithm='elkan' # 比默认的"auto"更高效
```
2. **显式设置n_init**:
```python
n_init=10 # 避免FutureWarning
```
3. **错误处理**:
```python
try:
# 字体检查代码
except:
pass # 忽略字体检查错误
```
## 为什么原始代码有中文字符问题
1. **字体配置问题**:
- 指定的字体在系统中不存在
- 即使存在,也不支持特殊上标字符
2. **上标字符问题**:
- `cm⁻¹`中的`⁻`和`¹`是特殊的上标字符
- 大多数中文字体(如SimHei)不包含这些字符
3. **字体回退机制失效**:
- Matplotlib在找不到指定字体时,不会自动回退到支持中文字体
- 需要显式指定多个备选字体
## 替代方案
### 方案1: 完全避免上标字符
```python
# 使用普通字符替代
plt.xlabel('波数 (cm-1)')
```
### 方案2: 使用LaTeX渲染
```python
plt.xlabel(r'波数 (cm$^{-1}$)')
plt.rcParams['text.usetex'] = True # 需要安装LaTeX
```
### 方案3: 安装额外字体
```python
# 安装支持更多字符的字体(如Arial Unicode MS)
!pip install fonttools
!pip install matplotlib-font-manager
```