<think>由于用户提到“可视化的过程不支持中文”,我们之前的代码已经通过`set_chinese_font`函数尝试设置中文字体。但是,如果用户的环境中没有我们预设的字体,或者由于其他原因导致中文显示失败,我们需要提供更健壮的解决方案。解决方案:1.确保在设置中文字体时,如果首选字体不存在,则尝试使用更通用的方法(比如使用`matplotlib`的字体管理器动态添加中文字体)。2.提供备选方案:如果系统没有中文字体,我们可以从网络下载一个开源中文字体(如“SimHei”),或者使用用户提供的字体文件。3.在设置字体失败时,使用英文标签替代中文,以避免乱码。但是,由于用户要求可视化过程支持中文,我们将重点放在如何确保中文字体可用上。改进步骤:a.检查预设字体是否存在,如果不存在则尝试使用`matplotlib`的字体管理器查找其他可用中文字体。b.如果仍然找不到,尝试使用`matplotlib`的`font_manager`添加一个默认的中文字体文件(假设我们有一个字体文件路径,或者从网络下载)。c.如果以上都失败,则使用英文标签。由于网络下载字体可能需要网络连接,并且用户环境可能受限,我们将提供一个备选方案:允许用户指定一个中文字体文件路径。修改后的`set_chinese_font`函数:我们将做以下调整:1.首先尝试使用之前的方法(查找系统已有中文字体)。2.如果失败,尝试使用`matplotlib`自带的`DejaVuSans`字体(它支持一些基本的中文,但可能不完整)或者尝试使用`SimHei.ttf`(如果存在于`matplotlib`的字体缓存中)。3.如果还不行,就提供一个下载链接,或者建议用户安装中文字体。但是,由于程序自动下载字体可能涉及网络和权限问题,这里我们只做到:尝试多种系统常见路径下的中文字体,以及使用`matplotlib`的字体管理器添加用户指定的字体文件(如果用户提供了的话)。考虑到时间,我们提供一个更健壮的版本,但不会包含自动下载(因为下载需要额外库和网络请求,可能增加复杂性)。我们将修改`set_chinese_font`函数,允许传入一个字体文件路径作为备选。如果没有传入,则尝试使用系统已知的常见路径中的字体。常见中文字体在Windows、Linux、macOS上的路径:-Windows:`C:/Windows/Fonts/simhei.ttf`(黑体)-macOS:`/System/Library/Fonts/PingFang.ttc`(苹方)-Linux:`/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf`(可能需要安装)由于跨平台,我们尝试这些路径。修改后的代码:注意:如果用户的环境是Linux,可能需要安装中文字体,比如`fonts-noto-cjk`等。但是,为了简化,我们只尝试加载已知路径的字体文件,如果存在则使用。另外,我们也可以考虑使用`matplotlib`的`font_manager`的`findfont`函数,但之前的方法已经尝试了。我们重新设计`set_chinese_font`函数:步骤:1.尝试使用之前的方法(查找已加载的字体列表中的中文字体)。2.如果找不到,尝试从常见路径加载中文字体文件,然后使用`font_manager`添加。3.如果还不行,则使用默认的`sans-serif`字体,但会警告中文可能显示不正常,并将标签改为英文(或者用户可以选择忽略中文显示)。然而,用户要求中文显示,所以我们不改为英文,而是抛出警告,并希望用户安装中文字体。但根据用户反馈,他们希望可视化过程支持中文,所以我们尽可能确保中文显示。我们尝试以下常见字体路径:-Windows:'C:/Windows/Fonts/simhei.ttf'-macOS:['/System/Library/Fonts/PingFang.ttc','/Library/Fonts/ArialUnicode.ttf']-Linux:['/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf','/usr/share/fonts/truetype/wqy/wqy-microhei.ttc']注意:由于字体文件路径在不同系统可能不同,我们尝试这些路径,如果存在则添加该字体。修改后的`set_chinese_font`函数如下:由于代码较长,我们只修改字体设置部分,并提供详细的注释。另外,我们还可以设置`matplotlib`的配置参数`'font.family'`为`sans-serif`,并设置我们添加的字体为优先。但是,由于我们无法确保所有环境都有这些字体,所以我们在尝试后如果成功,则设置该字体。如果以上方法都不行,则打印警告,并使用英文(但用户要求中文,所以我们不改变标签语言,而是希望用户注意到问题)。由于用户的问题描述是“可视化的过程不支持中文怎么办”,我们假设用户希望无论如何都要显示中文,所以我们不改变标签语言,而是尽可能设置字体。修改后的代码:注意:我们不会改变标签的中文,而是确保字体可用。我们将尝试:1.使用`font_manager`查找已注册的字体中是否有中文字体。2.如果没有,则尝试从已知路径加载字体并添加到字体缓存。3.设置`rcParams`。具体实现如下:</think># 解决可视化中文显示问题
当 Matplotlib 或 Seaborn 不支持中文时,主要原因是缺少合适的中文字体或未正确配置字体设置。以下是完整的解决方案,确保可视化中能正常显示中文:
```python
import scipy.io
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
import matplotlib as mpl
import matplotlib.font_manager as fm
import pandas as pd
import seaborn as sns
import os
from pathlib import Path
import platform
def configure_chinese_font():
"""
配置中文字体支持,解决中文显示问题
返回: 是否成功配置中文字体
"""
# 尝试查找系统中已安装的中文字体
system = platform.system()
chinese_fonts = []
# 不同系统的字体路径
if system == 'Windows':
font_paths = ['C:\\Windows\\Fonts']
preferred_fonts = ['simhei.ttf', 'msyh.ttc', 'simsun.ttc', 'kaiu.ttf']
elif system == 'Darwin': # macOS
font_paths = [
'/System/Library/Fonts',
'/Library/Fonts',
os.path.expanduser('~/Library/Fonts')
]
preferred_fonts = ['PingFang.ttc', 'Hei.ttc', 'Song.ttc', 'Kai.ttf']
else: # Linux
font_paths = [
'/usr/share/fonts',
'/usr/local/share/fonts',
os.path.expanduser('~/.fonts')
]
preferred_fonts = ['wqy-microhei.ttc', 'wqy-zenhei.ttc', 'DroidSansFallback.ttf']
# 查找所有中文字体文件
for path in font_paths:
if os.path.exists(path):
for root, _, files in os.walk(path):
for file in files:
if file.lower().endswith(('.ttf', '.ttc', '.otf')):
if any(keyword in file.lower() for keyword in ['chinese', 'cjk', 'sc', 'tc', 'jp', 'kr', 'han', 'hei', 'song', 'kai']):
chinese_fonts.append(os.path.join(root, file))
# 优先选择首选字体
selected_font = None
for font in preferred_fonts:
for chinese_font in chinese_fonts:
if font.lower() in chinese_font.lower():
selected_font = chinese_font
break
if selected_font:
break
# 如果没找到首选字体,使用第一个找到的中文字体
if not selected_font and chinese_fonts:
selected_font = chinese_fonts[0]
# 配置Matplotlib使用找到的字体
if selected_font:
try:
# 添加字体到Matplotlib字体管理器
font_prop = fm.FontProperties(fname=selected_font)
font_name = font_prop.get_name()
# 设置全局字体
plt.rcParams['font.family'] = font_name
plt.rcParams['font.sans-serif'] = [font_name]
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
print(f"成功设置中文字体: {font_name} ({selected_font})")
return True
except Exception as e:
print(f"设置字体时出错: {e}")
# 如果找不到中文字体,尝试使用Matplotlib内置字体
try:
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['font.sans-serif'] = ['DejaVu Sans'] # 支持更多字符的字体
plt.rcParams['axes.unicode_minus'] = False
print("使用默认字体 (可能不支持完整中文)")
return False
except:
print("无法设置中文字体,中文显示可能不正常")
return False
# 配置中文字体
font_configured = configure_chinese_font()
def process_alpha_eeg_data(mat_file_path):
"""
处理脑电数据,支持中文路径
参数:
mat_file_path: .mat文件路径(支持中文)
返回:
fig: 生成的图表对象
results_df: 包含所有分析结果的DataFrame
p_value: 闭眼与睁眼条件下的统计显著性p值
"""
# 1. 安全加载.mat文件数据(支持中文路径)
def safe_loadmat(file_path):
"""安全加载MAT文件,支持中文路径"""
try:
return scipy.io.loadmat(file_path)
except UnicodeDecodeError:
path_obj = Path(file_path)
return scipy.io.loadmat(str(path_obj))
except Exception as e:
raise ValueError(f"无法加载MAT文件: {str(e)}")
mat_data = safe_loadmat(mat_file_path)
# 2. 提取数据矩阵
if 'data' not in mat_data:
# 改进的数据矩阵检测
valid_keys = [k for k in mat_data.keys()
if not k.startswith('__')
and isinstance(mat_data[k], np.ndarray)
and mat_data[k].ndim == 2]
if not valid_keys:
raise ValueError("未找到有效的二维数据矩阵")
# 选择最大的数据矩阵
data_matrix = mat_data[max(valid_keys, key=lambda k: mat_data[k].size)]
print(f"使用自动检测到的数据矩阵: {max(valid_keys, key=lambda k: mat_data[k].size)}")
else:
data_matrix = mat_data['data']
# 3. 解析数据矩阵结构
timestamps = data_matrix[:, 0] # 时间戳
# 检查数据列数
num_columns = data_matrix.shape[1]
if num_columns < 19:
raise ValueError(f"数据矩阵只有{num_columns}列,需要至少19列(时间戳+16个EEG通道+2个触发器)")
eeg_data = data_matrix[:, 1:17] # 16个EEG通道
trigger_eyes_closed = data_matrix[:, 17] # 闭眼触发器
trigger_eyes_open = data_matrix[:, 18] # 睁眼触发器
# 固定采样率512Hz
sampling_rate = 512.0
# 4. 预处理 - 带通滤波和陷波滤波
def preprocess(data):
"""应用带通滤波和陷波滤波预处理EEG数据"""
# 带通滤波提取Alpha波 (8-12Hz)
nyquist = 0.5 * sampling_rate
low = 8 / nyquist
high = 12 / nyquist
b, a = signal.butter(4, [low, high], btype='bandpass')
alpha_data = signal.filtfilt(b, a, data)
# 陷波滤波去除50/60Hz工频干扰
notch_freq = 50.0 # 根据实际工频干扰调整
notch_width = 2.0
freq = notch_freq / nyquist
q = freq / (notch_width / nyquist)
b, a = signal.iirnotch(freq, q)
return signal.filtfilt(b, a, alpha_data)
# 应用预处理到所有通道
eeg_data_filtered = np.apply_along_axis(preprocess, 0, eeg_data)
# 5. 计算注意力指数
def calculate_attention(alpha_data):
"""计算基于Alpha波的注意力指数"""
# 计算Alpha波能量 (RMS)
alpha_energy = np.sqrt(np.mean(alpha_data**2))
# 计算注意力指数 (与Alpha能量负相关)
attention_index = 1 / (1 + alpha_energy)
# 归一化到0-100范围
attention_index = np.clip(attention_index * 100, 0, 100)
return attention_index
# 6. 识别所有会话块
def find_sessions(trigger):
"""识别所有会话的开始和结束"""
# 找到所有上升沿(会话开始)
trigger_diff = np.diff(trigger)
session_starts = np.where(trigger_diff == 1)[0] + 1
# 找到所有下降沿(会话结束)
session_ends = np.where(trigger_diff == -1)[0] + 1
# 确保每个开始都有对应的结束
sessions = []
for start in session_starts:
# 找到下一个结束点
ends_after_start = session_ends[session_ends > start]
if len(ends_after_start) > 0:
end = ends_after_start[0]
sessions.append((start, end))
return sessions
# 获取所有会话块(闭眼和睁眼)
closed_eye_sessions = find_sessions(trigger_eyes_closed)
open_eye_sessions = find_sessions(trigger_eyes_open)
# 7. 处理每个会话块
session_results = []
# 处理闭眼会话块
for session_idx, (start_idx, end_idx) in enumerate(closed_eye_sessions):
# 提取会话数据
session_eeg = eeg_data_filtered[start_idx:end_idx, :]
session_duration = (end_idx - start_idx) / sampling_rate
# 计算平均注意力指数(所有通道)
channel_attention = []
for ch in range(session_eeg.shape[1]):
attention = calculate_attention(session_eeg[:, ch])
channel_attention.append(attention)
session_avg_attention = np.mean(channel_attention)
# 存储会话结果
session_results.append({
'session_id': session_idx + 1,
'condition': 'eyes_closed' if not font_configured else '闭眼',
'start_time': timestamps[start_idx],
'duration': session_duration,
'avg_attention': session_avg_attention,
'channel_attention': channel_attention
})
# 处理睁眼会话块
for session_idx, (start_idx, end_idx) in enumerate(open_eye_sessions):
# 提取会话数据
session_eeg = eeg_data_filtered[start_idx:end_idx, :]
session_duration = (end_idx - start_idx) / sampling_rate
# 计算平均注意力指数(所有通道)
channel_attention = []
for ch in range(session_eeg.shape[1]):
attention = calculate_attention(session_eeg[:, ch])
channel_attention.append(attention)
session_avg_attention = np.mean(channel_attention)
# 存储会话结果
session_results.append({
'session_id': session_idx + 1 + len(closed_eye_sessions),
'condition': 'eyes_open' if not font_configured else '睁眼',
'start_time': timestamps[start_idx],
'duration': session_duration,
'avg_attention': session_avg_attention,
'channel_attention': channel_attention
})
# 创建结果DataFrame
results_df = pd.DataFrame(session_results)
# 8. 简化可视化结果
fig = plt.figure(figsize=(16, 10), constrained_layout=True)
gs = fig.add_gridspec(2, 1) # 2行1列布局
# 根据字体配置选择标签
closed_label = 'eyes_closed' if not font_configured else '闭眼'
open_label = 'eyes_open' if not font_configured else '睁眼'
condition_label = 'condition' if not font_configured else '条件'
# 图1: 随时间变化的注意力指数
ax1 = fig.add_subplot(gs[0, 0])
# 为不同条件设置不同颜色
colors = {closed_label: 'blue', open_label: 'orange'}
for condition in [closed_label, open_label]:
condition_data = results_df[results_df['condition'] == condition]
ax1.plot(condition_data['session_id'], condition_data['avg_attention'],
'o-', label=condition, markersize=8, color=colors[condition])
ax1.set_title('Attention Index Over Time' if not font_configured else '不同条件下注意力指数随时间变化',
fontsize=16)
ax1.set_xlabel('Session ID' if not font_configured else '会话块编号', fontsize=14)
ax1.set_ylabel('Average Attention Index (%)' if not font_configured else '平均注意力指数 (%)', fontsize=14)
ax1.legend(fontsize=12)
ax1.grid(True, linestyle='--', alpha=0.3)
# 添加每个数据点的数值标签
for _, row in results_df.iterrows():
ax1.text(row['session_id'], row['avg_attention'] + 2,
f"{row['avg_attention']:.1f}",
ha='center', va='bottom', fontsize=9)
# 图2: 闭眼与睁眼条件下的注意力比较
ax2 = fig.add_subplot(gs[1, 0])
# 箱线图展示条件间差异
sns.boxplot(x='condition', y='avg_attention', data=results_df, ax=ax2,
palette='Set2', hue='condition', legend=False)
# 添加散点图显示个体数据点
sns.stripplot(x='condition', y='avg_attention', data=results_df, ax=ax2,
color='black', alpha=0.7, size=7, jitter=True)
ax2.set_title('Attention Comparison Between Conditions' if not font_configured else '闭眼与睁眼条件下注意力比较',
fontsize=16)
ax2.set_xlabel(condition_label, fontsize=14)
ax2.set_ylabel('Average Attention Index (%)' if not font_configured else '平均注意力指数 (%)', fontsize=14)
# 添加统计显著性标记(使用独立样本t检验)
from scipy import stats
closed_data = results_df[results_df['condition'] == closed_label]['avg_attention']
open_data = results_df[results_df['condition'] == open_label]['avg_attention']
t_stat, p_value = stats.ttest_ind(closed_data, open_data)
# 添加显著性标记
y_max = max(results_df['avg_attention']) + 5
ax2.plot([0, 0, 1, 1], [y_max, y_max+2, y_max+2, y_max], lw=1.5, c='black')
ax2.text(0.5, y_max+3, f"p = {p_value:.4f}", ha='center', va='bottom', fontsize=12)
plt.tight_layout()
# 9. 保存结果
base_name = os.path.splitext(os.path.basename(mat_file_path))[0]
# 保存图像
fig.savefig(f"{base_name}_analysis.png", dpi=300, bbox_inches='tight')
# 保存结果到CSV
results_df.to_csv(f"{base_name}_results.csv", index=False, encoding='utf-8-sig')
return fig, results_df, p_value
# 使用示例
if __name__ == "__main__":
# 中文路径示例
mat_file = "F:/Grade2/attention/2348892/subject_02.mat"
try:
# 处理数据并生成可视化
fig, results_df, p_value = process_alpha_eeg_data(mat_file)
# 显示图像
plt.show()
# 打印会话结果和统计显著性
print("EEG Analysis Results:" if not font_configured else "EEG分析结果:")
print(results_df[['session_id', 'condition', 'duration', 'avg_attention']])
print(f"\nStatistical Significance: p = {p_value:.4f}" if not font_configured else f"\n统计显著性结果: p = {p_value:.4f}")
if p_value < 0.05:
print("Significant difference between conditions (p < 0.05)" if not font_configured else "闭眼与睁眼条件下的注意力指数存在显著差异 (p < 0.05)")
else:
print("No significant difference between conditions" if not font_configured else "闭眼与睁眼条件下的注意力指数无显著差异")
except Exception as e:
print(f"Error during processing: {str(e)}" if not font_configured else f"处理过程中发生错误: {str(e)}")
import traceback
traceback.print_exc()
```
## 中文显示问题解决方案详解
### 1. 智能字体配置系统
```python
def configure_chinese_font():
"""
配置中文字体支持,解决中文显示问题
返回: 是否成功配置中文字体
"""
# 根据操作系统确定字体路径
system = platform.system()
if system == 'Windows':
font_paths = ['C:\\Windows\\Fonts']
preferred_fonts = ['simhei.ttf', 'msyh.ttc', 'simsun.ttc', 'kaiu.ttf']
elif system == 'Darwin': # macOS
font_paths = ['/System/Library/Fonts', ...]
preferred_fonts = ['PingFang.ttc', ...]
else: # Linux
font_paths = ['/usr/share/fonts', ...]
preferred_fonts = ['wqy-microhei.ttc', ...]
# 遍历字体目录查找中文字体
for path in font_paths:
if os.path.exists(path):
for root, _, files in os.walk(path):
for file in files:
# 识别中文字体文件
if file.lower().endswith(('.ttf', '.ttc', '.otf')):
if any(keyword in file.lower() for keyword in
['chinese', 'cjk', 'sc', 'tc', ...]):
chinese_fonts.append(os.path.join(root, file))
# 选择最佳字体
selected_font = None
for font in preferred_fonts:
for chinese_font in chinese_fonts:
if font.lower() in chinese_font.lower():
selected_font = chinese_font
break
# 配置Matplotlib
if selected_font:
try:
font_prop = fm.FontProperties(fname=selected_font)
font_name = font_prop.get_name()
plt.rcParams['font.family'] = font_name
plt.rcParams['font.sans-serif'] = [font_name]
plt.rcParams['axes.unicode_minus'] = False
return True
...
```
**功能说明**:
- **跨平台支持**:针对Windows、macOS和Linux系统使用不同的字体路径
- **智能字体搜索**:自动扫描系统字体目录,识别中文字体文件
- **首选字体优先**:优先选择常见中文字体(如Windows的SimHei、macOS的PingFang)
- **优雅回退**:如果找不到中文字体,使用支持更多字符的DejaVu Sans字体
### 2. 动态标签系统
```python
# 根据字体配置选择标签
closed_label = 'eyes_closed' if not font_configured else '闭眼'
open_label = 'eyes_open' if not font_configured else '睁眼'
condition_label = 'condition' if not font_configured else '条件'
# 在图表中使用动态标签
ax1.set_title('Attention Index Over Time' if not font_configured else '不同条件下注意力指数随时间变化')
ax1.set_xlabel('Session ID' if not font_configured else '会话块编号')
```
**功能说明**:
- **双语支持**:根据字体配置状态自动切换中英文标签
- **一致性**:所有文本元素(标题、轴标签、图例等)都使用相同逻辑
- **清晰可读**:即使没有中文字体,也能提供有意义的英文标签
### 3. 错误处理和日志
```python
# 详细的错误处理
try:
font_prop = fm.FontProperties(fname=selected_font)
...
except Exception as e:
print(f"设置字体时出错: {e}")
# 回退到默认字体方案
```
**功能说明**:
- **全面异常捕获**:捕获字体加载和设置过程中的所有异常
- **详细日志**:提供清晰的错误信息帮助诊断问题
- **友好提示**:在控制台输出字体配置状态和问题说明
## 备选方案:安装中文字体
如果上述自动配置无法解决问题,可以手动安装中文字体:
### 方法1:使用思源黑体(开源免费)
1. 下载思源黑体:
- 官方下载:https://github.com/adobe-fonts/source-han-sans
- 直接下载:https://github.com/adobe-fonts/source-han-sans/raw/release/OTF/SourceHanSansSC.zip
2. 安装字体:
- **Windows**:解压后右键选择"为所有用户安装"
- **macOS**:双击字体文件 > 点击"安装字体"
- **Linux**:复制到`/usr/share/fonts/`或`~/.fonts/`
3. 在代码中指定字体:
```python
# 在configure_chinese_font函数中添加
preferred_fonts.insert(0, 'SourceHanSansSC-Regular.otf')
```
### 方法2:使用Matplotlib的字体缓存
```python
# 清除字体缓存并重新生成
import matplotlib as mpl
mpl.font_manager._rebuild()
# 然后重新运行配置函数
configure_chinese_font()
```
## 常见问题解决
1. **字体安装后仍无法显示中文**:
```python
# 强制刷新字体缓存
from matplotlib.font_manager import fontManager
fontManager.addfont("/path/to/your/chinese_font.ttf")
mpl.rcParams['font.family'] = 'Your Chinese Font Name'
```
2. **部分字符显示为方框**:
- 使用支持更广字符集的字体(如思源黑体)
- 确保字体文件完整无损坏
3. **Docker环境中的字体问题**:
```dockerfile
# Dockerfile中添加
RUN apt-get update && apt-get install -y fonts-wqy-microhei
```
##