diff --git a/.claude/settings.local.json b/.claude/settings.local.json
new file mode 100644
index 0000000..6dd1e44
--- /dev/null
+++ b/.claude/settings.local.json
@@ -0,0 +1,15 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(mkdir:*)",
+ "Bash(cd ..:*)",
+ "Bash(ls:*)",
+ "Bash(echo 语法检查失败:*)",
+ "Bash(head:*)",
+ "Bash(cd module1_dashboard && python -m py_compile cardio_dashboard.py 2>&1)",
+ "Bash(cd:*)",
+ "Bash(python:*)",
+ "Bash(chmod +x:*)"
+ ]
+ }
+}
diff --git a/.env b/.env
new file mode 100644
index 0000000..e69de29
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..de78f21
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..0745301
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..8306744
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/zion_agent.iml b/.idea/zion_agent.iml
new file mode 100644
index 0000000..831f3c0
--- /dev/null
+++ b/.idea/zion_agent.iml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/aicodes/README.md b/aicodes/README.md
new file mode 100644
index 0000000..eba084b
--- /dev/null
+++ b/aicodes/README.md
@@ -0,0 +1,70 @@
+# CardioAI - 心血管疾病智能辅助系统
+
+## 项目概述
+本项目是一个多模块应用,集成了数据可视化(Streamlit)、机器学习预测(XGBoost/Flask)和AI语音问答(DeepSeek/cosyVoice)。
+
+## 项目结构
+```
+F:\Project\PythonProject\zion_agent\
+├── .env # 环境配置文件
+├── data\ # 数据文件目录
+│ └── 心血管疾病.xlsx # 心血管疾病数据集
+├── aicodes\ # 源代码根目录
+│ ├── requirements.txt # 项目依赖包列表
+│ ├── data\ # 项目数据目录
+│ ├── module1_dashboard\ # 模块1: Streamlit数据可视化
+│ ├── module2_predictor\ # 模块2: Flask+XGBoost预测模型
+│ │ └── templates\ # Flask模板文件
+│ └── module3_voice_assistant\ # 模块3: AI语音问答系统
+│ └── templates\ # 语音问答模板文件
+```
+
+## 环境配置
+
+### 1. 创建Conda虚拟环境
+```bash
+conda create -n cardioenv python=3.10
+conda activate cardioenv
+```
+
+### 2. 安装依赖包
+```bash
+cd aicodes
+pip install -r requirements.txt
+```
+
+### 3. 配置环境变量
+在 `.env` 文件中配置必要的API密钥和参数:
+```
+# 示例配置
+OPENAI_API_KEY=your_openai_api_key_here
+DASHSCOPE_API_KEY=your_dashscope_api_key_here
+```
+
+## 各模块说明
+
+### Module 1: 数据可视化看板 (Streamlit)
+- 位置: `module1_dashboard/`
+- 功能: 心血管疾病数据可视化分析
+- 技术栈: Streamlit + Plotly + Pandas
+
+### Module 2: 疾病预测模型 (Flask + XGBoost)
+- 位置: `module2_predictor/`
+- 功能: 心血管疾病风险预测
+- 技术栈: Flask + XGBoost + Scikit-learn
+
+### Module 3: AI语音问答助手
+- 位置: `module3_voice_assistant/`
+- 功能: 基于语音的智能问答系统
+- 技术栈: DeepSeek API + cosyVoice + LangChain
+
+## 下一步操作
+1. 将 `心血管疾病.xlsx` 数据文件放入 `data/` 目录
+2. 根据需要配置 `.env` 文件中的API密钥
+3. 开始开发各个模块的功能
+
+## 开发说明
+- 请严格按照给定的文件路径进行开发
+- 数据文件路径: `F:\Project\PythonProject\zion_agent\data\心血管疾病.xlsx`
+- 配置文件路径: `F:\Project\PythonProject\zion_agent\.env`
+- 代码根目录: `F:\Project\PythonProject\zion_agent\aicodes`
\ No newline at end of file
diff --git a/aicodes/data/心血管疾病.xlsx b/aicodes/data/心血管疾病.xlsx
new file mode 100644
index 0000000..6a4700e
Binary files /dev/null and b/aicodes/data/心血管疾病.xlsx differ
diff --git a/aicodes/module1_dashboard/README.md b/aicodes/module1_dashboard/README.md
new file mode 100644
index 0000000..39691db
--- /dev/null
+++ b/aicodes/module1_dashboard/README.md
@@ -0,0 +1,115 @@
+# Module 1: 心血管疾病数据可视化看板
+
+## 概述
+这是一个基于Streamlit的交互式数据可视化应用程序,用于分析和可视化心血管疾病数据。
+
+## 功能特性
+- 数据加载与清洗
+- 特征工程(年龄转换、BMI计算)
+- 异常值处理(血压数据)
+- 交互式数据筛选
+- 多种可视化图表
+- 数据导出功能
+
+## 文件结构
+```
+module1_dashboard/
+├── cardio_dashboard.py # 主应用程序文件
+└── README.md # 本说明文件
+```
+
+## 运行要求
+
+### 1. 环境配置
+确保已创建并激活conda虚拟环境:
+```bash
+conda create -n cardioenv python=3.10
+conda activate cardioenv
+```
+
+### 2. 安装依赖
+```bash
+cd aicodes
+pip install -r requirements.txt
+```
+
+### 3. 准备数据文件
+将 `心血管疾病.xlsx` 文件放置在以下路径:
+```
+F:\Project\PythonProject\zion_agent\data\心血管疾病.xlsx
+```
+
+如果数据文件不存在,应用程序将使用示例数据进行演示。
+
+## 运行应用程序
+
+### 方式1: 使用Streamlit命令行
+```bash
+cd aicodes/module1_dashboard
+streamlit run cardio_dashboard.py
+```
+
+### 方式2: 使用指定的Python解释器
+```bash
+"C:\Users\Yeraphael\.conda\envs\cardioenv\python.exe" -m streamlit run cardio_dashboard.py
+```
+
+## 应用程序界面
+
+### 侧边栏筛选器
+- **年龄范围滑块**: 筛选指定年龄范围的记录
+- **性别多选框**: 筛选指定性别的记录(1=男性,2=女性)
+- **心血管疾病状态**: 筛选疾病状态(0=无疾病,1=有疾病)
+
+### 主页展示
+- **总记录数**: 筛选后的数据记录总数
+- **心血管疾病风险率**: 心血管疾病患者的比例
+- **平均年龄**: 筛选后数据的平均年龄
+
+### 可视化图表
+1. **年龄分布直方图**: 按心血管疾病状态区分的年龄分布
+2. **BMI类别堆叠柱状图**: BMI类别对心血管疾病的影响
+3. **血压与年龄关系图**: 收缩压和舒张压与年龄的关系
+
+### 数据导出
+可以从侧边栏下载筛选后的数据为CSV文件。
+
+## 数据处理流程
+
+### 1. 数据加载
+- 从Excel文件加载数据
+- 检查文件是否存在
+- 显示原始数据信息
+
+### 2. 特征工程
+- 将age(天)转换为age_years(年)
+- 计算BMI: weight / (height/100)^2
+- 创建BMI类别: 偏瘦、正常、超重、肥胖
+
+### 3. 数据清洗
+- 删除舒张压≥收缩压的记录
+- 删除血压极端异常值:
+ - 收缩压: 90-250 mmHg
+ - 舒张压: 60-150 mmHg
+
+### 4. 类别转换
+- 胆固醇水平: 1=正常, 2=高于正常, 3=显著高于正常
+- 血糖水平: 1=正常, 2=高于正常, 3=显著高于正常
+
+## 故障排除
+
+### 常见问题
+1. **数据文件未找到**: 应用程序将使用示例数据进行演示
+2. **缺少必需列**: 应用程序会跳过相关处理步骤并显示警告
+3. **依赖包缺失**: 确保已安装requirements.txt中的所有包
+
+### 日志查看
+Streamlit应用程序运行时会在终端显示日志信息,包括:
+- 数据加载状态
+- 数据处理步骤
+- 错误和警告信息
+
+## 开发说明
+ 文件路径已硬编码为:`F:\Project\PythonProject\zion_agent\data\心血管疾病.xlsx`
+- 如需更改数据路径,请修改`cardio_dashboard.py`中的`DATA_PATH`常量
+- 所有数据处理函数都使用了`@st.cache_data`装饰器以提高性能
\ No newline at end of file
diff --git a/aicodes/module1_dashboard/__pycache__/cardio_dashboard.cpython-313.pyc b/aicodes/module1_dashboard/__pycache__/cardio_dashboard.cpython-313.pyc
new file mode 100644
index 0000000..e6d0e5f
Binary files /dev/null and b/aicodes/module1_dashboard/__pycache__/cardio_dashboard.cpython-313.pyc differ
diff --git a/aicodes/module1_dashboard/cardio_dashboard.py b/aicodes/module1_dashboard/cardio_dashboard.py
new file mode 100644
index 0000000..8630957
--- /dev/null
+++ b/aicodes/module1_dashboard/cardio_dashboard.py
@@ -0,0 +1,774 @@
+"""
+CardioAI - 心血管疾病数据可视化看板
+Streamlit应用程序,用于对心血管疾病数据进行清洗、特征工程和交互式可视化
+
+数据路径: F:\\Project\\PythonProject\\zion_agent\\data\\心血管疾病.xlsx
+"""
+
+import streamlit as st
+import pandas as pd
+import numpy as np
+import plotly.express as px
+import plotly.graph_objects as go
+import os
+from pathlib import Path
+
+# 设置页面配置
+st.set_page_config(
+ page_title="CardioAI - 心血管疾病智能分析",
+ page_icon="❤️",
+ layout="wide",
+ initial_sidebar_state="expanded"
+)
+
+def standardize_column_names(df):
+ """
+ 标准化数据框列名
+
+ Args:
+ df: 原始数据框
+
+ Returns:
+ 标准化列名后的数据框,列名映射字典
+ """
+ if df is None or len(df) == 0:
+ return df, {}
+
+ df_std = df.copy()
+ column_mapping = {}
+
+ # 常见列名映射(中文到英文)
+ common_mappings = {
+ # 中文列名可能的各种写法
+ '年龄': 'age', '年龄(天)': 'age', '年龄(天)': 'age', 'age': 'age',
+ '性别': 'gender', '性别(1男2女)': 'gender', '性别(1男2女)': 'gender', 'gender': 'gender',
+ '身高': 'height', '身高(cm)': 'height', '身高(cm)': 'height', 'height': 'height',
+ '体重': 'weight', '体重(kg)': 'weight', '体重(kg)': 'weight', 'weight': 'weight',
+ '收缩压': 'ap_hi', '收缩压(mmHg)': 'ap_hi', '收缩压(mmHg)': 'ap_hi', 'ap_hi': 'ap_hi', 'aphi': 'ap_hi',
+ '舒张压': 'ap_lo', '舒张压(mmHg)': 'ap_lo', '舒张压(mmHg)': 'ap_lo', 'ap_lo': 'ap_lo', 'aplo': 'ap_lo',
+ '胆固醇': 'cholesterol', '胆固醇水平': 'cholesterol', 'cholesterol': 'cholesterol',
+ '血糖': 'gluc', '血糖水平': 'gluc', 'gluc': 'gluc',
+ '心血管疾病': 'cardio', '是否患心血管疾病': 'cardio', '心血管疾病(0无1有)': 'cardio', 'cardio': 'cardio',
+ # 英文列名可能的大小写变化
+ 'AGE': 'age', 'Age': 'age',
+ 'GENDER': 'gender', 'Gender': 'gender',
+ 'HEIGHT': 'height', 'Height': 'height',
+ 'WEIGHT': 'weight', 'Weight': 'weight',
+ 'AP_HI': 'ap_hi', 'Ap_hi': 'ap_hi', 'APHI': 'ap_hi',
+ 'AP_LO': 'ap_lo', 'Ap_lo': 'ap_lo', 'APLO': 'ap_lo',
+ 'CHOLESTEROL': 'cholesterol', 'Cholesterol': 'cholesterol',
+ 'GLUC': 'gluc', 'Gluc': 'gluc',
+ 'CARDIO': 'cardio', 'Cardio': 'cardio'
+ }
+
+ # 应用列名映射
+ new_columns = []
+ for col in df_std.columns:
+ col_str = str(col).strip()
+ original_col = col_str # 保存原始列名
+
+ # 检查是否在映射表中
+ if col_str in common_mappings:
+ new_col = common_mappings[col_str]
+ column_mapping[original_col] = new_col
+ new_columns.append(new_col)
+ else:
+ # 尝试小写化
+ col_lower = col_str.lower()
+ if col_lower in common_mappings:
+ # 小写形式在映射表中
+ new_col = common_mappings[col_lower]
+ column_mapping[original_col] = new_col
+ new_columns.append(new_col)
+ elif col_lower in common_mappings.values():
+ # 已经是标准列名的小写形式
+ column_mapping[original_col] = col_lower
+ new_columns.append(col_lower)
+ else:
+ # 保留原列名,但标准化格式
+ # 去除特殊字符,替换为下划线
+ new_col = col_lower
+ for char in [' ', '(', ')', '(', ')', '【', '】', '[', ']', '{', '}', '-', '–', '—']:
+ new_col = new_col.replace(char, '_')
+ # 去除重复下划线
+ while '__' in new_col:
+ new_col = new_col.replace('__', '_')
+ # 去除首尾下划线
+ new_col = new_col.strip('_')
+
+ column_mapping[original_col] = new_col
+ new_columns.append(new_col)
+
+ df_std.columns = new_columns
+ return df_std, column_mapping
+
+# 定义常量
+DATA_PATH = r"F:\Project\PythonProject\zion_agent\aicodes\data\心血管疾病.xlsx"
+
+# 转换字典
+CHOLESTEROL_MAP = {
+ 1: "正常",
+ 2: "高于正常",
+ 3: "显著高于正常"
+}
+
+GLUC_MAP = {
+ 1: "正常",
+ 2: "高于正常",
+ 3: "显著高于正常"
+}
+
+BMI_CATEGORIES = {
+ "underweight": "偏瘦 (<18.5)",
+ "normal": "正常 (18.5-24.9)",
+ "overweight": "超重 (25-29.9)",
+ "obese": "肥胖 (≥30)"
+}
+
+@st.cache_data(ttl=3600)
+def load_and_clean_data(data_path):
+ """
+ 加载和清洗心血管疾病数据
+
+ Args:
+ data_path: Excel数据文件路径
+
+ Returns:
+ pandas.DataFrame: 清洗后的数据框,如果文件不存在则返回None
+ """
+ try:
+ # 检查文件是否存在
+ if not os.path.exists(data_path):
+ st.error(f"数据文件不存在: {data_path}")
+ st.info("请将 '心血管疾病.xlsx' 文件放置在: F:\\Project\\PythonProject\\zion_agent\\data\\")
+ return None
+
+ # 加载数据 - 尝试不同的列名可能性
+ df = pd.read_excel(data_path, engine='openpyxl')
+
+ # 记录原始数据信息
+ original_count = len(df)
+ st.success(f"成功加载数据: {original_count:,} 条记录")
+
+ # 显示列名映射(帮助识别列名)
+ st.info(f"检测到 {len(df.columns)} 个数据列")
+
+ # 保存原始列名(标准化前)
+ original_columns = list(df.columns)
+
+ # 标准化列名
+ df, column_mapping = standardize_column_names(df)
+
+ # 显示原始和清洗后的列名对照
+ with st.expander("🔍 详细数据信息"):
+ st.write(f"**数据形状**: {df.shape}")
+ st.write("**原始列名**:", original_columns)
+ st.write("**标准化后列名**:", list(df.columns))
+ st.write("**列名映射**:", column_mapping)
+
+ # 检查关键列是否存在
+ critical_columns = ['age', 'gender', 'height', 'weight', 'ap_hi', 'ap_lo', 'cholesterol', 'gluc', 'cardio']
+ missing_columns = []
+ for col in critical_columns:
+ if col not in df.columns:
+ missing_columns.append(col)
+
+ if missing_columns:
+ st.warning(f"缺失关键列: {missing_columns}")
+ st.info("尝试在数据中查找类似列名...")
+ # 尝试查找相似的列名
+ for missing_col in missing_columns:
+ possible_matches = [c for c in df.columns if missing_col in str(c).lower()]
+ if possible_matches:
+ st.write(f" '{missing_col}' 可能对应: {possible_matches}")
+
+ # 显示数据类型和前几行数据
+ st.write("**数据类型**:")
+ st.write(df.dtypes)
+
+ st.write("**前5行数据**:")
+ st.dataframe(df.head())
+
+ # 显示基本统计信息
+ st.write("**基本统计信息**:")
+ numeric_cols = df.select_dtypes(include=[np.number]).columns
+ if len(numeric_cols) > 0:
+ st.dataframe(df[numeric_cols].describe().round(2))
+
+ return df
+
+ except Exception as e:
+ st.error(f"加载数据时出错: {str(e)}")
+ st.exception(e) # 显示完整异常信息
+ return None
+
+@st.cache_data(ttl=3600)
+def process_features(df, strict_cleaning=True):
+ """
+ 执行特征工程和数据清洗
+
+ Args:
+ df: 原始数据框
+ strict_cleaning: 是否执行严格的异常值清洗
+
+ Returns:
+ pandas.DataFrame: 处理后的数据框
+ """
+ if df is None or len(df) == 0:
+ return pd.DataFrame()
+
+ # 创建数据副本
+ df_processed = df.copy()
+ original_count = len(df_processed)
+ step_counts = {"原始数据": original_count}
+
+ st.info(f"开始数据处理,原始数据量: {original_count:,} 条记录")
+
+ # 检查缺失值
+ missing_values = df_processed.isnull().sum()
+ total_missing = missing_values.sum()
+ if total_missing > 0:
+ st.warning(f"发现缺失值: 总计 {total_missing:,} 个缺失值")
+ with st.expander("查看缺失值详情"):
+ missing_percentage = (missing_values / len(df_processed) * 100).round(2)
+ missing_df = pd.DataFrame({
+ '缺失数量': missing_values,
+ '缺失百分比': missing_percentage
+ })
+ st.dataframe(missing_df[missing_df['缺失数量'] > 0])
+ else:
+ st.success("没有缺失值")
+
+ # 检查重复记录
+ duplicates = df_processed.duplicated().sum()
+ if duplicates > 0:
+ st.warning(f"发现重复记录: {duplicates:,} 条 ({duplicates/original_count*100:.1f}%)")
+ # 可以选择删除重复记录,但这里仅标记
+ df_processed['is_duplicate'] = df_processed.duplicated()
+ else:
+ st.success("没有重复记录")
+
+ # 1. 将age转换为年(自动检测单位)
+ if 'age' in df_processed.columns:
+ # 检查age列的值范围
+ age_min = df_processed['age'].min()
+ age_max = df_processed['age'].max()
+ age_mean = df_processed['age'].mean()
+
+ st.write(f"age列统计: 最小值={age_min:.1f}, 最大值={age_max:.1f}, 平均值={age_mean:.1f}")
+
+ # 自动检测单位:如果值看起来像年(0-150),则直接使用;如果看起来像天(>100),则转换
+ if age_max > 150 and age_min > 0:
+ # 看起来像天为单位
+ st.info(f"检测到年龄数据以天为单位(最大值={age_max:.0f}),转换为年")
+ df_processed['age_years'] = (df_processed['age'] / 365.25).round().astype(int)
+ age_years_min = df_processed['age_years'].min()
+ age_years_max = df_processed['age_years'].max()
+ st.write(f"转换后年龄范围: {age_years_min} 到 {age_years_max} 岁")
+ elif age_max <= 150 and age_min >= 0:
+ # 看起来已经是年为单位
+ st.info(f"检测到年龄数据以年为单位(最大值={age_max:.0f}),直接使用")
+ df_processed['age_years'] = df_processed['age'].round().astype(int)
+ age_years_min = df_processed['age_years'].min()
+ age_years_max = df_processed['age_years'].max()
+ st.write(f"年龄范围: {age_years_min} 到 {age_years_max} 岁")
+ else:
+ # 异常范围,尝试按天处理
+ st.warning(f"年龄数据范围异常({age_min:.0f} 到 {age_max:.0f}),尝试按天处理")
+ df_processed['age_years'] = (df_processed['age'] / 365.25).round().astype(int)
+ age_years_min = df_processed['age_years'].min()
+ age_years_max = df_processed['age_years'].max()
+ st.write(f"转换后年龄范围: {age_years_min} 到 {age_years_max} 岁")
+ else:
+ st.warning("数据中未找到 'age' 列")
+ # 尝试查找可能的年龄列
+ age_like_cols = [col for col in df_processed.columns if 'age' in str(col).lower()]
+ if age_like_cols:
+ st.info(f"找到可能的年龄列: {age_like_cols}")
+ df_processed['age_years'] = 0
+
+ # 2. 计算BMI (体重kg / (身高cm/100)^2)
+ if all(col in df_processed.columns for col in ['weight', 'height']):
+ # 检查身高体重范围
+ height_min = df_processed['height'].min()
+ height_max = df_processed['height'].max()
+ weight_min = df_processed['weight'].min()
+ weight_max = df_processed['weight'].max()
+
+ st.write(f"身高范围: {height_min:.1f} 到 {height_max:.1f} cm")
+ st.write(f"体重范围: {weight_min:.1f} 到 {weight_max:.1f} kg")
+
+ # 检查单位是否合理
+ if height_max > 300:
+ st.warning(f"身高最大值 {height_max:.1f} cm 可能不是厘米单位")
+
+ df_processed['bmi'] = df_processed['weight'] / ((df_processed['height'] / 100) ** 2)
+ df_processed['bmi'] = df_processed['bmi'].round(2)
+
+ # 创建BMI类别
+ conditions = [
+ df_processed['bmi'] < 18.5,
+ (df_processed['bmi'] >= 18.5) & (df_processed['bmi'] < 25),
+ (df_processed['bmi'] >= 25) & (df_processed['bmi'] < 30),
+ df_processed['bmi'] >= 30
+ ]
+ choices = ['underweight', 'normal', 'overweight', 'obese']
+ df_processed['bmi_category'] = np.select(conditions, choices, default='unknown')
+
+ # 统计BMI类别分布
+ bmi_dist = df_processed['bmi_category'].value_counts()
+ st.write("BMI类别分布:", bmi_dist.to_dict())
+ else:
+ st.warning("数据中未找到 'weight' 或 'height' 列,无法计算BMI")
+ df_processed['bmi'] = 0
+ df_processed['bmi_category'] = 'unknown'
+
+ # 3. 异常值处理 - 血压数据
+ if all(col in df_processed.columns for col in ['ap_hi', 'ap_lo']):
+ # 检查血压范围和单位
+ ap_hi_min = df_processed['ap_hi'].min()
+ ap_hi_max = df_processed['ap_hi'].max()
+ ap_lo_min = df_processed['ap_lo'].min()
+ ap_lo_max = df_processed['ap_lo'].max()
+
+ # 自动检测单位:如果值看起来像kPa(正常范围12-33),则转换为mmHg
+ if ap_hi_max < 50 and ap_lo_max < 50:
+ # 看起来像kPa单位(正常血压范围12-22 kPa)
+ st.warning(f"检测到血压数据可能以kPa为单位(收缩压范围: {ap_hi_min:.1f} 到 {ap_hi_max:.1f})")
+ st.info("正在将血压数据从kPa转换为mmHg(乘以7.5)")
+
+ # 转换数据
+ df_processed['ap_hi'] = df_processed['ap_hi'] * 7.5
+ df_processed['ap_lo'] = df_processed['ap_lo'] * 7.5
+
+ # 更新范围
+ ap_hi_min = df_processed['ap_hi'].min()
+ ap_hi_max = df_processed['ap_hi'].max()
+ ap_lo_min = df_processed['ap_lo'].min()
+ ap_lo_max = df_processed['ap_lo'].max()
+
+ st.write(f"转换后收缩压范围: {ap_hi_min:.0f} 到 {ap_hi_max:.0f} mmHg")
+ st.write(f"转换后舒张压范围: {ap_lo_min:.0f} 到 {ap_lo_max:.0f} mmHg")
+ else:
+ # 看起来像mmHg单位
+ st.write(f"收缩压范围: {ap_hi_min:.0f} 到 {ap_hi_max:.0f} mmHg")
+ st.write(f"舒张压范围: {ap_lo_min:.0f} 到 {ap_lo_max:.0f} mmHg")
+
+ # 检查是否可能已经是转换后的值
+ if ap_hi_max > 300 or ap_lo_max > 200:
+ st.warning(f"血压值异常高(收缩压最大={ap_hi_max:.0f} mmHg),请检查数据单位")
+
+ # 统计异常血压记录
+ diastolic_ge_systolic = (df_processed['ap_lo'] >= df_processed['ap_hi']).sum()
+ systolic_out_of_range = ((df_processed['ap_hi'] < 90) | (df_processed['ap_hi'] > 250)).sum()
+ diastolic_out_of_range = ((df_processed['ap_lo'] < 60) | (df_processed['ap_lo'] > 150)).sum()
+
+ st.write(f"舒张压≥收缩压的记录: {diastolic_ge_systolic:,} 条 ({diastolic_ge_systolic/original_count*100:.1f}%)")
+ st.write(f"收缩压异常记录: {systolic_out_of_range:,} 条 ({systolic_out_of_range/original_count*100:.1f}%)")
+ st.write(f"舒张压异常记录: {diastolic_out_of_range:,} 条 ({diastolic_out_of_range/original_count*100:.1f}%)")
+
+ if strict_cleaning:
+ # 严格清洗:删除异常记录
+ # 删除舒张压 >= 收缩压的记录
+ initial_count = len(df_processed)
+ df_processed = df_processed[df_processed['ap_lo'] < df_processed['ap_hi']]
+ removed_count = initial_count - len(df_processed)
+ if removed_count > 0:
+ st.info(f"严格清洗: 删除了 {removed_count:,} 条舒张压≥收缩压的记录")
+ step_counts["删除舒张压≥收缩压"] = len(df_processed)
+
+ # 删除血压极端异常值
+ initial_count = len(df_processed)
+ df_processed = df_processed[
+ (df_processed['ap_hi'] >= 90) & (df_processed['ap_hi'] <= 250) &
+ (df_processed['ap_lo'] >= 60) & (df_processed['ap_lo'] <= 150)
+ ]
+ removed_count = initial_count - len(df_processed)
+ if removed_count > 0:
+ st.info(f"严格清洗: 删除了 {removed_count:,} 条血压异常值记录")
+ step_counts["删除血压异常值"] = len(df_processed)
+ else:
+ # 宽松清洗:只标记不删除
+ st.info("宽松清洗模式: 保留所有血压记录,仅标记异常")
+ df_processed['bp_anomaly'] = np.where(
+ (df_processed['ap_lo'] >= df_processed['ap_hi']) |
+ (df_processed['ap_hi'] < 90) | (df_processed['ap_hi'] > 250) |
+ (df_processed['ap_lo'] < 60) | (df_processed['ap_lo'] > 150),
+ '异常', '正常'
+ )
+ anomaly_count = (df_processed['bp_anomaly'] == '异常').sum()
+ st.write(f"血压异常标记: {anomaly_count:,} 条记录 ({anomaly_count/len(df_processed)*100:.1f}%)")
+ else:
+ st.warning("数据中未找到 'ap_hi' 或 'ap_lo' 列,跳过血压异常值处理")
+ # 尝试查找可能的血压列
+ bp_cols = [col for col in df_processed.columns if 'bp' in str(col).lower() or 'pressure' in str(col).lower()]
+ if bp_cols:
+ st.info(f"找到可能的血压列: {bp_cols}")
+
+ # 4. 类别转换
+ if 'cholesterol' in df_processed.columns:
+ # 检查胆固醇值范围
+ cholesterol_values = df_processed['cholesterol'].unique()
+ st.write(f"胆固醇唯一值: {sorted(cholesterol_values)}")
+ df_processed['cholesterol_str'] = df_processed['cholesterol'].map(CHOLESTEROL_MAP)
+ df_processed['cholesterol_str'] = df_processed['cholesterol_str'].fillna("未知")
+ else:
+ df_processed['cholesterol_str'] = "未知"
+
+ if 'gluc' in df_processed.columns:
+ # 检查血糖值范围
+ gluc_values = df_processed['gluc'].unique()
+ st.write(f"血糖唯一值: {sorted(gluc_values)}")
+ df_processed['gluc_str'] = df_processed['gluc'].map(GLUC_MAP)
+ df_processed['gluc_str'] = df_processed['gluc_str'].fillna("未知")
+ else:
+ df_processed['gluc_str'] = "未知"
+
+ # 5. BMI类别描述转换
+ df_processed['bmi_category_str'] = df_processed['bmi_category'].map(BMI_CATEGORIES)
+ df_processed['bmi_category_str'] = df_processed['bmi_category_str'].fillna("未知")
+
+ # 最终统计
+ final_count = len(df_processed)
+ removed_total = original_count - final_count
+ retention_rate = final_count / original_count * 100 if original_count > 0 else 0
+
+ st.success(f"数据处理完成:")
+ st.write(f"- 原始记录数: {original_count:,}")
+ st.write(f"- 处理后记录数: {final_count:,}")
+ st.write(f"- 删除记录数: {removed_total:,} ({removed_total/original_count*100:.1f}%)")
+ st.write(f"- 数据保留率: {retention_rate:.1f}%")
+
+ # 显示各步骤计数
+ with st.expander("查看详细处理步骤"):
+ for step, count in step_counts.items():
+ st.write(f"{step}: {count:,} 条记录")
+
+ return df_processed
+
+def create_visualizations(df_filtered):
+ """
+ 创建数据可视化图表
+
+ Args:
+ df_filtered: 筛选后的数据框
+ """
+ if df_filtered.empty:
+ st.warning("没有可用的数据用于可视化")
+ return
+
+ col1, col2 = st.columns(2)
+
+ with col1:
+ # 年龄分布直方图(按cardio区分)
+ if 'age_years' in df_filtered.columns and 'cardio' in df_filtered.columns:
+ fig1 = px.histogram(
+ df_filtered,
+ x='age_years',
+ color='cardio',
+ nbins=30,
+ title="年龄分布直方图(按心血管疾病状态)",
+ labels={'age_years': '年龄(岁)', 'cardio': '心血管疾病', 'count': '人数'},
+ color_discrete_map={0: 'blue', 1: 'red'},
+ barmode='overlay',
+ opacity=0.7
+ )
+ fig1.update_layout(
+ xaxis_title="年龄(岁)",
+ yaxis_title="人数",
+ legend_title="心血管疾病",
+ hovermode="x unified"
+ )
+ st.plotly_chart(fig1, use_container_width=True)
+ else:
+ st.info("缺少年龄或心血管疾病数据,无法生成年龄分布图")
+
+ with col2:
+ # BMI类别对心血管疾病影响的堆叠柱状图
+ if all(col in df_filtered.columns for col in ['bmi_category_str', 'cardio']):
+ # 创建交叉表
+ cross_tab = pd.crosstab(
+ df_filtered['bmi_category_str'],
+ df_filtered['cardio'],
+ normalize='index'
+ ).round(4) * 100
+
+ # 重命名列
+ cross_tab = cross_tab.rename(columns={0: '无疾病', 1: '有疾病'})
+
+ fig2 = px.bar(
+ cross_tab,
+ x=cross_tab.index,
+ y=['无疾病', '有疾病'],
+ title="BMI类别对心血管疾病影响的堆叠柱状图",
+ labels={'value': '百分比 (%)', 'variable': '疾病状态', 'index': 'BMI类别'},
+ barmode='stack'
+ )
+ fig2.update_layout(
+ xaxis_title="BMI类别",
+ yaxis_title="百分比 (%)",
+ legend_title="疾病状态"
+ )
+ st.plotly_chart(fig2, use_container_width=True)
+ else:
+ st.info("缺少BMI类别或心血管疾病数据,无法生成BMI影响图")
+
+ # 额外图表:血压与年龄的关系
+ st.subheader("血压与年龄的关系")
+ if all(col in df_filtered.columns for col in ['age_years', 'ap_hi', 'ap_lo', 'cardio']):
+ col3, col4 = st.columns(2)
+
+ with col3:
+ fig3 = px.scatter(
+ df_filtered,
+ x='age_years',
+ y='ap_hi',
+ color='cardio',
+ title="收缩压与年龄的关系",
+ labels={'age_years': '年龄(岁)', 'ap_hi': '收缩压 (mmHg)', 'cardio': '心血管疾病'},
+ color_discrete_map={0: 'blue', 1: 'red'},
+ opacity=0.6
+ )
+ fig3.update_layout(
+ xaxis_title="年龄(岁)",
+ yaxis_title="收缩压 (mmHg)",
+ legend_title="心血管疾病"
+ )
+ st.plotly_chart(fig3, use_container_width=True)
+
+ with col4:
+ fig4 = px.scatter(
+ df_filtered,
+ x='age_years',
+ y='ap_lo',
+ color='cardio',
+ title="舒张压与年龄的关系",
+ labels={'age_years': '年龄(岁)', 'ap_lo': '舒张压 (mmHg)', 'cardio': '心血管疾病'},
+ color_discrete_map={0: 'blue', 1: 'red'},
+ opacity=0.6
+ )
+ fig4.update_layout(
+ xaxis_title="年龄(岁)",
+ yaxis_title="舒张压 (mmHg)",
+ legend_title="心血管疾病"
+ )
+ st.plotly_chart(fig4, use_container_width=True)
+
+def main():
+ """主函数"""
+ # 页面标题
+ st.title("❤️ CardioAI - 心血管疾病智能分析系统")
+ st.markdown("---")
+
+ # 加载数据
+ with st.spinner("正在加载数据..."):
+ df_raw = load_and_clean_data(DATA_PATH)
+
+ if df_raw is None:
+ # 显示示例数据用于演示
+ st.warning("使用示例数据进行演示(实际运行时请放置数据文件)")
+
+ # 创建示例数据
+ np.random.seed(42)
+ n_samples = 1000
+ example_data = {
+ 'age': np.random.randint(365*20, 365*80, n_samples), # 20-80岁
+ 'gender': np.random.choice([1, 2], n_samples),
+ 'height': np.random.normal(170, 10, n_samples).astype(int),
+ 'weight': np.random.normal(70, 15, n_samples).astype(int),
+ 'ap_hi': np.random.normal(120, 20, n_samples).astype(int),
+ 'ap_lo': np.random.normal(80, 15, n_samples).astype(int),
+ 'cholesterol': np.random.choice([1, 2, 3], n_samples, p=[0.7, 0.2, 0.1]),
+ 'gluc': np.random.choice([1, 2, 3], n_samples, p=[0.8, 0.15, 0.05]),
+ 'cardio': np.random.choice([0, 1], n_samples, p=[0.7, 0.3])
+ }
+ df_raw = pd.DataFrame(example_data)
+ st.info("正在使用示例数据进行演示")
+
+ # 数据处理选项
+ st.sidebar.header("⚙️ 数据处理选项")
+
+ # 清洗模式选择
+ cleaning_mode = st.sidebar.radio(
+ "选择数据清洗模式",
+ options=["宽松模式(保留所有数据)", "严格模式(删除异常值)"],
+ index=1, # 默认严格模式
+ help="宽松模式:标记异常但不删除;严格模式:删除血压异常值"
+ )
+ strict_cleaning = cleaning_mode == "严格模式(删除异常值)"
+
+ if strict_cleaning:
+ st.sidebar.info("严格模式:将删除血压异常记录")
+ else:
+ st.sidebar.info("宽松模式:保留所有数据,仅标记异常")
+
+ # 特征工程
+ with st.spinner("正在进行特征工程和数据清洗..."):
+ df_processed = process_features(df_raw, strict_cleaning=strict_cleaning)
+
+ if df_processed.empty:
+ st.error("数据处理后没有可用的记录")
+ return
+
+ # 侧边栏筛选器
+ st.sidebar.header("🔍 数据筛选器")
+
+ # 年龄范围筛选
+ if 'age_years' in df_processed.columns:
+ age_min = int(df_processed['age_years'].min())
+ age_max = int(df_processed['age_years'].max())
+ age_range = st.sidebar.slider(
+ "选择年龄范围",
+ min_value=age_min,
+ max_value=age_max,
+ value=(age_min, age_max),
+ help="筛选指定年龄范围的记录"
+ )
+ else:
+ age_range = (0, 100)
+
+ # 性别筛选
+ if 'gender' in df_processed.columns:
+ gender_options = sorted(df_processed['gender'].unique())
+ selected_genders = st.sidebar.multiselect(
+ "选择性别",
+ options=gender_options,
+ default=gender_options,
+ format_func=lambda x: "男性" if x == 1 else "女性" if x == 2 else f"性别{x}",
+ help="筛选指定性别的记录"
+ )
+ else:
+ selected_genders = []
+
+ # 心血管疾病状态筛选
+ if 'cardio' in df_processed.columns:
+ cardio_options = sorted(df_processed['cardio'].unique())
+ selected_cardio = st.sidebar.multiselect(
+ "选择心血管疾病状态",
+ options=cardio_options,
+ default=cardio_options,
+ format_func=lambda x: "无疾病" if x == 0 else "有疾病" if x == 1 else f"状态{x}",
+ help="筛选指定疾病状态的记录"
+ )
+ else:
+ selected_cardio = []
+
+ # 应用筛选
+ df_filtered = df_processed.copy()
+
+ # 年龄筛选
+ if 'age_years' in df_filtered.columns:
+ df_filtered = df_filtered[
+ (df_filtered['age_years'] >= age_range[0]) &
+ (df_filtered['age_years'] <= age_range[1])
+ ]
+
+ # 性别筛选
+ if selected_genders and 'gender' in df_filtered.columns:
+ df_filtered = df_filtered[df_filtered['gender'].isin(selected_genders)]
+
+ # 疾病状态筛选
+ if selected_cardio and 'cardio' in df_filtered.columns:
+ df_filtered = df_filtered[df_filtered['cardio'].isin(selected_cardio)]
+
+ # 主页展示
+ st.header("📊 数据概览")
+
+ col1, col2, col3 = st.columns(3)
+
+ with col1:
+ st.metric(
+ label="总记录数",
+ value=f"{len(df_filtered):,}",
+ help="筛选后的数据记录总数"
+ )
+
+ with col2:
+ if 'cardio' in df_filtered.columns:
+ cardio_rate = df_filtered['cardio'].mean() * 100
+ st.metric(
+ label="心血管疾病风险率",
+ value=f"{cardio_rate:.1f}%",
+ help="心血管疾病患者的比例"
+ )
+ else:
+ st.metric("心血管疾病风险率", "N/A")
+
+ with col3:
+ if 'age_years' in df_filtered.columns:
+ avg_age = df_filtered['age_years'].mean()
+ st.metric(
+ label="平均年龄",
+ value=f"{avg_age:.1f} 岁",
+ help="筛选后数据的平均年龄"
+ )
+ else:
+ st.metric("平均年龄", "N/A")
+
+ # 显示筛选后的数据摘要
+ with st.expander("📋 查看筛选后数据摘要"):
+ st.write(f"数据形状: {df_filtered.shape}")
+
+ # 显示关键统计信息
+ if not df_filtered.empty:
+ st.write("### 关键统计信息")
+
+ stats_col1, stats_col2, stats_col3 = st.columns(3)
+
+ with stats_col1:
+ if 'age_years' in df_filtered.columns:
+ st.write("**年龄统计**")
+ st.write(f"- 最小值: {df_filtered['age_years'].min()} 岁")
+ st.write(f"- 最大值: {df_filtered['age_years'].max()} 岁")
+ st.write(f"- 平均值: {df_filtered['age_years'].mean():.1f} 岁")
+
+ with stats_col2:
+ if 'bmi' in df_filtered.columns:
+ st.write("**BMI统计**")
+ st.write(f"- 最小值: {df_filtered['bmi'].min():.1f}")
+ st.write(f"- 最大值: {df_filtered['bmi'].max():.1f}")
+ st.write(f"- 平均值: {df_filtered['bmi'].mean():.1f}")
+
+ with stats_col3:
+ if all(col in df_filtered.columns for col in ['ap_hi', 'ap_lo']):
+ st.write("**血压统计**")
+ st.write(f"- 平均收缩压: {df_filtered['ap_hi'].mean():.1f} mmHg")
+ st.write(f"- 平均舒张压: {df_filtered['ap_lo'].mean():.1f} mmHg")
+
+ # 显示前10行数据
+ st.write("### 前10行数据")
+ st.dataframe(df_filtered.head(10))
+
+ # 数据可视化
+ st.header("📈 数据可视化")
+ create_visualizations(df_filtered)
+
+ # 数据下载
+ st.sidebar.markdown("---")
+ st.sidebar.header("📥 数据导出")
+
+ if not df_filtered.empty:
+ # 转换为CSV
+ csv_data = df_filtered.to_csv(index=False).encode('utf-8')
+
+ st.sidebar.download_button(
+ label="下载筛选后数据 (CSV)",
+ data=csv_data,
+ file_name="cardio_filtered_data.csv",
+ mime="text/csv",
+ help="下载当前筛选后的数据"
+ )
+
+ # 页脚
+ st.markdown("---")
+ st.markdown(
+ """
+
+
CardioAI - 心血管疾病智能辅助系统 | 版本 1.0 | 使用Streamlit构建
+
+ """,
+ unsafe_allow_html=True
+ )
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/aicodes/module1_dashboard/run_dashboard.bat b/aicodes/module1_dashboard/run_dashboard.bat
new file mode 100644
index 0000000..06ddaa0
--- /dev/null
+++ b/aicodes/module1_dashboard/run_dashboard.bat
@@ -0,0 +1,55 @@
+@echo off
+REM CardioAI Streamlit 仪表板启动脚本
+echo ================================================
+echo CardioAI - 心血管疾病数据可视化看板
+echo ================================================
+
+REM 检查是否在conda环境中
+where conda >nul 2>nul
+if %ERRORLEVEL% equ 0 (
+ echo 检查Conda环境...
+ conda info --envs | findstr cardioenv >nul
+ if %ERRORLEVEL% equ 0 (
+ echo 激活cardioenv环境...
+ call conda activate cardioenv
+ ) else (
+ echo 警告: 未找到cardioenv环境
+ echo 请先创建环境: conda create -n cardioenv python=3.10
+ pause
+ exit /b 1
+ )
+)
+
+REM 检查Python解释器
+echo 检查Python解释器...
+python --version
+if %ERRORLEVEL% neq 0 (
+ echo 错误: 未找到Python
+ pause
+ exit /b 1
+)
+
+REM 检查依赖包
+echo 检查依赖包...
+python -c "import streamlit, pandas, plotly, numpy, openpyxl" 2>nul
+if %ERRORLEVEL% neq 0 (
+ echo 错误: 缺少必要的依赖包
+ echo 请运行: pip install -r requirements.txt
+ pause
+ exit /b 1
+)
+
+REM 启动Streamlit应用程序
+echo 启动Streamlit应用程序...
+echo 应用程序将在浏览器中打开...
+echo 按Ctrl+C停止服务器
+echo.
+
+streamlit run cardio_dashboard.py
+
+if %ERRORLEVEL% neq 0 (
+ echo.
+ echo 启动失败,请检查错误信息
+ pause
+ exit /b 1
+)
\ No newline at end of file
diff --git a/aicodes/module1_dashboard/run_dashboard.sh b/aicodes/module1_dashboard/run_dashboard.sh
new file mode 100644
index 0000000..e19adb4
--- /dev/null
+++ b/aicodes/module1_dashboard/run_dashboard.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+# CardioAI Streamlit 仪表板启动脚本
+
+echo "================================================"
+echo "CardioAI - 心血管疾病数据可视化看板"
+echo "================================================"
+
+# 检查是否在conda环境中
+if command -v conda &> /dev/null; then
+ echo "检查Conda环境..."
+ if conda info --envs | grep -q "cardioenv"; then
+ echo "激活cardioenv环境..."
+ conda activate cardioenv
+ else
+ echo "警告: 未找到cardioenv环境"
+ echo "请先创建环境: conda create -n cardioenv python=3.10"
+ read -p "按Enter键继续..."
+ exit 1
+ fi
+fi
+
+# 检查Python解释器
+echo "检查Python解释器..."
+python --version
+if [ $? -ne 0 ]; then
+ echo "错误: 未找到Python"
+ read -p "按Enter键继续..."
+ exit 1
+fi
+
+# 检查依赖包
+echo "检查依赖包..."
+python -c "import streamlit, pandas, plotly, numpy, openpyxl" 2>/dev/null
+if [ $? -ne 0 ]; then
+ echo "错误: 缺少必要的依赖包"
+ echo "请运行: pip install -r requirements.txt"
+ read -p "按Enter键继续..."
+ exit 1
+fi
+
+# 启动Streamlit应用程序
+echo "启动Streamlit应用程序..."
+echo "应用程序将在浏览器中打开..."
+echo "按Ctrl+C停止服务器"
+echo ""
+
+streamlit run cardio_dashboard.py
+
+if [ $? -ne 0 ]; then
+ echo ""
+ echo "启动失败,请检查错误信息"
+ read -p "按Enter键继续..."
+ exit 1
+fi
\ No newline at end of file
diff --git a/aicodes/module1_dashboard/test_import.py b/aicodes/module1_dashboard/test_import.py
new file mode 100644
index 0000000..7443b59
--- /dev/null
+++ b/aicodes/module1_dashboard/test_import.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python
+"""
+测试脚本 - 验证cardio_dashboard.py的导入和基本功能
+"""
+
+import sys
+import os
+
+# 添加当前目录到Python路径
+sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+
+try:
+ # 测试导入必要的库
+ print("测试导入必要的库...")
+ import pandas as pd
+ import numpy as np
+ import streamlit as st
+ import plotly.express as px
+ print("[OK] 所有必要的库导入成功")
+
+ # 测试导入应用程序模块
+ print("\n测试导入cardio_dashboard模块...")
+ # 由于cardio_dashboard.py使用了Streamlit命令,我们需要模拟环境
+ # 这里只测试语法和导入
+
+ # 读取文件内容检查基本语法
+ with open('cardio_dashboard.py', 'r', encoding='utf-8') as f:
+ content = f.read()
+
+ # 检查关键函数是否存在
+ required_functions = ['load_and_clean_data', 'process_features', 'create_visualizations', 'main']
+ missing_functions = []
+
+ for func in required_functions:
+ if f'def {func}' not in content:
+ missing_functions.append(func)
+
+ if missing_functions:
+ print(f"✗ 缺少函数: {missing_functions}")
+ else:
+ print("[OK] 所有必需函数都存在")
+
+ # 检查装饰器
+ if '@st.cache_data' in content:
+ print("[OK] 使用了@st.cache_data装饰器")
+ else:
+ print("[ERROR] 未找到@st.cache_data装饰器")
+
+ # 检查数据路径
+ if r'F:\\Project\\PythonProject\\zion_agent\\data\\心血管疾病.xlsx' in content:
+ print("[OK] 数据路径正确设置")
+ else:
+ print("[ERROR] 数据路径未正确设置")
+
+ print("\n基本语法检查完成!")
+
+except ImportError as e:
+ print(f"[ERROR] 导入错误: {e}")
+ print("请确保已安装requirements.txt中的所有依赖包")
+ sys.exit(1)
+except Exception as e:
+ print(f"[ERROR] 错误: {e}")
+ sys.exit(1)
+
+print("\n" + "="*50)
+print("测试完成! 应用程序可以通过以下命令运行:")
+print("streamlit run cardio_dashboard.py")
+print("="*50)
\ No newline at end of file
diff --git a/aicodes/requirements.txt b/aicodes/requirements.txt
new file mode 100644
index 0000000..70bf01a
--- /dev/null
+++ b/aicodes/requirements.txt
@@ -0,0 +1,20 @@
+
+# 核心数据科学库
+pandas>=2.0.0
+openpyxl>=3.1.0
+numpy>=1.24.0
+scikit-learn>=1.3.0
+xgboost>=1.7.0
+joblib>=1.3.0
+
+# 可视化与Web框架
+streamlit>=1.28.0
+plotly>=5.18.0
+Flask>=2.3.0
+
+# 环境配置与API
+python-dotenv>=1.0.0
+langchain-openai>=0.0.5
+dashscope>=1.14.0
+requests>=2.31.0
+