diff --git a/test/.claude/settings.local.json b/test/.claude/settings.local.json new file mode 100644 index 0000000..4613a15 --- /dev/null +++ b/test/.claude/settings.local.json @@ -0,0 +1,10 @@ +{ + "permissions": { + "allow": [ + "Bash(python -c \"import pandas as pd; df = pd.read_excel\\('data/心血管疾病.xlsx', nrows=5\\); print\\('Columns:', df.columns.tolist\\(\\)\\); print\\('Data types:', df.dtypes\\); print\\('Sample data:'\\); print\\(df.head\\(\\)\\)\")", + "Bash(\"D:\\\\software\\\\anaconda\\\\Scripts\\\\conda.exe\" run:*)", + "Bash(\"D:\\\\software\\\\anaconda\\\\envs\\\\cardioenv\\\\python.exe\" module1_dashboard/test_data.py)", + "Bash(\"D:\\\\software\\\\anaconda\\\\envs\\\\cardioenv\\\\python.exe\" -m streamlit run module1_dashboard/cardio_dashboard.py --help)" + ] + } +} diff --git a/test/.env b/test/.env new file mode 100644 index 0000000..2b5cc08 --- /dev/null +++ b/test/.env @@ -0,0 +1,36 @@ +# CardioAI Configuration +# Environment variables for the cardiovascular disease intelligent assistant system + +# Data file path (relative to project root) +DATA_PATH=./data/心血管疾病.xlsx + +# Flask server configuration +FLASK_APP=module2_predictor/app.py +FLASK_ENV=development +FLASK_DEBUG=True +FLASK_HOST=0.0.0.0 +FLASK_PORT=5000 + +# Streamlit dashboard configuration +STREAMLIT_PORT=8501 +STREAMLIT_THEME=light + +# AI Model API Keys (replace with your actual keys) +# DeepSeek API (from dashscope) +DASHSCOPE_API_KEY=your_dashscope_api_key_here + +# OpenAI API (for langchain-openai, if used) +OPENAI_API_KEY=your_openai_api_key_here + +# Voice assistant configuration +VOICE_ASSISTANT_PORT=5001 +VOICE_ASSISTANT_HOST=0.0.0.0 + +# Model file paths (will be generated during training) +MODEL_PATH=./module2_predictor/models/xgb_model.pkl +SCALER_PATH=./module2_predictor/models/scaler.pkl +ENCODER_PATH=./module2_predictor/models/encoder.pkl + +# Feature configuration +NUMERICAL_FEATURES=age,trestbps,chol,thalach,oldpeak +CATEGORICAL_FEATURES=sex,cp,fbs,restecg,exang,slope,ca,thal \ No newline at end of file diff --git a/test/.idea/.gitignore b/test/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/test/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/test/.idea/inspectionProfiles/profiles_settings.xml b/test/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/test/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/test/.idea/misc.xml b/test/.idea/misc.xml new file mode 100644 index 0000000..c5cfaed --- /dev/null +++ b/test/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/test/.idea/modules.xml b/test/.idea/modules.xml new file mode 100644 index 0000000..51ab974 --- /dev/null +++ b/test/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/test/.idea/test.iml b/test/.idea/test.iml new file mode 100644 index 0000000..43df6ad --- /dev/null +++ b/test/.idea/test.iml @@ -0,0 +1,16 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/test/.idea/vcs.xml b/test/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/test/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..71c20a9 --- /dev/null +++ b/test/README.md @@ -0,0 +1,264 @@ +# CardioAI - 心血管疾病智能辅助系统 + +## 项目概述 + +CardioAI是一个多模块应用系统,集成了数据可视化、机器学习预测和AI语音问答功能,用于心血管疾病的智能辅助分析和诊断。 + +### 系统模块 + +1. **Module 1: 数据可视化仪表板** (Streamlit) - 本模块 + - 数据清洗与特征工程 + - 交互式数据筛选 + - 可视化分析图表 + +2. **Module 2: 机器学习预测器** (Flask + XGBoost) + - 心血管疾病风险预测模型 + - RESTful API接口 + - 实时预测服务 + +3. **Module 3: AI语音助手** (DeepSeek + CosyVoice) + - 自然语言问答 + - 语音交互界面 + - 疾病知识查询 + +## Module 1: 数据可视化仪表板 + +### 功能特性 + +- ✅ **数据加载与清洗**: 自动处理异常值和缺失数据 +- ✅ **特征工程**: 年龄转换、BMI计算、类别编码 +- ✅ **交互式筛选**: 侧边栏多维度数据筛选 +- ✅ **可视化分析**: Plotly交互式图表 +- ✅ **性能优化**: 使用缓存加速数据加载 + +### 数据处理流程 + +1. **数据加载**: 从Excel文件加载原始数据 +2. **年龄转换**: 将天数转换为年数(四舍五入) +3. **BMI计算**: `BMI = 体重(kg) / (身高(m)^2)` +4. **异常值处理**: + - 删除舒张压 ≥ 收缩压的记录 + - 删除收缩压不在[90, 250] mmHg范围的记录 + - 删除舒张压不在[60, 150] mmHg范围的记录 +5. **类别转换**: + - 胆固醇水平: 1=正常, 2=高于正常, 3=极高 + - 血糖水平: 1=正常, 2=高于正常, 3=极高 + - 性别: 1=女性, 2=男性 + - BMI分类: <18.5=偏瘦, 18.5-24.9=正常, 25-29.9=超重, ≥30=肥胖 + +### 快速开始 + +#### 1. 环境配置 + +```bash +# 创建并激活conda虚拟环境 +conda create -n cardioenv python=3.10 +conda activate cardioenv + +# 安装依赖包 +pip install -r requirements.txt +``` + +#### 2. 数据准备 + +确保数据文件位于正确路径: +``` +项目根目录/ +├── data/ +│ └── 心血管疾病.xlsx +└── module1_dashboard/ + └── cardio_dashboard.py +``` + +#### 3. 启动仪表板 + +```bash +# 进入项目根目录 +cd D:\Project\PythonProject\AIcode\test + +# 激活conda环境 +conda activate cardioenv + +# 启动Streamlit应用程序 +streamlit run module1_dashboard/cardio_dashboard.py +``` + +或者使用conda直接运行: + +```bash +"D:\software\anaconda\Scripts\conda.exe" run -n cardioenv streamlit run module1_dashboard/cardio_dashboard.py +``` + +#### 4. 访问应用 + +打开浏览器,访问: [http://localhost:8501](http://localhost:8501) + +### 界面说明 + +#### 侧边栏筛选器 +- **年龄范围**: 滑动选择器,筛选指定年龄范围的记录 +- **性别**: 多选框,选择要分析的性别(女性/男性) +- **心血管疾病状态**: 多选框,选择疾病状态(有/无) +- **BMI分类**: 多选框,选择BMI分类(偏瘦/正常/超重/肥胖) +- **胆固醇水平**: 多选框,选择胆固醇水平 +- **血糖水平**: 多选框,选择血糖水平 + +#### 主界面区域 + +1. **关键指标面板** + - 筛选后记录数 + - 心血管疾病风险率 + - 平均年龄 + - 平均BMI + +2. **数据可视化图表** + - 年龄分布与心血管疾病关系直方图 + - BMI分类对心血管疾病影响的堆叠柱状图 + - 血压关系散点图 + - 胆固醇水平分布饼图 + - 血糖水平分布饼图 + +3. **数据预览** + - 数据摘要(形状、类型、缺失值) + - 原始数据表格(可自定义显示的列) + +### 配置文件说明 + +`.env` 文件包含以下配置项: + +```ini +# 数据文件路径 +DATA_PATH=./data/心血管疾病.xlsx + +# Flask服务器配置 +FLASK_APP=module2_predictor/app.py +FLASK_ENV=development + +# Streamlit配置 +STREAMLIT_PORT=8501 + +# AI模型API密钥(需要替换为实际值) +DASHSCOPE_API_KEY=your_dashscope_api_key_here +OPENAI_API_KEY=your_openai_api_key_here + +# 模型文件路径 +MODEL_PATH=./module2_predictor/models/xgb_model.pkl +``` + +### 依赖包说明 + +详细依赖见 `requirements.txt`: + +- **数据处理**: pandas, numpy, openpyxl +- **机器学习**: scikit-learn, xgboost, joblib +- **可视化**: streamlit, plotly +- **Web服务**: Flask +- **环境管理**: python-dotenv +- **AI集成**: langchain-openai, dashscope, requests + +### 常见问题 + +#### Q1: 数据加载失败 +**症状**: 应用程序无法启动,提示文件找不到或格式错误 +**解决**: +1. 检查 `data/心血管疾病.xlsx` 文件是否存在 +2. 确认文件格式为Excel 2007+ (.xlsx) +3. 检查文件编码,确保不是二进制损坏 + +#### Q2: 图表显示异常 +**症状**: 图表不显示或显示错误 +**解决**: +1. 检查Plotly是否正确安装:`pip install plotly` +2. 确保数据经过正确清洗,没有无限值或NaN +3. 检查筛选条件是否过于严格导致无数据 + +#### Q3: 应用程序运行缓慢 +**症状**: 页面响应慢,筛选操作延迟 +**解决**: +1. 利用 `@st.cache_data` 装饰器的缓存功能 +2. 减少一次性加载的数据量 +3. 优化图表复杂度,减少数据点数量 + +#### Q4: 中文显示乱码 +**症状**: 中文文字显示为乱码 +**解决**: +1. 确保系统支持中文字体 +2. Streamlit默认支持UTF-8编码,检查源代码文件保存为UTF-8 +3. 在Windows系统上,设置控制台编码为UTF-8 + +### 开发说明 + +#### 项目结构 +``` +D:\Project\PythonProject\AIcode\test\ +├── data\ # 数据文件目录 +│ └── 心血管疾病.xlsx +├── module1_dashboard\ # 数据可视化模块 +│ ├── cardio_dashboard.py +│ └── test_data.py +├── module2_predictor\ # 机器学习预测模块 +│ └── templates\ +├── module3_voice_assistant\ # AI语音助手模块 +│ └── templates\ +├── requirements.txt # Python依赖包 +├── .env # 环境配置 +└── README.md # 项目文档 +``` + +#### 代码结构 +- `load_and_process_data()`: 数据加载和清洗主函数,使用 `@st.cache_data` 缓存 +- `create_filters()`: 创建侧边栏筛选器组件 +- `apply_filters()`: 应用筛选条件到数据框 +- `display_metrics()`: 显示关键指标卡片 +- `create_visualizations()`: 创建所有可视化图表 +- `display_data_preview()`: 显示数据预览和摘要 + +#### 扩展开发 + +1. **添加新图表** + ```python + def create_new_chart(df): + fig = px.scatter(df, x='column1', y='column2', color='cardio_str') + st.plotly_chart(fig, use_container_width=True) + ``` + +2. **添加新筛选器** + ```python + # 在create_filters函数中添加 + new_filter = st.sidebar.selectbox("新筛选器", options=['选项1', '选项2']) + ``` + +3. **自定义样式** + ```python + st.markdown(""" + + """, unsafe_allow_html=True) + ``` + +### 性能优化建议 + +1. **数据缓存**: 所有数据处理函数使用 `@st.cache_data` 装饰器 +2. **增量加载**: 对于大型数据集,考虑分页或懒加载 +3. **图表优化**: 使用采样或聚合减少数据点数量 +4. **异步处理**: 长时间操作使用异步函数避免阻塞UI + +### 下一步计划 + +1. **Module 2开发**: 实现XGBoost预测模型和Flask API +2. **Module 3开发**: 集成DeepSeek和CosyVoice语音助手 +3. **功能增强**: 添加数据导出、报告生成功能 +4. **部署优化**: Docker容器化,云平台部署 + +### 技术支持 + +- **问题反馈**: 检查GitHub Issues或联系开发团队 +- **文档更新**: 随着功能迭代保持文档同步 +- **版本管理**: 使用Git进行版本控制,定期发布稳定版本 + +--- + +**版权所有 © 2024 CardioAI项目组** +**版本**: 1.0.0 +**最后更新**: 2024-04-02 \ No newline at end of file diff --git a/test/data/心血管疾病.xlsx b/test/data/心血管疾病.xlsx new file mode 100644 index 0000000..6a4700e Binary files /dev/null and b/test/data/心血管疾病.xlsx differ diff --git a/test/module1_dashboard/__pycache__/cardio_dashboard.cpython-310.pyc b/test/module1_dashboard/__pycache__/cardio_dashboard.cpython-310.pyc new file mode 100644 index 0000000..0ce8369 Binary files /dev/null and b/test/module1_dashboard/__pycache__/cardio_dashboard.cpython-310.pyc differ diff --git a/test/module1_dashboard/cardio_dashboard.py b/test/module1_dashboard/cardio_dashboard.py new file mode 100644 index 0000000..d19d487 --- /dev/null +++ b/test/module1_dashboard/cardio_dashboard.py @@ -0,0 +1,581 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +CardioAI - 心血管疾病智能辅助系统 +数据可视化仪表板模块 + +功能: +1. 数据加载与清洗 +2. 特征工程(年龄转换、BMI计算、类别转换) +3. 交互式数据筛选 +4. 可视化分析(Plotly图表) +""" + +import streamlit as st +import pandas as pd +import numpy as np +import plotly.express as px +import plotly.graph_objects as go +from pathlib import Path +import sys +import os + +# 设置页面配置 +st.set_page_config( + page_title="CardioAI - 心血管疾病分析仪表板", + page_icon="❤️", + layout="wide", + initial_sidebar_state="expanded" +) + +# 添加项目根目录到Python路径,确保可以导入其他模块 +project_root = Path(__file__).parent.parent +sys.path.append(str(project_root)) + +# 设置中文显示 +st.markdown(""" + +""", unsafe_allow_html=True) + +# 数据文件路径 +DATA_PATH = Path(__file__).parent.parent / "data" / "心血管疾病.xlsx" + +@st.cache_data(show_spinner="正在加载和清洗数据...") +def load_and_process_data(): + """ + 加载Excel数据并进行清洗和特征工程 + + 返回: + pd.DataFrame: 处理后的数据框 + """ + try: + # 加载数据 + st.info(f"正在从 {DATA_PATH} 加载数据...") + df = pd.read_excel(DATA_PATH) + + # 检查必要列是否存在 + required_columns = ['age', 'gender', 'height', 'weight', 'ap_hi', 'ap_lo', + 'cholesterol', 'gluc', 'cardio'] + missing_columns = [col for col in required_columns if col not in df.columns] + if missing_columns: + st.error(f"数据文件中缺少必要列: {missing_columns}") + return pd.DataFrame() + + # 创建数据副本 + df_processed = df.copy() + + # 1. 年龄转换:从天转换为年(四舍五入) + df_processed['age_years'] = (df_processed['age'] / 365.25).round().astype(int) + + # 2. 计算BMI: BMI = weight(kg) / (height(m)^2) + # 注意:height数据单位为厘米,需要转换为米 + df_processed['bmi'] = df_processed['weight'] / ((df_processed['height'] / 100) ** 2) + df_processed['bmi'] = df_processed['bmi'].round(2) + + # 3. 异常值处理 + # 删除舒张压 >= 收缩压的记录 + invalid_bp = df_processed['ap_lo'] >= df_processed['ap_hi'] + if invalid_bp.any(): + st.warning(f"删除 {invalid_bp.sum()} 条舒张压 >= 收缩压的异常记录") + df_processed = df_processed[~invalid_bp].copy() + + # 删除血压极端异常值 + # 收缩压 ∈ [90, 250], 舒张压 ∈ [60, 150] + bp_outliers = ~((df_processed['ap_hi'] >= 90) & (df_processed['ap_hi'] <= 250) & + (df_processed['ap_lo'] >= 60) & (df_processed['ap_lo'] <= 150)) + if bp_outliers.any(): + st.warning(f"删除 {bp_outliers.sum()} 条血压极端异常值记录") + df_processed = df_processed[~bp_outliers].copy() + + # 4. 类别转换 + # cholesterol转换 + cholesterol_map = { + 1: "正常", + 2: "高于正常", + 3: "极高" + } + df_processed['cholesterol_str'] = df_processed['cholesterol'].map(cholesterol_map) + + # gluc转换 + gluc_map = { + 1: "正常", + 2: "高于正常", + 3: "极高" + } + df_processed['gluc_str'] = df_processed['gluc'].map(gluc_map) + + # gender转换 + gender_map = { + 1: "女性", + 2: "男性" + } + df_processed['gender_str'] = df_processed['gender'].map(gender_map) + + # cardio转换 + cardio_map = { + 0: "无心血管疾病", + 1: "有心血管疾病" + } + df_processed['cardio_str'] = df_processed['cardio'].map(cardio_map) + + # 5. BMI分类 + def categorize_bmi(bmi): + if bmi < 18.5: + return "偏瘦" + elif 18.5 <= bmi < 24.9: + return "正常" + elif 25 <= bmi < 29.9: + return "超重" + else: + return "肥胖" + + df_processed['bmi_category'] = df_processed['bmi'].apply(categorize_bmi) + + # 记录处理后的数据信息 + st.success(f"数据加载和清洗完成!共处理 {len(df_processed)} 条记录") + st.info(f"原始数据: {len(df)} 条记录, 清洗后: {len(df_processed)} 条记录") + + return df_processed + + except Exception as e: + st.error(f"数据加载失败: {str(e)}") + return pd.DataFrame() + +def create_filters(df): + """ + 创建侧边栏筛选器 + + 参数: + df: 处理后的数据框 + + 返回: + dict: 筛选条件字典 + """ + st.sidebar.markdown("## 🔍 数据筛选") + + # 年龄范围筛选 + min_age = int(df['age_years'].min()) + max_age = int(df['age_years'].max()) + age_range = st.sidebar.slider( + "选择年龄范围:", + min_value=min_age, + max_value=max_age, + value=(min_age, max_age), + help="筛选指定年龄范围内的记录" + ) + + # 性别筛选 + gender_options = df['gender_str'].unique().tolist() + selected_genders = st.sidebar.multiselect( + "选择性别:", + options=gender_options, + default=gender_options, + help="选择要分析的性别" + ) + + # 心血管疾病状态筛选 + cardio_options = df['cardio_str'].unique().tolist() + selected_cardio = st.sidebar.multiselect( + "选择心血管疾病状态:", + options=cardio_options, + default=cardio_options, + help="选择要分析的心血管疾病状态" + ) + + # BMI分类筛选 + bmi_options = df['bmi_category'].unique().tolist() + selected_bmi = st.sidebar.multiselect( + "选择BMI分类:", + options=bmi_options, + default=bmi_options, + help="选择要分析的BMI分类" + ) + + # 胆固醇水平筛选 + cholesterol_options = df['cholesterol_str'].unique().tolist() + selected_cholesterol = st.sidebar.multiselect( + "选择胆固醇水平:", + options=cholesterol_options, + default=cholesterol_options, + help="选择要分析的胆固醇水平" + ) + + # 血糖水平筛选 + gluc_options = df['gluc_str'].unique().tolist() + selected_gluc = st.sidebar.multiselect( + "选择血糖水平:", + options=gluc_options, + default=gluc_options, + help="选择要分析的血糖水平" + ) + + return { + 'age_range': age_range, + 'genders': selected_genders, + 'cardio': selected_cardio, + 'bmi_categories': selected_bmi, + 'cholesterol': selected_cholesterol, + 'gluc': selected_gluc + } + +def apply_filters(df, filters): + """ + 应用筛选条件到数据框 + + 参数: + df: 原始数据框 + filters: 筛选条件字典 + + 返回: + pd.DataFrame: 筛选后的数据框 + """ + filtered_df = df.copy() + + # 应用年龄筛选 + filtered_df = filtered_df[ + (filtered_df['age_years'] >= filters['age_range'][0]) & + (filtered_df['age_years'] <= filters['age_range'][1]) + ] + + # 应用性别筛选 + if filters['genders']: + filtered_df = filtered_df[filtered_df['gender_str'].isin(filters['genders'])] + + # 应用心血管疾病筛选 + if filters['cardio']: + filtered_df = filtered_df[filtered_df['cardio_str'].isin(filters['cardio'])] + + # 应用BMI分类筛选 + if filters['bmi_categories']: + filtered_df = filtered_df[filtered_df['bmi_category'].isin(filters['bmi_categories'])] + + # 应用胆固醇筛选 + if filters['cholesterol']: + filtered_df = filtered_df[filtered_df['cholesterol_str'].isin(filters['cholesterol'])] + + # 应用血糖筛选 + if filters['gluc']: + filtered_df = filtered_df[filtered_df['gluc_str'].isin(filters['gluc'])] + + return filtered_df + +def display_metrics(filtered_df, original_df): + """ + 显示关键指标 + + 参数: + filtered_df: 筛选后的数据框 + original_df: 原始数据框 + """ + col1, col2, col3, col4 = st.columns(4) + + with col1: + st.metric( + label="筛选后记录数", + value=f"{len(filtered_df):,}", + delta=f"{len(filtered_df) - len(original_df):+,}" + ) + + with col2: + # 心血管疾病风险率 + if len(filtered_df) > 0: + risk_rate = (filtered_df['cardio'].sum() / len(filtered_df) * 100).round(2) + st.metric( + label="心血管疾病风险率", + value=f"{risk_rate}%", + help="当前筛选条件下心血管疾病患者比例" + ) + else: + st.metric(label="心血管疾病风险率", value="N/A") + + with col3: + # 平均年龄 + if len(filtered_df) > 0: + avg_age = filtered_df['age_years'].mean().round(1) + st.metric( + label="平均年龄", + value=f"{avg_age} 岁", + help="当前筛选条件下的平均年龄" + ) + else: + st.metric(label="平均年龄", value="N/A") + + with col4: + # 平均BMI + if len(filtered_df) > 0: + avg_bmi = filtered_df['bmi'].mean().round(1) + st.metric( + label="平均BMI", + value=str(avg_bmi), + help="当前筛选条件下的平均身体质量指数" + ) + else: + st.metric(label="平均BMI", value="N/A") + +def create_visualizations(df): + """ + 创建可视化图表 + + 参数: + df: 要可视化的数据框 + """ + if len(df) == 0: + st.warning("没有可用的数据进行可视化") + return + + st.markdown("## 📊 数据可视化分析") + + # 图1: 年龄分布直方图(按心血管疾病状态区分) + col1, col2 = st.columns(2) + + with col1: + st.markdown("### 年龄分布分析") + fig_age = px.histogram( + df, + x='age_years', + color='cardio_str', + nbins=30, + barmode='overlay', + opacity=0.7, + labels={ + 'age_years': '年龄(岁)', + 'cardio_str': '心血管疾病状态', + 'count': '人数' + }, + title="年龄分布与心血管疾病关系", + color_discrete_map={ + "有心血管疾病": "#e63946", + "无心血管疾病": "#457b9d" + } + ) + fig_age.update_layout( + legend_title="疾病状态", + hovermode='x unified' + ) + st.plotly_chart(fig_age, use_container_width=True) + + with col2: + st.markdown("### BMI分类与心血管疾病关系") + # 创建交叉表 + bmi_cardio_cross = pd.crosstab( + df['bmi_category'], + df['cardio_str'], + normalize='index' + ).reset_index() + + # 转换为长格式 + bmi_cardio_long = bmi_cardio_cross.melt( + id_vars='bmi_category', + var_name='cardio_status', + value_name='proportion' + ) + + fig_bmi = px.bar( + bmi_cardio_long, + x='bmi_category', + y='proportion', + color='cardio_status', + barmode='stack', + labels={ + 'bmi_category': 'BMI分类', + 'proportion': '比例', + 'cardio_status': '心血管疾病状态' + }, + title="BMI分类对心血管疾病的影响", + color_discrete_map={ + "有心血管疾病": "#e63946", + "无心血管疾病": "#457b9d" + } + ) + fig_bmi.update_layout( + yaxis_tickformat='.1%', + legend_title="疾病状态" + ) + st.plotly_chart(fig_bmi, use_container_width=True) + + # 图3: 血压关系散点图 + st.markdown("### 血压关系分析") + fig_bp = px.scatter( + df, + x='ap_hi', + y='ap_lo', + color='cardio_str', + size='bmi', + hover_data=['age_years', 'gender_str', 'cholesterol_str'], + labels={ + 'ap_hi': '收缩压 (mmHg)', + 'ap_lo': '舒张压 (mmHg)', + 'cardio_str': '心血管疾病状态', + 'bmi': 'BMI' + }, + title="血压关系散点图", + color_discrete_map={ + "有心血管疾病": "#e63946", + "无心血管疾病": "#457b9d" + } + ) + fig_bp.update_layout(legend_title="疾病状态") + st.plotly_chart(fig_bp, use_container_width=True) + + # 图4: 胆固醇和血糖水平分析 + col3, col4 = st.columns(2) + + with col3: + st.markdown("### 胆固醇水平分布") + cholesterol_counts = df['cholesterol_str'].value_counts().reset_index() + cholesterol_counts.columns = ['cholesterol_level', 'count'] + + fig_chol = px.pie( + cholesterol_counts, + values='count', + names='cholesterol_level', + title="胆固醇水平分布", + color_discrete_sequence=px.colors.sequential.RdBu + ) + fig_chol.update_traces(textposition='inside', textinfo='percent+label') + st.plotly_chart(fig_chol, use_container_width=True) + + with col4: + st.markdown("### 血糖水平分布") + gluc_counts = df['gluc_str'].value_counts().reset_index() + gluc_counts.columns = ['gluc_level', 'count'] + + fig_gluc = px.pie( + gluc_counts, + values='count', + names='gluc_level', + title="血糖水平分布", + color_discrete_sequence=px.colors.sequential.Blues + ) + fig_gluc.update_traces(textposition='inside', textinfo='percent+label') + st.plotly_chart(fig_gluc, use_container_width=True) + +def display_data_preview(df): + """ + 显示数据预览 + + 参数: + df: 要预览的数据框 + """ + st.markdown("## 📋 数据预览") + + # 显示数据摘要 + with st.expander("数据摘要", expanded=False): + col1, col2 = st.columns(2) + with col1: + st.write("**数据形状:**", df.shape) + st.write("**数据类型:**") + st.write(df.dtypes.astype(str).reset_index().rename(columns={0: '类型', 'index': '列名'})) + + with col2: + st.write("**缺失值统计:**") + missing = df.isnull().sum().reset_index() + missing.columns = ['列名', '缺失值数量'] + missing = missing[missing['缺失值数量'] > 0] + if len(missing) > 0: + st.write(missing) + else: + st.write("无缺失值") + + # 显示数据表格 + with st.expander("查看原始数据", expanded=False): + # 选择要显示的列 + available_columns = df.columns.tolist() + default_columns = ['age_years', 'gender_str', 'bmi', 'bmi_category', + 'ap_hi', 'ap_lo', 'cholesterol_str', 'gluc_str', 'cardio_str'] + + selected_columns = st.multiselect( + "选择要显示的列:", + options=available_columns, + default=default_columns + ) + + if selected_columns: + display_df = df[selected_columns].copy() + st.dataframe(display_df.head(100), use_container_width=True) + st.caption(f"显示前 100 行(共 {len(df)} 行)") + else: + st.info("请选择要显示的列") + +def main(): + """ + 主函数 + """ + # 标题 + st.markdown('

