你遇到的错误如下:
```
TypeError: Could not convert [...] to numeric
```
这个错误表明你在对某些非数值列计算 `mean()`,也就是说,`weathered_chem` 中某些列不是数值类型,而是字符串(例如编号、描述等),导致无法计算平均值。
---
## 🔍 错误原因分析
你在调用 `weathered_chem.mean()` 时,`weathered_chem` 中可能仍然保留了非数值列(例如样品编号、描述性字段),虽然你尝试提取了化学成分列,但可能因为列名不匹配导致这些非数值列没有被过滤掉。
---
## ✅ 解决方案:更精确地匹配化学成分列名
你当前的代码使用了模糊匹配,例如:
```python
any(x in col for x in ['SiO2', 'Na2O', 'K2O', ...])
```
但如果你的列名是 `SiO2(%)`、`K2O (%)`、或者 `氧化钾(K2O)`,这种模糊匹配可能会失败,导致某些非数值列被保留。
---
## ✅ 修改后的代码(重点修改 `preprocess_data` 函数)
```python
def preprocess_data(df):
# 删除全为空的行
df = df.dropna(how='all')
# 填充缺失值为0
df = df.fillna(0)
# 精确提取化学成分列(匹配括号内的化学式)
chem_keywords = ['SiO2', 'Na2O', 'K2O', 'CaO', 'MgO', 'Al2O3', 'Fe2O3', 'CuO',
'PbO', 'BaO', 'P2O5', 'SrO', 'SnO2', 'SO2']
chem_cols = [col for col in df.columns if any(kw in col for kw in chem_keywords)]
# 确保只保留数值列
df = df[chem_cols]
for col in df.columns:
try:
df[col] = pd.to_numeric(df[col], errors='coerce')
except:
pass
df = df.dropna(axis=1, how='all') # 删除全为NaN的列
return df
```
---
## ✅ 完整修正后的代码如下:
```python
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from pathlib import Path
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 读取数据
excel_path1 = Path(r"C:\Users\x1861\Desktop\数学建模\高钾玻璃风化前(1).xlsx")
excel_path2 = Path(r"C:\Users\x1861\Desktop\数学建模\铅钡玻璃风化前(1).xlsx")
df_unweathered = pd.read_excel(excel_path1, sheet_name='Sheet1') # 风化前数据
df_weathered = pd.read_excel(excel_path2, sheet_name='Sheet1') # 风化后数据
# 数据预处理函数
def preprocess_data(df):
df = df.dropna(how='all') # 删除全为空的行
df = df.fillna(0) # 填充缺失值为0
# 提取化学成分列
chem_keywords = ['SiO2', 'Na2O', 'K2O', 'CaO', 'MgO', 'Al2O3', 'Fe2O3', 'CuO',
'PbO', 'BaO', 'P2O5', 'SrO', 'SnO2', 'SO2']
chem_cols = [col for col in df.columns if any(kw in col for kw in chem_keywords)]
df = df[chem_cols]
# 转换为数值型
for col in df.columns:
df[col] = pd.to_numeric(df[col], errors='coerce')
df = df.dropna(axis=1, how='all') # 删除全为NaN的列
return df
# 处理风化前后数据
unweathered_chem = preprocess_data(df_unweathered)
weathered_chem = preprocess_data(df_weathered)
# 计算均值
mean_unweathered = unweathered_chem.mean().reset_index()
mean_unweathered.columns = ['化学成分', '含量(%)']
mean_unweathered['风化状态'] = '无风化'
mean_weathered = weathered_chem.mean().reset_index()
mean_weathered.columns = ['化学成分', '含量(%)']
mean_weathered['风化状态'] = '风化'
# 合并数据
mean_data = pd.concat([mean_unweathered, mean_weathered])
# 化学成分排序(按化学重要性)
chem_order = ['二氧化硅(SiO2)', '氧化钾(K2O)', '氧化钙(CaO)', '氧化铝(Al2O3)',
'氧化铁(Fe2O3)', '氧化铜(CuO)', '氧化铅(PbO)', '氧化钡(BaO)',
'五氧化二磷(P2O5)', '氧化钠(Na2O)', '氧化镁(MgO)',
'氧化锶(SrO)', '氧化锡(SnO2)', '二氧化硫(SO2)']
mean_data['化学成分'] = pd.Categorical(mean_data['化学成分'], categories=chem_order, ordered=True)
mean_data = mean_data.sort_values('化学成分')
# 创建折线图
plt.figure(figsize=(16, 8))
sns.set_style("whitegrid")
sns.lineplot(
data=mean_data,
x='化学成分',
y='含量(%)',
hue='风化状态',
style='风化状态',
markers={'无风化': 'o', '风化': 's'},
palette={'无风化': '#3498DB', '风化': '#E74C3C'},
linewidth=2.5,
markersize=10,
dashes=False
)
# 添加数据标签
for line in plt.gca().lines:
for x, y in zip(line.get_xdata(), line.get_ydata()):
if y > 0.1: # 只标注含量大于0.1%的成分
plt.annotate(f'{y:.1f}%',
(x, y),
textcoords="offset points",
xytext=(0, 8),
ha='center',
fontsize=9,
bbox=dict(boxstyle='round,pad=0.2',
fc='white',
alpha=0.7))
# 图表美化
plt.title('高钾玻璃风化前后各化学成分含量均值对比', fontsize=16, pad=20)
plt.xlabel('化学成分', fontsize=12)
plt.ylabel('含量(%)', fontsize=12)
plt.xticks(rotation=45, ha='right')
plt.legend(title='风化状态', bbox_to_anchor=(1, 1))
plt.tight_layout()
# 显示图表
plt.show()
# 输出统计结果
print("高钾玻璃化学成分含量均值统计:")
stat_table = mean_data.pivot_table(index='化学成分', columns='风化状态', values='含量(%)')
stat_table['变化率(%)'] = (stat_table['风化'] - stat_table['无风化']) / stat_table['无风化'] * 100
print(stat_table.round(2).to_markdown(index=True, tablefmt='grid'))
```
---
## ✅ 总结问题原因
- **列名不匹配**:原始代码中提取的列包含非数值列。
- **未做类型转换**:即使列名正确,也可能包含字符串类型,导致 `mean()` 报错。
---