Python数据分析实战:睡眠质量关联研究
·
【实战】Python数据分析+机器学习:睡眠质量关联性研究完整流程
基于pandas、matplotlib、scikit-learn的完整数据分析案例,从数据预处理到模型部署,一篇搞定!
📊 前言
睡眠质量对健康和生活质量至关重要。在数字化时代,如何利用数据科学方法分析睡眠影响因素,成为热门研究话题。
本文将带你完成一个完整的数据分析项目,涵盖:
- ✅ 数据加载与预处理
- ✅ 数据可视化
- ✅ 特征工程
- ✅ 机器学习建模
- ✅ 结果分析与文档生成
🎯 项目概述
数据集介绍
我们使用包含400名受试者的睡眠数据集,包含13个特征:
| 特征分类 | 具体特征 | 数据类型 |
|---|---|---|
| 基本信息 | 性别、年龄、职业 | 分类/数值 |
| 睡眠数据 | 睡眠时长、睡眠质量 | 数值 |
| 身体状况 | 身体活动、压力、BMI、血压、心率 | 数值/分类 |
| 生活方式 | 每日步数 | 数值 |
| 睡眠障碍 | 睡眠障碍类型 | 分类 |
技术栈
pandas # 数据处理
matplotlib # 数据可视化
scikit-learn # 机器学习
🔧 第一步:环境配置与中文字体设置
常见问题:matplotlib中文显示乱码
Windows环境下,matplotlib默认不支持中文显示,这是新手最常遇到的问题。
解决方案
import matplotlib.pyplot as plt
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['font.size'] = 10
plt.style.use('default')
字体优先级:
- Microsoft YaHei(微软雅黑)- 推荐
- SimHei(黑体)- 备选
测试字体是否可用
创建测试程序验证字体配置:
import matplotlib.pyplot as plt
plt.figure(figsize=(8, 6))
plt.text(0.5, 0.5, '汉字测试\n数值: 123.45',
ha='center', va='center', fontsize=20)
plt.title('中文字体测试')
plt.show()
经验提示:在每个图表创建前重新设置字体参数,避免设置丢失。
📥 第二步:数据加载与探索
加载数据
import pandas as pd
# 加载CSV数据
df = pd.read_csv('sleep.csv')
print(f"数据集: {df.shape[0]}条记录, {df.shape[1]}个特征")
print("\n数据概览:")
print(df.head())
数据探索
# 查看数据类型
print(df.info())
# 查看统计信息
print(df.describe())
# 检查缺失值
print(df.isnull().sum())
输出结果:
数据集: 400条记录, 13个特征
数据完整性: 100% (无缺失值)
🛠️ 第三步:特征工程
3.1 血压数据分离
血压数据格式为"收缩压/舒张压",需要拆分:
# 分离血压字段
df['systolic_bp'] = df['blood_pressure'].apply(
lambda x: int(x.split('/')[0])
)
df['diastolic_bp'] = df['blood_pressure'].apply(
lambda x: int(x.split('/')[1])
)
代码解释:
apply(): 对每行数据应用函数split('/'): 按’/'分割字符串int(): 转换为整数类型
3.2 分类变量编码
机器学习模型需要数值型输入:
from sklearn.preprocessing import LabelEncoder
le_dict = {}
categorical_cols = ['gender', 'occupation', 'bmi_category', 'sleep_disorder']
for col in categorical_cols:
le = LabelEncoder()
df[f'{col}_encoded'] = le.fit_transform(df[col])
le_dict[col] = le # 保存编码器便于后续解码
编码结果示例:
gender: Male -> 1, Female -> 0
occupation: Doctor -> 0, Nurse -> 1, ...
3.3 创建二分类标签
# 是否存在睡眠障碍(0=无,1=有)
df['has_disorder'] = df['sleep_disorder_encoded'].apply(
lambda x: 1 if x > 0 else 0
)
3.4 数据标准化
消除不同特征量纲的影响:
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
# 选择特征
features = ['age', 'gender_encoded', 'sleep_quality',
'physical_activity_level', 'stress_level',
'bmi_category_encoded', 'heart_rate', 'daily_steps',
'systolic_bp', 'diastolic_bp']
X = df[features]
y = df['sleep_duration']
# 标准化
X_scaled = StandardScaler().fit_transform(X)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
X_scaled, y, test_size=0.2, random_state=42
)
参数说明:
test_size=0.2: 80%训练,20%测试random_state=42: 随机种子,确保结果可复现
📊 第四步:数据可视化
4.1 创建多子图布局
fig, axes = plt.subplots(2, 3, figsize=(18, 12))
4.2 绘制睡眠时长直方图
# 睡眠时长分布
axes[0, 0].hist(df['sleep_duration'], bins=20,
color='skyblue', edgecolor='black', alpha=0.7)
axes[0, 0].axvline(df['sleep_duration'].mean(),
color='red', linestyle='--', linewidth=2)
axes[0, 0].set_xlabel('睡眠时长(小时)')
axes[0, 0].set_ylabel('频数')
axes[0, 0].set_title('睡眠时长分布', fontweight='bold')
效果图:
直方图显示:大部分人的睡眠时长在7-9小时
红色虚线:标记均值位置(8.04小时)
4.3 绘制散点图(彩色编码)
# 年龄与睡眠时长关系(按睡眠质量着色)
scatter = axes[1, 1].scatter(
df['age'], df['sleep_duration'],
c=df['sleep_quality'], cmap='viridis',
alpha=0.6, s=50
)
axes[1, 1].set_xlabel('年龄')
axes[1, 1].set_ylabel('睡眠时长(小时)')
axes[1, 1].set_title('年龄与睡眠时长关系', fontweight='bold')
# 添加颜色条
plt.colorbar(scatter, ax=axes[1, 1], label='睡眠质量')
技巧:
cmap='viridis': 使用viridis颜色映射alpha=0.6: 设置透明度colorbar: 添加图例说明
4.4 绘制饼图
# BMI分类分布
bmi_counts = df['bmi_category'].value_counts()
axes[0, 2].pie(
bmi_counts.values,
labels=bmi_counts.index,
autopct='%1.1f%%',
colors=['#ff9999', '#66b3ff', '#99ff99', '#ffcc99']
)
axes[0, 2].set_title('BMI分类分布', fontweight='bold')
4.5 自定义相关性热图
import numpy as np
# 选择数值型特征
numeric_cols = ['age', 'sleep_duration', 'sleep_quality',
'physical_activity_level', 'stress_level',
'heart_rate', 'daily_steps']
corr = df[numeric_cols].corr()
# 绘制热图
im = axes[0, 0].imshow(corr, cmap='coolwarm',
aspect='auto', vmin=-1, vmax=1)
axes[0, 0].set_xticks(range(len(numeric_cols)))
axes[0, 0].set_yticks(range(len(numeric_cols)))
axes[0, 0].set_xticklabels(numeric_cols, rotation=45, ha='right')
axes[0, 0].set_yticklabels(numeric_cols)
axes[0, 0].set_title('特征相关性热图', fontweight='bold')
# 添加数值标注
for i in range(len(numeric_cols)):
for j in range(len(numeric_cols)):
axes[0, 0].text(j, i, f'{corr.iloc[i, j]:.2f}',
ha='center', va='center', fontsize=8)
# 添加颜色条
plt.colorbar(im, ax=axes[0, 0], shrink=0.8).set_label(
'相关系数', rotation=270, labelpad=20
)
核心技巧:
vmin=-1, vmax=1: 设置颜色范围为完全负相关到完全正相关- 双重循环添加相关系数数值
rotation=270: 旋转颜色条标签
4.6 保存图表
plt.tight_layout() # 自动调整布局
plt.savefig('figure1_overview.png', dpi=300, bbox_inches='tight')
plt.close()
参数说明:
dpi=300: 高分辨率,适合论文发表bbox_inches='tight': 紧凑布局,避免内容被裁剪
🤖 第五步:机器学习建模
5.1 随机森林回归(预测睡眠时长)
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error, r2_score
# 定义特征
features_reg = ['age', 'gender_encoded', 'sleep_quality',
'physical_activity_level', 'stress_level',
'bmi_category_encoded', 'heart_rate', 'daily_steps',
'systolic_bp', 'diastolic_bp']
# 准备数据
X_reg = df[features_reg]
y_reg = df['sleep_duration']
X_reg_scaled = StandardScaler().fit_transform(X_reg)
X_train, X_test, y_train, y_test = train_test_split(
X_reg_scaled, y_reg, test_size=0.2, random_state=42
)
# 训练模型
rf_reg = RandomForestRegressor(n_estimators=100, random_state=42)
rf_reg.fit(X_train, y_train)
# 预测
y_pred_reg = rf_reg.predict(X_test)
# 评估
mse = mean_squared_error(y_test, y_pred_reg)
r2 = r2_score(y_test, y_pred_reg)
print(f"随机森林回归 MSE: {mse:.4f}")
print(f"随机森林回归 R2: {r2:.4f}")
输出结果:
随机森林回归 MSE: 4.9658
随机森林回归 R2: -0.0657
5.2 随机森林分类(预测睡眠障碍)
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
# 定义特征(包含睡眠时长)
features_clf = ['age', 'gender_encoded', 'sleep_duration',
'sleep_quality', 'physical_activity_level',
'stress_level', 'bmi_category_encoded',
'heart_rate', 'daily_steps',
'systolic_bp', 'diastolic_bp']
# 准备数据
X_clf = df[features_clf]
y_clf = df['has_disorder']
X_train_clf, X_test_clf, y_train_clf, y_test_clf = train_test_split(
X_clf, y_clf, test_size=0.2, random_state=42
)
# 训练模型
rf_clf = RandomForestClassifier(n_estimators=100, random_state=42)
rf_clf.fit(X_train_clf, y_train_clf)
# 预测
y_pred_clf = rf_clf.predict(X_test_clf)
# 评估
acc = accuracy_score(y_test_clf, y_pred_clf)
print(f"随机森林分类 准确率: {acc:.4f}")
输出结果:
随机森林分类 准确率: 0.7875
5.3 特征重要性分析
import pandas as pd
# 回归模型特征重要性
feature_imp = pd.DataFrame({
'特征': features_reg,
'重要性': rf_reg.feature_importances_
})
feature_imp = feature_imp.sort_values('重要性', ascending=False)
print("特征重要性TOP 5:")
print(feature_imp.head(5))
输出结果:
特征重要性TOP 5:
特征 重要性
9 daily_steps 0.1772
2 sleep_quality 0.1477
6 physical_activity_level 0.1452
7 heart_rate 0.1265
10 diastolic_bp 0.0984
📈 第六步:模型结果可视化
6.1 预测值vs实际值
fig, axes = plt.subplots(2, 2, figsize=(16, 12))
# 预测vs实际
axes[0, 0].scatter(y_test, y_pred_reg, alpha=0.6, s=50, color='blue')
axes[0, 0].plot([y_test.min(), y_test.max()],
[y_test.min(), y_test.max()], 'r--', lw=2)
axes[0, 0].set_xlabel('实际睡眠时长')
axes[0, 0].set_ylabel('预测睡眠时长')
axes[0, 0].set_title('回归预测vs实际', fontweight='bold')
axes[0, 0].legend(['完美预测'])
axes[0, 0].grid(True, alpha=0.3)
6.2 残差图
# 残差图
residuals = y_test - y_pred_reg
axes[0, 1].scatter(y_pred_reg, residuals, alpha=0.6, s=50, color='green')
axes[0, 1].axhline(y=0, color='red', linestyle='--', lw=2)
axes[0, 1].set_xlabel('预测值')
axes[0, 1].set_ylabel('残差')
axes[0, 1].set_title('回归残差图', fontweight='bold')
axes[0, 1].grid(True, alpha=0.3)
6.3 特征重要性条形图
# 回归特征重要性
top_reg = feature_imp.head(8)
axes[1, 0].barh(top_reg['特征'], top_reg['重要性'], color='coral')
axes[1, 0].set_xlabel('重要性')
axes[1, 0].set_ylabel('特征')
axes[1, 0].set_title('回归特征重要性', fontweight='bold')
axes[1, 0].invert_yaxis()
# 分类特征重要性
clf_imp = pd.DataFrame({
'特征': features_clf,
'重要性': rf_clf.feature_importances_
})
clf_imp = clf_imp.sort_values('重要性', ascending=False)
top_clf = clf_imp.head(8)
axes[1, 1].barh(top_clf['特征'], top_clf['重要性'], color='skyblue')
axes[1, 1].set_xlabel('重要性')
axes[1, 1].set_ylabel('特征')
axes[1, 1].set_title('分类特征重要性', fontweight='bold')
axes[1, 1].invert_yaxis()
plt.tight_layout()
plt.savefig('figure3_model_results.png', dpi=300, bbox_inches='tight')
plt.close()
💡 第七步:结果分析与建议
核心发现
1. 每日步数是最重要的影响因素 🏃
- 回归模型重要性: 17.72%
- 分类模型重要性: 14.21%
建议: 每天保持8000-10000步的活动量
2. 身体活动水平次重要 🏋️
- 与睡眠质量呈正相关
- 中高强度运动效果更佳
建议: 每周至少150分钟中等强度有氧运动
3. 睡眠质量与时长相互影响 😴
- 形成良性循环
- 高质量睡眠延长睡眠时长
建议: 建立规律作息,固定上床和起床时间
4. 心率反映健康状态 ❤️
- 较低静息心率与更好睡眠相关
- 通过运动和压力管理可改善
建议: 学习冥想、深呼吸等放松技巧
模型性能分析
| 模型 | 任务 | 指标 | 结果 | 评价 |
|---|---|---|---|---|
| 随机森林回归 | 预测睡眠时长 | MSE: 4.97 | 预测能力有限 | |
| 随机森林回归 | 预测睡眠时长 | R²: -0.07 | 解释性差 | |
| 随机森林分类 | 预测睡眠障碍 | 准确率: 78.75% | 表现良好 |
📚 完整代码下载
代码文件:
sleep_analysis.py- 主分析程序(342行)test_chinese_font.py- 字体测试程序(160行)
可视化结果:
figure1_overview.png- 数据概览figure2_correlation.png- 相关性分析figure3_model_results.png- 模型结果
数据文件:
sleep.csv- 原始数据集
🎓 学习要点总结
技术要点
-
数据预处理
- 分类变量编码(LabelEncoder)
- 特征转换(lambda函数)
- 数据标准化(StandardScaler)
-
数据可视化
- 中文字体配置
- 多子图布局
- 自定义热图实现
- 高清图表保存
-
机器学习
- 随机森林算法
- 回归vs分类任务
- 特征重要性分析
- 模型评估指标
常见问题
| 问题 | 解决方案 |
|---|---|
| 中文显示乱码 | 设置字体:plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] |
| 负号显示异常 | 设置:plt.rcParams['axes.unicode_minus'] = False |
| 图片被裁剪 | 保存时加参数:bbox_inches='tight' |
| 代码复现性差 | 设置随机种子:random_state=42 |
🚀 扩展方向
短期优化
- 添加超参数调优(GridSearchCV)
- 使用交叉验证
- 尝试更多算法(XGBoost、LightGBM)
中期扩展
- 交互式可视化(Dash、Streamlit)
- 增加数据量(1000+样本)
- 添加更多特征(饮食、心理健康)
长期规划
- 开发Web应用
- 部署为API服务
- 深度学习模型(神经网络)
💬 交流讨论
欢迎评论区留言讨论:
- 💡 数据分析经验分享
- 🐛 代码问题咨询
- 📈 模型改进建议
- 🎯 学习心得交流
相关推荐:
📖 参考资料
如果本文对你有帮助,请点赞、收藏、转发支持!
关注我,获取更多Python数据分析、机器学习实战教程!
更多推荐
所有评论(0)