❤️ CardioAI - 心血管疾病智能分析仪表板

', unsafe_allow_html=True) + st.markdown("---") + + # 加载数据 + with st.spinner("正在加载数据,请稍候..."): + df = load_and_process_data() + + if df.empty: + st.error("数据加载失败,请检查数据文件路径和格式") + return + + # 创建筛选器 + filters = create_filters(df) + + # 应用筛选 + filtered_df = apply_filters(df, filters) + + # 显示关键指标 + st.markdown("## 📈 关键指标") + display_metrics(filtered_df, df) + + # 显示数据预览 + display_data_preview(filtered_df) + + # 创建可视化图表 + create_visualizations(filtered_df) + + # 侧边栏信息 + st.sidebar.markdown("---") + st.sidebar.markdown("## ℹ️ 使用说明") + st.sidebar.info(""" + 1. 使用左侧筛选器选择要分析的数据子集 + 2. 查看上方的关键指标了解数据概况 + 3. 探索下方的可视化图表分析趋势和关系 + 4. 点击数据预览查看详细数据 + """) + + st.sidebar.markdown("## 📊 数据说明") + st.sidebar.info(""" + - **年龄**: 原始数据为天数,已转换为岁数 + - **BMI**: 身体质量指数,计算公式:体重(kg)/身高(m)² + - **血压**: 收缩压(ap_hi)和舒张压(ap_lo) + - **胆固醇/血糖**: 1=正常, 2=高于正常, 3=极高 + - **心血管疾病**: 0=无, 1=有 + """) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/test/module1_dashboard/test_data.py b/test/module1_dashboard/test_data.py new file mode 100644 index 0000000..65932af --- /dev/null +++ b/test/module1_dashboard/test_data.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +测试数据加载和处理的脚本 +""" + +import sys +import os + +# 添加父目录到路径 +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +# 导入数据处理函数 +from cardio_dashboard import load_and_process_data + +def test_data_loading(): + """测试数据加载和清洗功能""" + print("开始测试数据加载和清洗...") + + try: + # 加载数据 + df = load_and_process_data() + + if df.empty: + print("❌ 数据加载失败:返回空数据框") + return False + + print(f"✅ 数据加载成功!共 {len(df)} 条记录") + + # 检查必要的列 + required_columns = ['age_years', 'bmi', 'bmi_category', 'cholesterol_str', + 'gluc_str', 'gender_str', 'cardio_str', 'ap_hi', 'ap_lo'] + + missing_columns = [col for col in required_columns if col not in df.columns] + if missing_columns: + print(f"❌ 缺少必要的列: {missing_columns}") + return False + + print("✅ 所有必要的列都存在") + + # 检查数据类型 + print("\n数据摘要:") + print(f"- 年龄范围: {df['age_years'].min()} ~ {df['age_years'].max()} 岁") + print(f"- BMI范围: {df['bmi'].min():.1f} ~ {df['bmi'].max():.1f}") + print(f"- 收缩压范围: {df['ap_hi'].min()} ~ {df['ap_hi'].max()} mmHg") + print(f"- 舒张压范围: {df['ap_lo'].min()} ~ {df['ap_lo'].max()} mmHg") + + # 检查类别转换 + print("\n类别分布:") + print(f"- 性别: {df['gender_str'].value_counts().to_dict()}") + print(f"- 心血管疾病: {df['cardio_str'].value_counts().to_dict()}") + print(f"- BMI分类: {df['bmi_category'].value_counts().to_dict()}") + print(f"- 胆固醇水平: {df['cholesterol_str'].value_counts().to_dict()}") + print(f"- 血糖水平: {df['gluc_str'].value_counts().to_dict()}") + + # 检查异常值处理 + invalid_bp = df['ap_lo'] >= df['ap_hi'] + if invalid_bp.any(): + print(f"❌ 仍然存在舒张压 >= 收缩压的记录: {invalid_bp.sum()} 条") + return False + else: + print("✅ 已成功删除舒张压 >= 收缩压的记录") + + # 检查血压范围 + bp_in_range = ((df['ap_hi'] >= 90) & (df['ap_hi'] <= 250) & + (df['ap_lo'] >= 60) & (df['ap_lo'] <= 150)) + if not bp_in_range.all(): + print(f"❌ 仍然存在血压异常值: {(~bp_in_range).sum()} 条") + return False + else: + print("✅ 所有血压值都在正常范围内") + + print("\n🎉 所有测试通过!") + return True + + except Exception as e: + print(f"❌ 测试过程中发生错误: {str(e)}") + import traceback + traceback.print_exc() + return False + +if __name__ == "__main__": + success = test_data_loading() + sys.exit(0 if success else 1) \ No newline at end of file diff --git a/test/requirements.txt b/test/requirements.txt new file mode 100644 index 0000000..988a6a6 --- /dev/null +++ b/test/requirements.txt @@ -0,0 +1,33 @@ +# CardioAI - Cardiovascular Disease Intelligent Assistant System +# Python dependencies for the multi-module application + +# Conda Environment Setup Instructions: +# 1. Create a new conda environment named 'cardioenv' with Python 3.10: +# conda create -n cardioenv python=3.10 +# 2. Activate the environment: +# conda activate cardioenv +# 3. Install dependencies from this file: +# pip install -r requirements.txt + +# Core data processing and machine learning +pandas>=2.0.0 +openpyxl>=3.1.0 +numpy>=1.24.0 +scikit-learn>=1.3.0 +xgboost>=2.0.0 +joblib>=1.3.0 + +# Data visualization and dashboard +streamlit>=1.28.0 +plotly>=5.18.0 + +# Web API and prediction server +Flask>=3.0.0 + +# Environment configuration +python-dotenv>=1.0.0 + +# AI and language model integration +langchain-openai>=0.0.5 +dashscope>=1.14.0 +requests>=2.31.0 \ No newline at end of file diff --git a/zzs_test b/zzs_test new file mode 160000 index 0000000..bf30e49 --- /dev/null +++ b/zzs_test @@ -0,0 +1 @@ +Subproject commit bf30e493dd7de56e5164ac7d359c55d8489a6f62 diff --git a/zzs_test4 b/zzs_test4 new file mode 160000 index 0000000..39b398e --- /dev/null +++ b/zzs_test4 @@ -0,0 +1 @@ +Subproject commit 39b398e1599bdff6a47a3bf8c75720f504af760a