""" 建议模型服务 高精确率模型 + DiCE反事实解释,生成个性化建议 """ import logging import time import sys import os import pandas as pd import numpy as np from typing import Dict, Any, List, Optional from pathlib import Path # 添加DiCE模块路径 dice_path_I = Path("/Users/ning/Desktop/idea/代码forSarcoAdvisor/4.DICE建模/DICE/I") dice_path_II = Path("/Users/ning/Desktop/idea/代码forSarcoAdvisor/4.DICE建模/DICE/II") sys.path.append(str(dice_path_I)) sys.path.append(str(dice_path_II)) from schemas.user_input import UserInput, AdvisoryResponse, RecommendationItem from utils.model_loader import model_manager from config import DiCEConfig, SystemConfig # 尝试导入DiCE相关模块 try: import dice_ml from dice_ml import Dice DICE_AVAILABLE = True except ImportError: DICE_AVAILABLE = False logging.warning("DiCE module not available, will use rule-based recommendations") logger = logging.getLogger(__name__) class AdvisoryService: """建议服务类""" def __init__(self): self.model_manager = model_manager self.dice_explainers = {} # DiCE初始化将在模型加载后进行 def initialize_dice(self): """在模型加载后初始化DiCE解释器""" self._init_dice_explainers() def _init_dice_explainers(self): """初始化DiCE解释器""" if not DICE_AVAILABLE: logger.warning("DiCE不可用,将使用基于规则的建议") return try: # 加载DiCE数据和解释器 self._setup_sarcoI_dice() self._setup_sarcoII_dice() logger.info("DiCE解释器初始化完成") except Exception as e: logger.error(f"DiCE解释器初始化失败: {str(e)}") self.dice_explainers = {} def _setup_sarcoI_dice(self): """设置SarcoI DiCE解释器""" try: # 动态确定数据路径 app_path = Path(__file__).parent.parent if (app_path / "data").exists(): # 云端部署路径 data_path = app_path / "data" else: # 本地开发路径 data_path = Path('/Users/ning/Desktop/idea/代码forSarcoAdvisor/4.DICE建模/预筛选') # 🚀 根据配置加载SarcoI数据集 config = DiCEConfig.get_current_config() train_data = pd.read_csv(data_path / 'SarcoI_train_final.csv') # 根据配置决定是否合并测试集 if config["combine_train_test"]: try: test_data = pd.read_csv(data_path / 'SarcoI_test_final.csv') all_data = pd.concat([train_data, test_data], ignore_index=True) logger.info(f"SarcoI DiCE合并训练+测试数据: {len(all_data)} 样本") except FileNotFoundError: all_data = train_data logger.info("SarcoI测试集未找到,仅使用训练集") else: all_data = train_data logger.info(f"SarcoI DiCE使用训练数据: {len(all_data)} 样本") # 根据配置决定是否采样 if config["max_samples"] and len(all_data) > config["max_samples"]: all_data = all_data.sample(n=config["max_samples"], random_state=42) logger.info(f"SarcoI DiCE数据采样至: {len(all_data)} 样本") logger.info(f"SarcoI DiCE最终数据集: {len(all_data)} 样本 (模式: {DiCEConfig.PRECISION_MODE})") # 创建DiCE数据对象 dice_data = dice_ml.Data( dataframe=all_data, continuous_features=['body_mass_index', 'race_ethnicity', 'WWI', 'age_years', 'Activity_Sedentary_Ratio', 'Total_Moderate_Minutes_week', 'Vigorous_MET_Ratio'], outcome_name='SarcoI' ) # 包装模型 wrapped_model = self._wrap_model('sarcoI') dice_model = dice_ml.Model(model=wrapped_model, backend='sklearn') # 🚀 创建极致精度解释器 - 使用genetic方法 + 配置文件中的高精度参数 dice_config_params = DiCEConfig.get_current_config() logger.info(f"SarcoI DiCE使用精度模式: {DiCEConfig.PRECISION_MODE}") # genetic方法是DiCE中最精确但最慢的方法,完美符合您的需求 self.dice_explainers['sarcoI'] = { 'explainer': Dice(dice_data, dice_model, method='genetic'), 'mutable_features': ['Activity_Sedentary_Ratio', 'Total_Moderate_Minutes_week', 'Vigorous_MET_Ratio', 'body_mass_index', 'WWI'], # 🎯 使用配置文件中的高精度参数 'precision_config': dice_config_params } except Exception as e: logger.error(f"SarcoI DiCE设置失败: {str(e)}") def _setup_sarcoII_dice(self): """设置SarcoII DiCE解释器""" try: # 动态确定数据路径 app_path = Path(__file__).parent.parent if (app_path / "data").exists(): # 云端部署路径 data_path = app_path / "data" else: # 本地开发路径 data_path = Path('/Users/ning/Desktop/idea/代码forSarcoAdvisor/4.DICE建模/预筛选') # 🚀 根据配置加载SarcoII数据集 config = DiCEConfig.get_current_config() train_data = pd.read_csv(data_path / 'SarcoII_train_final.csv') # 根据配置决定是否合并测试集 if config["combine_train_test"]: try: test_data = pd.read_csv(data_path / 'SarcoII_test_final.csv') all_data = pd.concat([train_data, test_data], ignore_index=True) logger.info(f"SarcoII DiCE合并训练+测试数据: {len(all_data)} 样本") except FileNotFoundError: all_data = train_data logger.info("SarcoII测试集未找到,仅使用训练集") else: all_data = train_data logger.info(f"SarcoII DiCE使用训练数据: {len(all_data)} 样本") # 根据配置决定是否采样 if config["max_samples"] and len(all_data) > config["max_samples"]: all_data = all_data.sample(n=config["max_samples"], random_state=42) logger.info(f"SarcoII DiCE数据采样至: {len(all_data)} 样本") logger.info(f"SarcoII DiCE最终数据集: {len(all_data)} 样本 (模式: {DiCEConfig.PRECISION_MODE})") # 创建DiCE数据对象 dice_data = dice_ml.Data( dataframe=all_data, continuous_features=['body_mass_index', 'race_ethnicity', 'age_years', 'Activity_Sedentary_Ratio', 'Activity_Diversity_Index', 'WWI', 'Vigorous_MET_Ratio', 'sedentary_minutes'], outcome_name='SarcoII' ) # 包装模型 wrapped_model = self._wrap_model('sarcoII') dice_model = dice_ml.Model(model=wrapped_model, backend='sklearn') # 🚀 创建极致精度解释器 - 使用genetic方法 + 配置文件中的高精度参数 dice_config_params = DiCEConfig.get_current_config() logger.info(f"SarcoII DiCE使用精度模式: {DiCEConfig.PRECISION_MODE}") # genetic方法是DiCE中最精确但最慢的方法,完美符合您的需求 self.dice_explainers['sarcoII'] = { 'explainer': Dice(dice_data, dice_model, method='genetic'), 'mutable_features': ['Activity_Sedentary_Ratio', 'Activity_Diversity_Index', 'WWI', 'Vigorous_MET_Ratio', 'sedentary_minutes', 'body_mass_index'], # 🎯 使用配置文件中的高精度参数 'precision_config': dice_config_params } except Exception as e: logger.error(f"SarcoII DiCE设置失败: {str(e)}") def _wrap_model(self, model_type: str): """包装模型以适配DiCE""" class ModelWrapper: def __init__(self, model, threshold, model_manager_ref, model_type): self.model = model self.threshold = threshold self.model_manager = model_manager_ref self.model_type = model_type def predict(self, X): X_processed = self._process_input(X) proba = self.model.predict_proba(X_processed)[:, 1] return (proba >= self.threshold).astype(int) def predict_proba(self, X): X_processed = self._process_input(X) return self.model.predict_proba(X_processed) def _process_input(self, X): if isinstance(X, pd.DataFrame): X_processed = X.copy() for col in X_processed.columns: if X_processed[col].dtype.name == 'category': X_processed[col] = X_processed[col].astype('int64') return X_processed else: return X # 检查模型是否存在 if model_type not in self.model_manager.advisory_models: raise KeyError(f"建议模型 '{model_type}' 未找到") model = self.model_manager.advisory_models[model_type] threshold = self.model_manager.thresholds[model_type]['advisory'] return ModelWrapper(model, threshold, self.model_manager, model_type) async def generate_recommendations(self, user_data: UserInput, risk_types: List[str] = None, num_recommendations: int = 3, language: str = "zh") -> AdvisoryResponse: """ 生成个性化建议 Args: user_data: 用户输入数据 risk_types: 需要生成建议的风险类型 num_recommendations: 每种类型的建议数量 Returns: AdvisoryResponse: 建议结果 """ start_time = time.time() fallback_used = False try: # 转换用户数据 user_dict = user_data.model_dump() # 默认为高风险的类型生成建议 if risk_types is None: risk_types = self._determine_risk_types(user_dict) # 生成建议 sarcoI_recommendations = [] sarcoII_recommendations = [] if 'sarcoI' in risk_types: sarcoI_recommendations = await self._generate_sarcoI_recommendations( user_dict, num_recommendations, language ) if 'sarcoII' in risk_types: sarcoII_recommendations = await self._generate_sarcoII_recommendations( user_dict, num_recommendations, language ) # 如果没有生成任何建议,使用降级方案 if not sarcoI_recommendations and not sarcoII_recommendations: logger.warning("DiCE建议生成失败,使用降级方案") fallback_recs = self._generate_fallback_recommendations(user_dict, risk_types, language) if 'sarcoI' in risk_types: sarcoI_recommendations = fallback_recs[:2] # 前2个给SarcoI if 'sarcoII' in risk_types: sarcoII_recommendations = fallback_recs[2:4] if len(fallback_recs) > 2 else fallback_recs # 后2个给SarcoII fallback_used = True # 合并所有建议用于API返回 all_recommendations = sarcoI_recommendations + sarcoII_recommendations # 生成优先级行动和目标指标 priority_actions = self._generate_priority_actions(sarcoI_recommendations, sarcoII_recommendations) target_metrics = self._generate_target_metrics(user_dict, sarcoI_recommendations, sarcoII_recommendations) processing_time = time.time() - start_time response = AdvisoryResponse( sarcoI_recommendations=sarcoI_recommendations, sarcoII_recommendations=sarcoII_recommendations, recommendations=all_recommendations, # 添加合并的建议列表 priority_actions=priority_actions, target_metrics=target_metrics, processing_time=processing_time, success=True, fallback_used=fallback_used ) logger.info(f"建议生成完成: SarcoI={len(sarcoI_recommendations)}, SarcoII={len(sarcoII_recommendations)}, " f"fallback={fallback_used}, 耗时={processing_time:.2f}s") return response except Exception as e: logger.error(f"建议生成失败: {str(e)}") # 返回基础建议 basic_recommendations = self._generate_basic_recommendations(language) processing_time = time.time() - start_time if language == 'en': priority_actions = ["Consult Professional Doctor", "Increase Physical Activity", "Improve Diet"] target_metrics = {"Recommendation": "Seek professional medical guidance"} else: priority_actions = ["咨询专业医生", "增加体力活动", "改善饮食习惯"] target_metrics = {"建议": "寻求专业医疗指导"} return AdvisoryResponse( sarcoI_recommendations=basic_recommendations, sarcoII_recommendations=[], recommendations=basic_recommendations, # 添加合并的建议列表 priority_actions=priority_actions, target_metrics=target_metrics, processing_time=processing_time, success=False, fallback_used=True ) def _determine_risk_types(self, user_dict: Dict) -> List[str]: """确定需要生成建议的风险类型""" risk_types = [] try: # 使用建议模型进行高精确率预测 sarcoI_result = self.model_manager.predict_advisory(user_dict, 'sarcoI') sarcoII_result = self.model_manager.predict_advisory(user_dict, 'sarcoII') if sarcoI_result['risk_level'] in ['medium', 'high']: risk_types.append('sarcoI') if sarcoII_result['risk_level'] in ['medium', 'high']: risk_types.append('sarcoII') except Exception as e: logger.error(f"风险类型判断失败: {str(e)}") # 默认返回所有类型 risk_types = ['sarcoI', 'sarcoII'] return risk_types if risk_types else ['sarcoI', 'sarcoII'] async def _generate_sarcoI_recommendations(self, user_dict: Dict, num_recs: int, language: str = "zh") -> List[RecommendationItem]: """生成SarcoI建议""" recommendations = [] try: if 'sarcoI' in self.dice_explainers: logger.info("SarcoI 尝试使用DiCE生成建议...") # 使用DiCE生成建议,添加超时保护 import signal def dice_timeout_handler(signum, frame): raise TimeoutError("DiCE生成超时") # 设置2分钟超时 signal.signal(signal.SIGALRM, dice_timeout_handler) signal.alarm(120) try: dice_recs = self._generate_dice_recommendations(user_dict, 'sarcoI', num_recs, language) recommendations.extend(dice_recs) logger.info(f"SarcoI DiCE生成成功,获得{len(dice_recs)}个建议") finally: signal.alarm(0) # 如果DiCE建议不足,补充规则建议 if len(recommendations) < num_recs: logger.info("SarcoI DiCE建议不足,补充规则建议...") rule_recs = self._generate_sarcoI_rule_recommendations(user_dict, language) recommendations.extend(rule_recs[:num_recs - len(recommendations)]) except Exception as e: logger.error(f"SarcoI建议生成失败: {str(e)}") logger.info("SarcoI 使用规则建议作为降级方案") recommendations = self._generate_sarcoI_rule_recommendations(user_dict, language)[:num_recs] return recommendations[:num_recs] async def _generate_sarcoII_recommendations(self, user_dict: Dict, num_recs: int, language: str = "zh") -> List[RecommendationItem]: """生成SarcoII建议""" recommendations = [] try: if 'sarcoII' in self.dice_explainers: # 使用DiCE生成建议 dice_recs = self._generate_dice_recommendations(user_dict, 'sarcoII', num_recs, language) recommendations.extend(dice_recs) # 如果DiCE建议不足,补充规则建议 if len(recommendations) < num_recs: rule_recs = self._generate_sarcoII_rule_recommendations(user_dict, language) recommendations.extend(rule_recs[:num_recs - len(recommendations)]) except Exception as e: logger.error(f"SarcoII建议生成失败: {str(e)}") recommendations = self._generate_sarcoII_rule_recommendations(user_dict, language)[:num_recs] return recommendations[:num_recs] def _generate_dice_recommendations(self, user_dict: Dict, model_type: str, num_recs: int, language: str = "zh") -> List[RecommendationItem]: """使用DiCE生成反事实建议""" recommendations = [] import time try: dice_start_time = time.time() logger.info(f"开始{model_type} DiCE分析...") dice_config = self.dice_explainers[model_type] explainer = dice_config['explainer'] mutable_features = dice_config['mutable_features'] # 准备查询数据 query_df = self._prepare_dice_query(user_dict, model_type) logger.info(f"{model_type} DiCE查询数据准备完成: {query_df.shape}") logger.info(f"{model_type} 查询数据内容: {query_df.iloc[0].to_dict()}") # 检查用户当前预测结果 wrapped_model = self._wrap_model(model_type) current_prediction = wrapped_model.predict_proba(query_df)[0] current_class = wrapped_model.predict(query_df)[0] logger.info(f"{model_type} 当前预测: 类别={current_class}, 概率={current_prediction}") # 如果当前预测已经是低风险,生成维持性建议 if current_class == 0: logger.info(f"{model_type} 当前预测为低风险类别(0),生成维持性建议") maintenance_recommendations = self._generate_maintenance_recommendations(user_dict, model_type, language) recommendations.extend(maintenance_recommendations) return recommendations # 生成反事实 - 保持原始参数确保精确度 logger.info(f"{model_type} 开始生成反事实,目标类别=0,可变特征={mutable_features}") # 🚀 使用配置文件中的极致精度参数 dice_config_params = DiCEConfig.get_current_config() logger.info(f"{model_type} 使用DiCE精度模式: {DiCEConfig.PRECISION_MODE}") logger.info(f"{model_type} DiCE配置参数: {dice_config_params}") dice_params_list = [ # 🚀 极致精度配置:追求最高质量 { "total_CFs": dice_config_params.get('max_counterfactuals', 500), "desired_class": 0, "features_to_vary": mutable_features, "proximity_weight": dice_config_params.get('proximity_weight', 0.01), "diversity_weight": dice_config_params.get('diversity_weight', 15.0) }, # 🔥 高精度备用配置 { "total_CFs": dice_config_params.get('max_counterfactuals', 500) // 2, "desired_class": 0, "features_to_vary": mutable_features, "proximity_weight": dice_config_params.get('proximity_weight', 0.01) * 2, "diversity_weight": dice_config_params.get('diversity_weight', 15.0) * 0.8 }, # 🛡️ 保守配置(如果前两个失败) { "total_CFs": 100, "desired_class": 0, "features_to_vary": mutable_features, "proximity_weight": 0.1, "diversity_weight": 5.0 } ] # 🚀 设置DiCE总体超时 - 极致精度模式无时间限制 dice_timeout = dice_config_params.get('timeout_seconds', None) if dice_timeout: logger.info(f"{model_type} DiCE设置超时: {dice_timeout}秒") else: logger.info(f"{model_type} DiCE无超时限制 (极致精度模式)") counterfactuals = None for i, params in enumerate(dice_params_list): try: logger.info(f"{model_type} 尝试DiCE参数组合 {i+1}: {params}") logger.info(f"{model_type} 查询数据形状: {query_df.shape}") logger.info(f"{model_type} 可变特征: {params.get('features_to_vary', [])}") # 🚀 生成反事实,添加详细日志和计时 dice_gen_start = time.time() logger.info(f"{model_type} 开始DiCE生成 (极致精度模式)...") logger.info(f"{model_type} 预期生成 {params['total_CFs']} 个反事实样本") logger.info(f"{model_type} 多样性权重: {params['diversity_weight']}, 接近度权重: {params['proximity_weight']}") counterfactuals = explainer.generate_counterfactuals(query_df, **params) dice_gen_time = time.time() - dice_gen_start logger.info(f"{model_type} DiCE生成完成,耗时: {dice_gen_time:.2f}秒") # 检查是否生成了有效的反事实 if (hasattr(counterfactuals, 'cf_examples_list') and len(counterfactuals.cf_examples_list) > 0 and counterfactuals.cf_examples_list[0].final_cfs_df is not None and len(counterfactuals.cf_examples_list[0].final_cfs_df) > 0): logger.info(f"{model_type} DiCE参数组合 {i+1} 成功生成反事实") break else: logger.warning(f"{model_type} DiCE参数组合 {i+1} 未生成有效反事实") counterfactuals = None except Exception as param_e: logger.warning(f"{model_type} DiCE参数组合 {i+1} 失败: {str(param_e)}") counterfactuals = None if counterfactuals is None: logger.error(f"{model_type} 所有DiCE参数组合均失败") dice_time = time.time() - dice_start_time logger.info(f"{model_type} DiCE分析完成,耗时: {dice_time:.2f}秒") # 解析反事实结果 logger.info(f"{model_type} 反事实对象类型: {type(counterfactuals)}") logger.info(f"{model_type} 反事实对象属性: {dir(counterfactuals)}") if hasattr(counterfactuals, 'cf_examples_list'): logger.info(f"{model_type} cf_examples_list长度: {len(counterfactuals.cf_examples_list)}") if len(counterfactuals.cf_examples_list) > 0: cf_example = counterfactuals.cf_examples_list[0] logger.info(f"{model_type} cf_example类型: {type(cf_example)}") logger.info(f"{model_type} cf_example属性: {dir(cf_example)}") if hasattr(cf_example, 'final_cfs_df'): cf_df = cf_example.final_cfs_df logger.info(f"{model_type} final_cfs_df: {cf_df is not None}") if cf_df is not None: logger.info(f"{model_type} 反事实DataFrame形状: {cf_df.shape}") logger.info(f"{model_type} 反事实内容:\n{cf_df}") # 临时简化:直接使用前N个反事实,避免Series比较问题 logger.info(f"{model_type} 使用简化筛选,直接选择前{num_recs}个反事实") best_counterfactuals = cf_df.head(num_recs) logger.info(f"{model_type} 筛选出 {len(best_counterfactuals)} 个最佳反事实") # 处理筛选后的反事实 for i, (_, cf_row) in enumerate(best_counterfactuals.iterrows()): try: logger.info(f"{model_type} 处理第{i+1}个最佳反事实: {cf_row.to_dict()}") # 安全的特征变化分析 try: changes = self._analyze_feature_changes(query_df.iloc[0], cf_row, mutable_features) logger.info(f"{model_type} 特征变化: {changes}") except Exception as e: logger.error(f"{model_type} 特征变化分析失败: {str(e)}") continue # 安全的建议生成,避免重复 try: rec = self._changes_to_recommendation(changes, model_type, i + 1, language) if rec: # 检查是否与已有建议重复 is_duplicate = False for existing_rec in recommendations: if existing_rec.title == rec.title: is_duplicate = True break if not is_duplicate: recommendations.append(rec) logger.info(f"{model_type} 生成建议: {rec.title}") else: logger.info(f"{model_type} 跳过重复建议: {rec.title}") else: logger.warning(f"{model_type} 第{i+1}个反事实未生成有效建议") except Exception as e: logger.error(f"{model_type} 建议生成失败: {str(e)}") continue except Exception as e: logger.error(f"{model_type} 处理第{i+1}个反事实时出错: {str(e)}") import traceback logger.error(f"{model_type} 错误详情: {traceback.format_exc()}") continue else: logger.error(f"{model_type} final_cfs_df 为 None") else: logger.error(f"{model_type} cf_example 没有 final_cfs_df 属性") else: logger.error(f"{model_type} cf_examples_list 为空") else: logger.error(f"{model_type} counterfactuals 没有 cf_examples_list 属性") except Exception as e: dice_time = time.time() - dice_start_time if 'dice_start_time' in locals() else 0 logger.error(f"{model_type} DiCE建议生成失败 (耗时: {dice_time:.2f}秒): {str(e)}") # 🚀 追求极致精度模式:移除所有超时限制,无论耗时多久都要获得最佳结果 # 不再有任何时间限制,DiCE将运行到完成为止 logger.info(f"{model_type} DiCE分析耗时: {dice_time:.2f}秒 - 极致精度模式,无时间限制") return recommendations def _select_best_counterfactuals(self, original_row, cf_df, mutable_features, max_count): """🚀 极致精度反事实筛选 - 多维度质量评估,确保最优建议""" import pandas as pd import numpy as np if len(cf_df) <= max_count: return cf_df # 🎯 扩展特征分组,更细致的分类确保建议全面性 feature_groups = { 'weight_management': ['body_mass_index', 'WWI'], 'aerobic_exercise': ['Total_Moderate_Minutes_week', 'Vigorous_MET_Ratio'], 'sedentary_behavior': ['Activity_Sedentary_Ratio', 'sedentary_minutes'], 'exercise_diversity': ['Activity_Diversity_Index'], 'lifestyle_balance': [] # 综合性改变 } # 🏆 多维度质量评分系统 scored_cfs = [] for idx, cf_row in cf_df.iterrows(): # 1. 计算变化幅度合理性 (0-1分) change_reasonableness = self._calculate_change_reasonableness(original_row, cf_row, mutable_features) # 2. 计算特征变化多样性 (0-1分) change_diversity = self._calculate_change_diversity(original_row, cf_row, mutable_features) # 3. 计算实际可操作性 (0-1分) actionability = self._calculate_actionability(original_row, cf_row, mutable_features) # 4. 计算健康改善潜力 (0-1分) health_impact = self._calculate_health_impact(original_row, cf_row, mutable_features) # 🎯 综合质量评分 (加权平均) quality_score = ( change_reasonableness * 0.25 + # 变化合理性 change_diversity * 0.20 + # 多样性 actionability * 0.30 + # 可操作性 (最重要) health_impact * 0.25 # 健康影响 ) # 获取主要变化特征类别 primary_feature = self._get_primary_change_feature(original_row, cf_row, mutable_features) feature_category = self._get_feature_category(primary_feature, feature_groups) scored_cfs.append({ 'cf_row': cf_row, 'quality_score': quality_score, 'feature_category': feature_category, 'primary_feature': primary_feature, 'change_reasonableness': change_reasonableness, 'change_diversity': change_diversity, 'actionability': actionability, 'health_impact': health_impact }) # 🏅 按质量评分排序 scored_cfs.sort(key=lambda x: x['quality_score'], reverse=True) # 🎪 智能选择策略:优先质量,兼顾多样性 selected_cfs = [] used_categories = set() # 第一轮:每个类别选择最高质量的建议 for cf_info in scored_cfs: if cf_info['feature_category'] not in used_categories: selected_cfs.append(cf_info['cf_row']) used_categories.add(cf_info['feature_category']) if len(selected_cfs) >= max_count: break # 第二轮:如果还需要更多建议,选择剩余的高质量建议 if len(selected_cfs) < max_count: for cf_info in scored_cfs: if len(selected_cfs) >= max_count: break # 检查是否已经选择过这个建议 already_selected = False for selected_cf in selected_cfs: if self._are_cfs_similar(selected_cf, cf_info['cf_row'], mutable_features): already_selected = True break if not already_selected: selected_cfs.append(cf_info['cf_row']) return pd.DataFrame(selected_cfs) def _calculate_change_reasonableness(self, original_row, cf_row, mutable_features): """计算变化幅度的合理性 (0-1分)""" reasonableness_scores = [] for feature in mutable_features: if feature in original_row and feature in cf_row: original_val = original_row[feature] cf_val = cf_row[feature] # 计算相对变化幅度 if original_val != 0: relative_change = abs(cf_val - original_val) / abs(original_val) else: relative_change = abs(cf_val) # 根据特征类型设定合理变化范围 if 'bmi' in feature.lower() or 'weight' in feature.lower(): # 体重相关:10-30%变化较合理 if 0.1 <= relative_change <= 0.3: score = 1.0 elif relative_change < 0.1: score = relative_change / 0.1 # 变化太小 else: score = max(0.3, 1.0 - (relative_change - 0.3) / 0.5) # 变化太大 elif 'activity' in feature.lower() or 'minutes' in feature.lower(): # 运动相关:20-100%变化较合理 if 0.2 <= relative_change <= 1.0: score = 1.0 elif relative_change < 0.2: score = relative_change / 0.2 else: score = max(0.2, 1.0 - (relative_change - 1.0) / 1.0) else: # 其他特征:通用评分 if 0.1 <= relative_change <= 0.5: score = 1.0 elif relative_change < 0.1: score = relative_change / 0.1 else: score = max(0.2, 1.0 - (relative_change - 0.5) / 0.5) reasonableness_scores.append(score) return np.mean(reasonableness_scores) if reasonableness_scores else 0.5 def _calculate_change_diversity(self, original_row, cf_row, mutable_features): """计算特征变化的多样性 (0-1分)""" changed_features = 0 total_features = len(mutable_features) for feature in mutable_features: if feature in original_row and feature in cf_row: if abs(original_row[feature] - cf_row[feature]) > 1e-6: # 有显著变化 changed_features += 1 # 多样性评分:变化特征数量 / 总特征数量 diversity_ratio = changed_features / total_features if total_features > 0 else 0 # 理想情况是改变2-4个特征,给予最高分 if 2 <= changed_features <= 4: return 1.0 elif changed_features == 1: return 0.7 # 单一变化,分数较低 elif changed_features > 4: return max(0.5, 1.0 - (changed_features - 4) * 0.1) # 变化太多,逐渐降分 else: return 0.3 # 没有变化,最低分 def _calculate_actionability(self, original_row, cf_row, mutable_features): """计算实际可操作性 (0-1分)""" actionability_scores = [] for feature in mutable_features: if feature in original_row and feature in cf_row: original_val = original_row[feature] cf_val = cf_row[feature] change = abs(cf_val - original_val) # 根据特征类型评估可操作性 if 'minutes' in feature.lower(): # 运动时间:每周增加30-150分钟较容易实现 if 30 <= change <= 150: score = 1.0 elif change < 30: score = 0.8 # 变化太小,效果有限 else: score = max(0.4, 1.0 - (change - 150) / 200) # 变化太大,难以实现 elif 'bmi' in feature.lower(): # BMI:减少1-5较容易实现 if 1 <= change <= 5: score = 1.0 elif change < 1: score = 0.7 else: score = max(0.3, 1.0 - (change - 5) / 10) elif 'ratio' in feature.lower(): # 比例类特征:0.1-0.3的变化较合理 if 0.1 <= change <= 0.3: score = 1.0 elif change < 0.1: score = 0.8 else: score = max(0.4, 1.0 - (change - 0.3) / 0.5) else: # 默认评分 score = 0.8 actionability_scores.append(score) return np.mean(actionability_scores) if actionability_scores else 0.5 def _calculate_health_impact(self, original_row, cf_row, mutable_features): """计算健康改善潜力 (0-1分)""" impact_scores = [] for feature in mutable_features: if feature in original_row and feature in cf_row: original_val = original_row[feature] cf_val = cf_row[feature] # 根据特征类型和变化方向评估健康影响 if 'bmi' in feature.lower(): # BMI降低有益健康 if cf_val < original_val: # BMI降低 reduction = (original_val - cf_val) / original_val score = min(1.0, reduction * 5) # BMI每降低20%得满分 else: score = 0.3 # BMI增加不利健康 elif 'minutes' in feature.lower() and 'moderate' in feature.lower(): # 中等强度运动增加有益健康 if cf_val > original_val: # 运动时间增加 increase = (cf_val - original_val) / max(original_val, 1) score = min(1.0, increase * 2) # 运动时间每增加50%得满分 else: score = 0.4 # 运动减少不利健康 elif 'sedentary' in feature.lower(): # 久坐时间减少有益健康 if cf_val < original_val: # 久坐时间减少 reduction = (original_val - cf_val) / original_val score = min(1.0, reduction * 3) # 久坐时间每减少33%得满分 else: score = 0.2 # 久坐增加不利健康 elif 'vigorous' in feature.lower(): # 高强度运动比例增加有益健康 if cf_val > original_val: increase = (cf_val - original_val) / max(original_val, 0.1) score = min(1.0, increase * 1.5) else: score = 0.5 else: # 默认中等影响 score = 0.6 impact_scores.append(score) return np.mean(impact_scores) if impact_scores else 0.5 def _are_cfs_similar(self, cf1, cf2, mutable_features, threshold=0.1): """判断两个反事实是否相似""" similarity_count = 0 total_features = 0 for feature in mutable_features: if feature in cf1 and feature in cf2: total_features += 1 val1, val2 = cf1[feature], cf2[feature] # 计算相对差异 if abs(val1) > 1e-6 or abs(val2) > 1e-6: relative_diff = abs(val1 - val2) / max(abs(val1), abs(val2), 1e-6) if relative_diff < threshold: similarity_count += 1 else: similarity_count += 1 # 都接近0,认为相似 similarity_ratio = similarity_count / total_features if total_features > 0 else 0 return similarity_ratio > 0.8 # 80%以上特征相似则认为是相似的反事实 def _calculate_counterfactual_quality(self, original_row, cf_row, mutable_features): """计算反事实的质量分数""" score = 0 for feature in mutable_features: if feature in original_row.index and feature in cf_row.index: # 确保获取标量值 original_val = float(original_row[feature]) cf_val = float(cf_row[feature]) change = abs(cf_val - original_val) # 根据特征类型评分 if feature == 'body_mass_index': # BMI变化:合理范围内的变化得分更高 if 18.5 <= cf_val <= 24.9: # 正常BMI范围 score += 10 elif 25 <= cf_val <= 29.9: # 超重范围 score += 5 else: # 过低或过高 score -= 5 # 变化幅度合理性 if change > 50: # BMI变化超过50不合理 score -= 20 elif change > 20: # 变化过大 score -= 10 elif feature in ['Total_Moderate_Minutes_week', 'Vigorous_MET_Ratio', 'Activity_Sedentary_Ratio']: # 运动相关特征:有改善但不过度 if change > 0 and change <= original_val * 2: # 合理增长 score += 8 elif change > original_val * 2: # 过度增长 score -= 5 elif feature == 'WWI': # WWI变化:适度减少 if cf_val < original_val and change <= 3: # 适度减少 score += 6 elif change > 5: # 变化过大 score -= 8 return score def _get_primary_change_feature(self, original_row, cf_row, mutable_features): """找到变化最大的特征""" max_change = 0 primary_feature = None for feature in mutable_features: if feature in original_row.index and feature in cf_row.index: # 确保获取标量值 original_val = float(original_row[feature]) cf_val = float(cf_row[feature]) # 计算相对变化 if abs(original_val) > 1e-10: # 避免除零错误 relative_change = abs((cf_val - original_val) / original_val) else: relative_change = abs(cf_val) if relative_change > max_change: max_change = relative_change primary_feature = feature return primary_feature or mutable_features[0] def _get_feature_category(self, feature, feature_groups): """获取特征所属的类别""" for category, features in feature_groups.items(): if feature in features: return category return 'other' def _prepare_dice_query(self, user_dict: Dict, model_type: str) -> pd.DataFrame: """准备DiCE查询数据""" if model_type == 'sarcoI': features = ['body_mass_index', 'race_ethnicity', 'WWI', 'age_years', 'Activity_Sedentary_Ratio', 'Total_Moderate_Minutes_week', 'Vigorous_MET_Ratio'] else: # sarcoII features = ['body_mass_index', 'race_ethnicity', 'age_years', 'Activity_Sedentary_Ratio', 'Activity_Diversity_Index', 'WWI', 'Vigorous_MET_Ratio', 'sedentary_minutes'] feature_values = [] missing_features = [] for feature in features: if feature in user_dict and user_dict[feature] is not None: value = user_dict[feature] feature_values.append(value) logger.debug(f"{model_type} 特征 {feature}: {value} (来自用户数据)") else: # 默认值 defaults = { 'Total_Moderate_Minutes_week': 150, 'Activity_Diversity_Index': 2, 'sedentary_minutes': 480, 'Activity_Sedentary_Ratio': 0.5, 'Vigorous_MET_Ratio': 0.2 } default_val = defaults.get(feature, 0) feature_values.append(default_val) missing_features.append(feature) logger.warning(f"{model_type} 特征 {feature}: {default_val} (默认值)") if missing_features: logger.info(f"{model_type} DiCE缺失特征使用默认值: {missing_features}") query_df = pd.DataFrame([feature_values], columns=features) # 验证数据范围 self._validate_dice_query_data(query_df, model_type) return query_df def _validate_dice_query_data(self, query_df: pd.DataFrame, model_type: str): """验证DiCE查询数据的合理性""" data = query_df.iloc[0] # 检查关键特征的合理性 if data['body_mass_index'] < 10 or data['body_mass_index'] > 50: logger.warning(f"{model_type} BMI异常: {data['body_mass_index']}") if data['WWI'] < 5 or data['WWI'] > 20: logger.warning(f"{model_type} WWI异常: {data['WWI']}") if data['age_years'] < 18 or data['age_years'] > 80: logger.warning(f"{model_type} 年龄异常: {data['age_years']}") # 检查活动相关特征 if 'Activity_Sedentary_Ratio' in data: if data['Activity_Sedentary_Ratio'] < 0 or data['Activity_Sedentary_Ratio'] > 1: logger.warning(f"{model_type} 久坐比例异常: {data['Activity_Sedentary_Ratio']}") logger.info(f"{model_type} DiCE数据验证完成") def _analyze_feature_changes(self, original, counterfactual, mutable_features) -> List[Dict]: """分析特征变化""" changes = [] for feature in mutable_features: if feature in original.index and feature in counterfactual.index: # 确保获取标量值,避免Series比较问题 original_val = float(original[feature]) cf_val = float(counterfactual[feature]) if abs(cf_val - original_val) > 1e-6: # 安全的百分比计算 if abs(original_val) > 1e-10: change_pct = ((cf_val - original_val) / original_val) * 100 else: change_pct = 0.0 changes.append({ 'feature': feature, 'original': original_val, 'target': cf_val, 'change': cf_val - original_val, 'change_pct': change_pct }) return changes def _changes_to_recommendation(self, changes: List[Dict], model_type: str, rec_id: int, language: str = "zh") -> Optional[RecommendationItem]: """将特征变化转换为多样化的精确建议""" if not changes: return None # 按特征类型优先级选择,确保多样性 feature_priority = { 'Total_Moderate_Minutes_week': 1, # 运动优先 'Vigorous_MET_Ratio': 2, 'Activity_Diversity_Index': 3, 'body_mass_index': 4, # 体重其次 'WWI': 5, 'Activity_Sedentary_Ratio': 6 } # 根据建议ID选择不同类型的特征,确保5个建议的多样性 if rec_id == 1: # 第一个建议:中等强度运动 preferred_features = ['Total_Moderate_Minutes_week', 'moderate_minutes'] elif rec_id == 2: # 第二个建议:高强度运动 preferred_features = ['Vigorous_MET_Ratio', 'vigorous_minutes'] elif rec_id == 3: # 第三个建议:运动多样性 preferred_features = ['Activity_Diversity_Index', 'Activity_Sedentary_Ratio'] elif rec_id == 4: # 第四个建议:体重管理 preferred_features = ['body_mass_index', 'WWI'] else: # 第五个建议:久坐行为改善 preferred_features = ['sedentary_minutes', 'Activity_Sedentary_Ratio'] # 寻找首选特征的变化 selected_change = None for feature in preferred_features: for change in changes: if change['feature'] == feature: selected_change = change break if selected_change: break # 如果没找到首选特征,选择变化最大的 if not selected_change: changes.sort(key=lambda x: abs(x['change']), reverse=True) selected_change = changes[0] feature = selected_change['feature'] original_val = selected_change['original'] target_val = selected_change['target'] change_val = selected_change['change'] # 生成精确数值建议 title, description, target_change, expected_impact = self._get_precise_feature_recommendation( feature, original_val, target_val, change_val, model_type, language ) # 基于变化幅度计算优先级 change_magnitude = abs(change_val / original_val) if original_val != 0 else abs(change_val) if change_magnitude > 0.3: # 变化超过30% priority = "High" if language == 'en' else "高" elif change_magnitude > 0.15: # 变化超过15% priority = "Medium" if language == 'en' else "中" else: priority = "Low" if language == 'en' else "低" return RecommendationItem( title=title, description=description, priority=priority, target_change=target_change, expected_impact=expected_impact ) def _get_precise_feature_recommendation(self, feature: str, original_val: float, target_val: float, change_val: float, model_type: str, language: str = "zh") -> tuple: """获取基于精确数值的特征建议""" if feature == 'body_mass_index': if language == 'en': title = "Exercise-Based Weight Management Plan" description = f"Current BMI: {original_val:.1f} → Target: {target_val:.1f}" if original_val > target_val: weight_loss = (original_val - target_val) * 2.5 # Assuming height 1.7m, 1 BMI ≈ 2.5kg description += f"\n• Weight Loss Goal: {weight_loss:.1f} kg" description += f"\n• Timeline: {weight_loss/0.75:.0f} weeks (0.5-1kg/week)" description += f"\n• Exercise Plan:" description += f"\n - Aerobic: 300+ min/week" description += f"\n - Strength training: 3x/week" description += f"\n - HIIT: 2x/week" target_change = f"BMI: {original_val:.1f} → {target_val:.1f} (change: {change_val:+.1f})" expected_impact = f"Every 1-point BMI reduction decreases sarcopenia risk by 8-12% through exercise" else: title = "运动减重精确管理计划" description = f"当前BMI: {original_val:.1f} → 目标: {target_val:.1f}" if original_val > target_val: weight_loss = (original_val - target_val) * 2.5 # 假设身高1.7m,每1BMI约2.5kg description += f"\n• 减重目标: {weight_loss:.1f} 公斤" description += f"\n• 时间计划: {weight_loss/0.75:.0f} 周 (每周0.5-1公斤)" description += f"\n• 运动方案:" description += f"\n - 有氧运动: 每周300+分钟" description += f"\n - 力量训练: 每周3次" description += f"\n - HIIT训练: 每周2次" target_change = f"BMI: {original_val:.1f} → {target_val:.1f} (变化: {change_val:+.1f})" expected_impact = f"BMI每降低1点,通过运动肌少症风险降低约8-12%" elif feature == 'Total_Moderate_Minutes_week': if language == 'en': title = "Moderate-Intensity Exercise Precision Plan" description = f"Current weekly: {original_val:.0f}min → Target: {target_val:.0f}min" if target_val > original_val: increase = target_val - original_val daily_increase = increase / 7 description += f"\n• Increase: {increase:.0f} min/week ({daily_increase:.0f} min/day)" # Specific exercise recommendations if daily_increase <= 15: description += f"\n• Start: Daily brisk walk {daily_increase:.0f}min" elif daily_increase <= 30: description += f"\n• Plan: Walk 20min + swim/cycle {daily_increase-20:.0f}min" else: description += f"\n• Plan: Walk 30min + other exercises {daily_increase-30:.0f}min" description += f"\n• Activity Mix:" description += f"\n - Walking/jogging: {increase*0.4:.0f}min/week" description += f"\n - Swimming: {increase*0.3:.0f}min/week" description += f"\n - Cycling: {increase*0.2:.0f}min/week" description += f"\n - Tai Chi/Yoga: {increase*0.1:.0f}min/week" # Progressive plan weeks_to_target = max(4, int(increase / 30)) description += f"\n\nProgressive plan ({weeks_to_target}-week target):" for week in range(1, min(weeks_to_target + 1, 5)): weekly_target = original_val + (increase * week / weeks_to_target) description += f"\nWeek {week}: {weekly_target:.0f}min/week" target_change = f"Moderate exercise: {original_val:.0f}min/week → {target_val:.0f}min/week" expected_impact = f"Every 30min/week increase reduces sarcopenia risk by 5-8%, muscle strength improves 10-15%" else: title = "中等强度运动精确计划" description = f"当前每周: {original_val:.0f}分钟 → 目标: {target_val:.0f}分钟" if target_val > original_val: increase = target_val - original_val daily_increase = increase / 7 description += f"\n• 增加: {increase:.0f} 分钟/周 (每天{daily_increase:.0f}分钟)" # 具体的运动建议 if daily_increase <= 15: description += f"\n• 开始: 每天快走{daily_increase:.0f}分钟" elif daily_increase <= 30: description += f"\n• 方案: 快走20分钟 + 游泳/骑车{daily_increase-20:.0f}分钟" else: description += f"\n• 方案: 快走30分钟 + 其他运动{daily_increase-30:.0f}分钟" description += f"\n• 活动组合:" description += f"\n - 快走/慢跑: {increase*0.4:.0f}分钟/周" description += f"\n - 游泳: {increase*0.3:.0f}分钟/周" description += f"\n - 骑车: {increase*0.2:.0f}分钟/周" description += f"\n - 太极/瑜伽: {increase*0.1:.0f}分钟/周" # 渐进式计划 weeks_to_target = max(4, int(increase / 30)) description += f"\n\n渐进式计划({weeks_to_target}周达标):" for week in range(1, min(weeks_to_target + 1, 5)): weekly_target = original_val + (increase * week / weeks_to_target) description += f"\n第{week}周:{weekly_target:.0f}分钟/周" target_change = f"中等强度运动: {original_val:.0f}分钟/周 → {target_val:.0f}分钟/周" expected_impact = f"每增加30分钟/周,肌少症风险降低约5-8%,肌肉力量提升10-15%" elif feature == 'Vigorous_MET_Ratio': if language == 'en': title = "High-Intensity Exercise Precision Enhancement Plan" description = f"Current ratio: {original_val:.3f} → Target ratio: {target_val:.3f}" if target_val > original_val: increase = target_val - original_val description += f"\n📈 Need to increase high-intensity exercise ratio by {increase:.3f}" # Convert to actual time recommendations weekly_minutes = increase * 150 # Based on 150min/week baseline daily_minutes = weekly_minutes / 7 description += f"\n⏱️ Equivalent to adding {weekly_minutes:.0f} min/week high-intensity exercise" description += f"\n (about {daily_minutes:.0f} min/day)" # Specific high-intensity exercise plans description += f"\n🔥 Recommended high-intensity exercise combination:" if weekly_minutes <= 60: description += f"\n • Strength training: 3x/week, {weekly_minutes/3:.0f}min each" description += f"\n • HIIT training: 2x/week, {weekly_minutes/4:.0f}min each" elif weekly_minutes <= 120: description += f"\n • Strength training: 3x/week, 25min each" description += f"\n • Running/sprints: 2x/week, {(weekly_minutes-75)/2:.0f}min each" description += f"\n • HIIT training: 2x/week, 15min each" else: description += f"\n • Strength training: 4x/week, 30min each" description += f"\n • Running: 3x/week, {(weekly_minutes-120)/3:.0f}min each" description += f"\n • HIIT training: 2x/week, 20min each" # Safety reminders and progressive plan description += f"\n⚠️ Safety reminders:" description += f"\n • Warm up thoroughly for 5-10 minutes before exercise" description += f"\n • Cool down and stretch for 10 minutes after exercise" description += f"\n • Progress gradually to avoid exercise injuries" # Progressive intensity enhancement weeks_to_target = max(6, int(weekly_minutes / 20)) description += f"\n📅 {weeks_to_target}-week progressive plan:" for week in [1, 2, 4, weeks_to_target]: if week <= weeks_to_target: weekly_target = weekly_minutes * week / weeks_to_target description += f"\n Week {week}: {weekly_target:.0f}min/week high-intensity exercise" target_change = f"High-intensity exercise ratio: {original_val:.3f} → {target_val:.3f}" expected_impact = f"Every 0.1 increase in high-intensity ratio reduces sarcopenia risk by 10-15%, muscle strength improves 20-30%" else: title = "高强度运动精确提升计划" description = f"当前比例: {original_val:.3f} → 目标比例: {target_val:.3f}" if target_val > original_val: increase = target_val - original_val description += f"\n📈 需要提升 {increase:.3f} 的高强度运动比例" # 转换为实际时间建议 weekly_minutes = increase * 150 # 假设基于150分钟/周的基础 daily_minutes = weekly_minutes / 7 description += f"\n⏱️ 相当于每周增加 {weekly_minutes:.0f} 分钟高强度运动" description += f"\n (每天约 {daily_minutes:.0f} 分钟)" # 具体的高强度运动计划 description += f"\n🔥 推荐高强度运动组合:" if weekly_minutes <= 60: description += f"\n • 力量训练:3次/周,每次{weekly_minutes/3:.0f}分钟" description += f"\n • HIIT训练:2次/周,每次{weekly_minutes/4:.0f}分钟" elif weekly_minutes <= 120: description += f"\n • 力量训练:3次/周,每次25分钟" description += f"\n • 跑步/冲刺:2次/周,每次{(weekly_minutes-75)/2:.0f}分钟" description += f"\n • HIIT训练:2次/周,每次15分钟" else: description += f"\n • 力量训练:4次/周,每次30分钟" description += f"\n • 跑步:3次/周,每次{(weekly_minutes-120)/3:.0f}分钟" description += f"\n • HIIT训练:2次/周,每次20分钟" # 安全提醒和渐进计划 description += f"\n⚠️ 安全提醒:" description += f"\n • 运动前充分热身5-10分钟" description += f"\n • 运动后拉伸放松10分钟" description += f"\n • 循序渐进,避免运动损伤" # 渐进式强度提升 weeks_to_target = max(6, int(weekly_minutes / 20)) description += f"\n📅 {weeks_to_target}周渐进计划:" for week in [1, 2, 4, weeks_to_target]: if week <= weeks_to_target: weekly_target = weekly_minutes * week / weeks_to_target description += f"\n 第{week}周:{weekly_target:.0f}分钟/周高强度运动" target_change = f"高强度运动比例: {original_val:.3f} → {target_val:.3f}" expected_impact = f"高强度运动比例每提升0.1,肌少症风险降低10-15%,肌肉力量提升20-30%" elif feature == 'Activity_Sedentary_Ratio': if language == 'en': title = "Sedentary Behavior Precision Improvement Plan" description = f"Current activity/sedentary ratio: {original_val:.3f} → Target ratio: {target_val:.3f}" if target_val > original_val: increase = target_val - original_val # Assuming 16 hours of waking time daily_activity_increase = increase * 16 * 60 # Convert to minutes description += f"\n📈 Need to improve activity/sedentary ratio by {increase:.3f}" description += f"\n⏱️ Equivalent to increasing daily activity by {daily_activity_increase:.0f} minutes" # Specific activity increase strategies description += f"\n🎯 Activity increase strategies:" if daily_activity_increase <= 30: description += f"\n • Stand and move for 3-5 minutes every hour" description += f"\n • Take stairs instead of elevators" description += f"\n • Stand during phone calls/meetings" elif daily_activity_increase <= 60: description += f"\n • Stand and move for 5 minutes every 45 minutes" description += f"\n • Take a 15-minute walk during lunch break" description += f"\n • Use standing desk for 2-3 hours" description += f"\n • Park farther away/get off transit early to walk" else: description += f"\n • Stand and move for 5 minutes every 30 minutes" description += f"\n • Walk/bike to work for 30 minutes" description += f"\n • Use standing desk for 4-6 hours" description += f"\n • Actively engage in household activities" # Different strategies for weekdays and weekends description += f"\n📅 Time-specific strategies:" description += f"\n Weekdays:" description += f"\n • Set 30-minute activity reminders" description += f"\n • Do stretching exercises between meetings" description += f"\n • Take 10-15 minute walks after lunch" description += f"\n Weekends:" description += f"\n • Increase outdoor activities to {daily_activity_increase*1.5:.0f} minutes" description += f"\n • Engage in housework, gardening, walking shopping" description += f"\n • Reduce TV/phone screen time" # Practical tools recommendations description += f"\n🛠️ Practical tools:" description += f"\n • Set sedentary reminders on phone" description += f"\n • Use pedometer/fitness tracker" description += f"\n • Adjustable height desk" description += f"\n • Exercise ball instead of office chair" target_change = f"Activity/sedentary ratio: {original_val:.3f} → {target_val:.3f}" expected_impact = f"Every 0.1 increase in activity ratio reduces sarcopenia risk by 6-10%, metabolism improves 15-20%" else: title = "久坐行为精确改善计划" description = f"当前活动/久坐比例: {original_val:.3f} → 目标比例: {target_val:.3f}" if target_val > original_val: increase = target_val - original_val # 假设16小时清醒时间 daily_activity_increase = increase * 16 * 60 # 转换为分钟 description += f"\n📈 需要提升 {increase:.3f} 的活动/久坐比例" description += f"\n⏱️ 相当于每天增加活动 {daily_activity_increase:.0f} 分钟" # 具体的活动增加策略 description += f"\n🎯 活动增加策略:" if daily_activity_increase <= 30: description += f"\n • 每小时起身活动3-5分钟" description += f"\n • 走楼梯代替电梯" description += f"\n • 站立接电话/开会" elif daily_activity_increase <= 60: description += f"\n • 每45分钟起身活动5分钟" description += f"\n • 午休时间散步15分钟" description += f"\n • 使用站立办公桌2-3小时" description += f"\n • 远距离停车/提前下车步行" else: description += f"\n • 每30分钟起身活动5分钟" description += f"\n • 上下班步行/骑车30分钟" description += f"\n • 站立办公4-6小时" description += f"\n • 主动做家务活动" # 工作日和周末的不同策略 description += f"\n📅 分时段策略:" description += f"\n 工作日:" description += f"\n • 设置每30分钟活动提醒" description += f"\n • 会议间隙做伸展运动" description += f"\n • 午餐后散步10-15分钟" description += f"\n 周末:" description += f"\n • 户外活动增加到{daily_activity_increase*1.5:.0f}分钟" description += f"\n • 家务活动、园艺、购物步行" description += f"\n • 减少看电视/手机时间" # 实用工具推荐 description += f"\n🛠️ 实用工具:" description += f"\n • 手机设置久坐提醒" description += f"\n • 使用计步器/智能手环" description += f"\n • 可调节高度办公桌" description += f"\n • 瑜伽球代替办公椅" target_change = f"活动/久坐比例: {original_val:.3f} → {target_val:.3f}" expected_impact = f"活动比例每提升0.1,肌少症风险降低6-10%,新陈代谢提升15-20%" elif feature == 'WWI': if language == 'en': title = "Core-Focused Waist Management Plan" description = f"Current WWI: {original_val:.2f} → Target WWI: {target_val:.2f}" if original_val > target_val: decrease = original_val - target_val # WWI = waist/height^0.5, assuming height 1.7m waist_reduction = decrease * (1.7 ** 0.5) description += f"\n📉 Need to reduce WWI by {decrease:.2f}" description += f"\n📏 Equivalent to waist reduction of approximately {waist_reduction:.1f} cm" description += f"\n💪 Exercise recommendations:" description += f"\n • Core strengthening: Planks, crunches, Russian twists" description += f"\n • Aerobic exercise: Focus on fat burning" description += f"\n • HIIT training: Target abdominal fat reduction" target_change = f"WWI: {original_val:.2f} → {target_val:.2f}" expected_impact = f"Every 0.5 WWI reduction decreases sarcopenia risk by 7-12%" else: title = "核心运动腰围精确管理计划" description = f"当前WWI: {original_val:.2f} → 目标WWI: {target_val:.2f}" if original_val > target_val: decrease = original_val - target_val # WWI = 腰围/身高^0.5,假设身高1.7m waist_reduction = decrease * (1.7 ** 0.5) description += f"\n📉 需要减少WWI {decrease:.2f}" description += f"\n📏 相当于腰围减少约 {waist_reduction:.1f} 厘米" description += f"\n💪 运动建议:" description += f"\n • 核心训练:平板支撑、卷腹、俄式转体" description += f"\n • 有氧运动:专注脂肪燃烧" description += f"\n • HIIT训练:针对腹部脂肪减少" target_change = f"WWI: {original_val:.2f} → {target_val:.2f}" expected_impact = f"WWI每降低0.5,肌少症风险降低约7-12%" elif feature == 'Activity_Diversity_Index': if language == 'en': title = "Exercise Diversity Precision Enhancement Plan" description = f"Current diversity index: {original_val:.1f} → Target: {target_val:.1f}" if target_val > original_val: increase = target_val - original_val target_types = int(target_val) + 2 description += f"\n📈 Need to increase activity diversity by {increase:.1f}" description += f"\n🎯 Goal: Engage in {target_types} different types of exercise weekly" # Exercise type classification and recommendations description += f"\n🏃‍♂️ Recommended exercise type combinations:" description += f"\n Aerobic exercises (2-3 types):" description += f"\n • Brisk walking/jogging - 3-4x/week, 30min each" description += f"\n • Swimming/water exercise - 2x/week, 45min each" description += f"\n • Cycling - 2-3x/week, 40min each" description += f"\n 💪 Strength training (1-2 types):" description += f"\n • Weight training - 2-3x/week, 45min each" description += f"\n • Bodyweight training - 3x/week, 30min each" description += f"\n • Resistance band training - 2x/week, 25min each" description += f"\n 🧘‍♀️ Flexibility & balance (1-2 types):" description += f"\n • Yoga - 2-3x/week, 60min each" description += f"\n • Tai Chi - 2x/week, 45min each" description += f"\n • Pilates - 2x/week, 50min each" description += f"\n 🏐 Recreational sports (1 type):" description += f"\n • Ball sports - 1-2x/week" description += f"\n • Dancing - 1-2x/week" description += f"\n • Hiking/mountain climbing - 1x/week" # Weekly plan example description += f"\n📅 Diverse weekly plan example:" description += f"\n Monday: Strength training (45min)" description += f"\n Tuesday: Brisk walking (30min) + Yoga (30min)" description += f"\n Wednesday: Swimming (45min)" description += f"\n Thursday: Bodyweight training (30min) + Tai Chi (30min)" description += f"\n Friday: Cycling (40min)" description += f"\n Saturday: Ball sports (60min)" description += f"\n Sunday: Hiking/climbing (90min)" # Progressive increase recommendations description += f"\n🔄 Progressive increase strategy:" current_types = max(1, int(original_val)) for week in range(1, 5): week_types = min(current_types + week, target_types) description += f"\n Week {week}: {week_types} exercise types" target_change = f"Activity diversity: {original_val:.1f} → {target_val:.1f}" expected_impact = f"Every 1-point increase in exercise diversity reduces sarcopenia risk by 4-8%, whole-body coordination improves 25%" else: title = "运动多样性精确提升计划" description = f"当前多样性指数: {original_val:.1f} → 目标: {target_val:.1f}" if target_val > original_val: increase = target_val - original_val target_types = int(target_val) + 2 description += f"\n📈 需要增加 {increase:.1f} 的活动多样性" description += f"\n🎯 目标:每周进行 {target_types} 种不同类型的运动" # 运动类型分类和建议 description += f"\n🏃‍♂️ 推荐运动类型组合:" description += f"\n 有氧运动类 (2-3种):" description += f"\n • 快走/慢跑 - 每周3-4次,每次30分钟" description += f"\n • 游泳/水中运动 - 每周2次,每次45分钟" description += f"\n • 骑自行车 - 每周2-3次,每次40分钟" description += f"\n 💪 力量训练类 (1-2种):" description += f"\n • 器械训练 - 每周2-3次,每次45分钟" description += f"\n • 自重训练 - 每周3次,每次30分钟" description += f"\n • 弹力带训练 - 每周2次,每次25分钟" description += f"\n 🧘‍♀️ 柔韧平衡类 (1-2种):" description += f"\n • 瑜伽 - 每周2-3次,每次60分钟" description += f"\n • 太极 - 每周2次,每次45分钟" description += f"\n • 普拉提 - 每周2次,每次50分钟" description += f"\n 🏐 趣味运动类 (1种):" description += f"\n • 球类运动 - 每周1-2次" description += f"\n • 舞蹈 - 每周1-2次" description += f"\n • 爬山/徒步 - 每周1次" # 周计划示例 description += f"\n📅 多样性周计划示例:" description += f"\n 周一:力量训练 (45分钟)" description += f"\n 周二:快走 (30分钟) + 瑜伽 (30分钟)" description += f"\n 周三:游泳 (45分钟)" description += f"\n 周四:自重训练 (30分钟) + 太极 (30分钟)" description += f"\n 周五:骑车 (40分钟)" description += f"\n 周六:球类运动 (60分钟)" description += f"\n 周日:徒步/爬山 (90分钟)" # 渐进式增加建议 description += f"\n🔄 渐进式增加策略:" current_types = max(1, int(original_val)) for week in range(1, 5): week_types = min(current_types + week, target_types) description += f"\n 第{week}周:{week_types}种运动类型" target_change = f"活动多样性: {original_val:.1f} → {target_val:.1f}" expected_impact = f"运动多样性每提升1点,肌少症风险降低4-8%,全身协调性提升25%" elif feature == 'sedentary_minutes': if language == 'en': title = "Daily Sedentary Time Precision Control Plan" description = f"Current daily sedentary: {original_val:.0f}min → Target: {target_val:.0f}min" if original_val > target_val: decrease = original_val - target_val hours_decrease = decrease / 60 description += f"\n📉 Need to reduce daily sedentary time by {decrease:.0f} minutes ({hours_decrease:.1f} hours)" # Time-segmented reduction strategies description += f"\n🕐 Time-segmented reduction strategies:" if decrease <= 60: description += f"\n • Stand and move for 5 minutes every hour" description += f"\n • Stand or walk during phone meetings" description += f"\n • Stand for 10 minutes after meals" elif decrease <= 120: description += f"\n • Stand and move for 5-8 minutes every 45 minutes" description += f"\n • Take 15-20 minute walks during lunch break" description += f"\n • Do simple exercises while watching TV" description += f"\n • Use standing desk for 1-2 hours" else: description += f"\n • Stand and move for 5-10 minutes every 30 minutes" description += f"\n • Walk/bike to work instead of driving" description += f"\n • Use standing desk for 3-4 hours" description += f"\n • Actively take on tasks requiring movement" # Specific alternative activities description += f"\n🚶‍♂️ Sedentary replacement activities:" description += f"\n Work hours:" description += f"\n • Stand during meetings/phone calls" description += f"\n • Walk to colleagues' desks for communication" description += f"\n • Take stairs instead of elevators" description += f"\n • Walk farther for water/restroom breaks" description += f"\n Leisure time:" description += f"\n • Stretch while watching TV" description += f"\n • Do housework while listening to music" description += f"\n • Stand while reading/studying" description += f"\n • Take walks instead of scrolling phone" # Practical tools and reminders description += f"\n🛠️ Practical tools and reminders:" description += f"\n • Set {30 if decrease > 120 else 45 if decrease > 60 else 60}-minute sedentary reminders on phone" description += f"\n • Use standing desk or adjustable desk" description += f"\n • Wear fitness tracker to monitor sedentary time" description += f"\n • Place reminder notes on desk" # Progressive reduction plan description += f"\n📅 4-week progressive reduction plan:" for week in range(1, 5): week_target = original_val - (decrease * week / 4) daily_reduction = decrease * week / 4 description += f"\n Week {week}: Daily sedentary {week_target:.0f}min (reduce {daily_reduction:.0f}min)" else: title = "每日久坐时间精确控制计划" description = f"当前每日久坐: {original_val:.0f}分钟 → 目标: {target_val:.0f}分钟" if original_val > target_val: decrease = original_val - target_val hours_decrease = decrease / 60 description += f"\n📉 需要每天减少久坐 {decrease:.0f} 分钟 ({hours_decrease:.1f}小时)" # 分时段减少策略 description += f"\n🕐 分时段减少策略:" if decrease <= 60: description += f"\n • 每小时起身活动5分钟" description += f"\n • 电话会议时站立或走动" description += f"\n • 用餐后站立10分钟" elif decrease <= 120: description += f"\n • 每45分钟起身活动5-8分钟" description += f"\n • 午休时间散步15-20分钟" description += f"\n • 看电视时做简单运动" description += f"\n • 站立办公1-2小时" else: description += f"\n • 每30分钟起身活动5-10分钟" description += f"\n • 上下班步行/骑车代替开车" description += f"\n • 站立办公3-4小时" description += f"\n • 主动承担需要走动的任务" # 具体的替代活动 description += f"\n🚶‍♂️ 久坐替代活动:" description += f"\n 工作时间:" description += f"\n • 站立开会/打电话" description += f"\n • 走到同事桌前交流" description += f"\n • 爬楼梯代替电梯" description += f"\n • 远距离取水/上厕所" description += f"\n 休闲时间:" description += f"\n • 看电视时做拉伸" description += f"\n • 边听音乐边做家务" description += f"\n • 站立阅读/学习" description += f"\n • 散步代替刷手机" # 实用工具和提醒 description += f"\n🛠️ 实用工具和提醒:" description += f"\n • 手机设置每{30 if decrease > 120 else 45 if decrease > 60 else 60}分钟久坐提醒" description += f"\n • 使用站立办公桌或升降桌" description += f"\n • 佩戴智能手环监测久坐" description += f"\n • 在办公桌放置提醒标语" # 渐进式减少计划 description += f"\n📅 4周渐进减少计划:" for week in range(1, 5): week_target = original_val - (decrease * week / 4) daily_reduction = decrease * week / 4 description += f"\n 第{week}周:每日久坐{week_target:.0f}分钟 (减少{daily_reduction:.0f}分钟)" target_change = f"每日久坐时间: {original_val:.0f}分钟 → {target_val:.0f}分钟" expected_impact = f"每日久坐时间每减少60分钟,肌少症风险降低约5-9%" else: title = f"{feature}优化计划" description = f"当前值: {original_val:.3f} → 目标值: {target_val:.3f}" target_change = f"{feature}: {original_val:.3f} → {target_val:.3f}" expected_impact = "遵循专业指导,预期改善肌少症风险" return title, description, target_change, expected_impact def _get_recommendation_texts(self, language: str = "zh") -> Dict: """获取双语建议文本""" texts = { "zh": { "increase_vigorous": { "title": "增加高强度运动", "description": "每周至少进行75分钟高强度运动,如跑步、游泳或力量训练\n\n具体建议:\n• 跑步:每次20-30分钟,每周3次\n• 力量训练:每次45分钟,每周2次\n• 游泳:每次30分钟,每周2次", "priority": "高", "target_change": "高强度运动比例提升至0.3以上", "expected_impact": "显著改善肌肉力量和心肺功能" }, "increase_moderate": { "title": "增加中等强度运动", "description": "每周进行至少150分钟中等强度运动\n\n推荐活动:\n• 快走:每次30分钟,每周5次\n• 骑车:每次40分钟,每周3次\n• 太极:每次45分钟,每周2次", "priority": "中", "target_change": "中等强度运动增至200分钟/周", "expected_impact": "提升整体体能和肌肉耐力" }, "reduce_sedentary": { "title": "减少久坐时间", "description": "控制每日久坐时间,增加活动频率\n\n实施方案:\n• 每30分钟起身活动5分钟\n• 站立办公2-3小时\n• 步行会议和电话\n• 使用提醒工具", "priority": "高", "target_change": "久坐比例降至0.4以下", "expected_impact": "改善肌肉活性,降低肌少症风险" }, "weight_management": { "title": "体重管理", "description": "通过运动控制体重在健康范围\n\n目标设定:\n• BMI控制在18.5-24.9\n• 每周减重0.5-1公斤\n• 有氧运动300分钟/周\n• 力量训练3次/周", "priority": "中", "target_change": "BMI控制在18.5-24.9范围内", "expected_impact": "减轻关节负担,改善身体成分" }, "reduce_sedentary": { "title": "减少久坐时间", "description": "每小时起身活动5-10分钟,减少连续久坐时间", "priority": "高", "target_change": "久坐比例降至0.4以下", "expected_impact": "改善肌肉活性,降低肌少症风险" }, "increase_moderate": { "title": "增加中等强度运动", "description": "每周进行至少150分钟中等强度运动,如快走、骑车", "priority": "中", "target_change": "中等强度运动增至200分钟/周", "expected_impact": "提升整体体能和肌肉耐力" }, "weight_management": { "title": "体重管理", "description": "通过合理饮食和运动,维持健康体重", "priority": "中", "target_change": "BMI控制在18.5-24.9范围内", "expected_impact": "减轻关节负担,改善身体成分" }, "protein_intake": { "title": "增加蛋白质摄入", "description": "每日摄入1.2-1.6g/kg体重的优质蛋白质", "priority": "高", "target_change": "蛋白质摄入达到推荐标准", "expected_impact": "促进肌肉合成,维持肌肉质量" }, "regular_monitoring": { "title": "定期健康监测", "description": "每6个月进行一次肌肉质量和功能评估", "priority": "中", "target_change": "建立定期监测计划", "expected_impact": "早期发现肌肉质量变化,及时调整干预策略" }, "increase_diversity": { "title": "增加运动多样性", "description": "尝试多种不同类型的体力活动,如有氧运动、力量训练、柔韧性练习", "priority": "高", "target_change": "活动类型增加至4种以上", "expected_impact": "全面改善身体功能,降低SarcoII风险" }, "strength_training": { "title": "加强力量训练", "description": "每周进行2-3次力量训练,重点锻炼大肌群", "priority": "高", "target_change": "建立规律的力量训练计划", "expected_impact": "增强肌肉力量,改善身体功能" }, "increase_activity": { "title": "增加体力活动", "description": "每周至少进行150分钟中等强度或75分钟高强度运动", "priority": "高", "target_change": "达到WHO推荐的运动量标准", "expected_impact": "全面改善身体健康状况" }, "balanced_nutrition": { "title": "均衡营养摄入", "description": "确保充足的蛋白质、维生素D和钙的摄入", "priority": "中", "target_change": "建立健康的饮食习惯", "expected_impact": "支持肌肉健康和骨骼强度" } }, "en": { "increase_vigorous": { "title": "Increase High-Intensity Exercise", "description": "Engage in at least 75 minutes of vigorous exercise weekly\n\nSpecific recommendations:\n• Running: 20-30 minutes per session, 3 times weekly\n• Strength training: 45 minutes per session, 2 times weekly\n• Swimming: 30 minutes per session, 2 times weekly", "priority": "High", "target_change": "Increase vigorous exercise ratio to above 0.3", "expected_impact": "Significantly improve muscle strength and cardiopulmonary function" }, "reduce_sedentary": { "title": "Reduce Sedentary Time", "description": "Control daily sedentary time and increase activity frequency\n\nImplementation plan:\n• Stand and move for 5 minutes every 30 minutes\n• Use standing desk for 2-3 hours\n• Walking meetings and phone calls\n• Use reminder tools", "priority": "High", "target_change": "Reduce sedentary ratio to below 0.4", "expected_impact": "Improve muscle activity and reduce sarcopenia risk" }, "increase_moderate": { "title": "Increase Moderate-Intensity Exercise", "description": "Engage in at least 150 minutes of moderate exercise weekly\n\nRecommended activities:\n• Brisk walking: 30 minutes per session, 5 times weekly\n• Cycling: 40 minutes per session, 3 times weekly\n• Tai Chi: 45 minutes per session, 2 times weekly", "priority": "Medium", "target_change": "Increase moderate exercise to 200 minutes/week", "expected_impact": "Enhance overall fitness and muscle endurance" }, "weight_management": { "title": "Weight Management", "description": "Maintain healthy weight through balanced diet and exercise", "priority": "Medium", "target_change": "Keep BMI within 18.5-24.9 range", "expected_impact": "Reduce joint burden and improve body composition" }, "protein_intake": { "title": "Increase Protein Intake", "description": "Consume 1.2-1.6g/kg body weight of high-quality protein daily", "priority": "High", "target_change": "Meet recommended protein intake standards", "expected_impact": "Promote muscle synthesis and maintain muscle mass" }, "regular_monitoring": { "title": "Regular Health Monitoring", "description": "Conduct muscle mass and function assessment every 6 months", "priority": "Medium", "target_change": "Establish regular monitoring plan", "expected_impact": "Early detection of muscle mass changes, timely intervention adjustment" }, "increase_diversity": { "title": "Increase Exercise Diversity", "description": "Try various types of physical activities, such as aerobic exercise, strength training, and flexibility exercises", "priority": "High", "target_change": "Increase activity types to 4 or more", "expected_impact": "Comprehensively improve body function and reduce SarcoII risk" }, "strength_training": { "title": "Strengthen Resistance Training", "description": "Perform strength training 2-3 times per week, focusing on major muscle groups", "priority": "High", "target_change": "Establish regular strength training plan", "expected_impact": "Enhance muscle strength and improve body function" }, "increase_activity": { "title": "Increase Physical Activity", "description": "Engage in at least 150 minutes of moderate-intensity or 75 minutes of vigorous-intensity exercise weekly", "priority": "High", "target_change": "Meet WHO recommended physical activity standards", "expected_impact": "Comprehensively improve overall health" }, "balanced_nutrition": { "title": "Balanced Nutrition Intake", "description": "Ensure adequate intake of protein, vitamin D, and calcium", "priority": "Medium", "target_change": "Establish healthy eating habits", "expected_impact": "Support muscle health and bone strength" } } } return texts def _generate_sarcoI_rule_recommendations(self, user_dict: Dict, language: str = "zh") -> List[RecommendationItem]: """生成SarcoI基于规则的建议""" recommendations = [] texts = self._get_recommendation_texts(language) # 基于用户数据生成规则建议 bmi = user_dict.get('body_mass_index', 25) sedentary_ratio = user_dict.get('Activity_Sedentary_Ratio', 0.5) moderate_minutes = user_dict.get('Total_Moderate_Minutes_week', 150) vigorous_ratio = user_dict.get('Vigorous_MET_Ratio', 0.2) if vigorous_ratio < 0.2: rec = texts[language]["increase_vigorous"] recommendations.append(RecommendationItem( title=rec["title"], description=rec["description"], priority=rec["priority"], target_change=rec["target_change"], expected_impact=rec["expected_impact"] )) if moderate_minutes < 150: rec = texts[language]["increase_moderate"] recommendations.append(RecommendationItem( title=rec["title"], description=rec["description"], priority=rec["priority"], target_change=rec["target_change"], expected_impact=rec["expected_impact"] )) if bmi > 25: rec = texts[language]["weight_management"] recommendations.append(RecommendationItem( title=rec["title"], description=rec["description"], priority=rec["priority"], target_change=rec["target_change"], expected_impact=rec["expected_impact"] )) return recommendations def _generate_maintenance_recommendations(self, user_dict: Dict, model_type: str, language: str = "zh") -> List[RecommendationItem]: """ 为低风险用户生成维持性建议 Args: user_dict: 用户数据字典 model_type: 模型类型 ('sarcoI' 或 'sarcoII') Returns: 维持性建议列表 """ recommendations = [] # 基础维持建议 if language == 'en': recommendations.append(RecommendationItem( title="Maintain Current Health Status", description="Your current sarcopenia risk is low, continue maintaining your existing healthy lifestyle", priority="Medium", target_change="Maintain current exercise and dietary habits", expected_impact="Continue reducing sarcopenia risk, maintain muscle health" )) else: recommendations.append(RecommendationItem( title="保持当前健康状态", description="您当前的肌少症风险较低,建议继续保持现有的健康生活方式", priority="中", target_change="维持当前的运动和饮食习惯", expected_impact="持续降低肌少症风险,保持肌肉健康" )) # 基于当前活动水平的维持建议 if model_type == 'sarcoI': current_moderate = user_dict.get('Total_Moderate_Minutes_week', 150) current_vigorous = user_dict.get('Vigorous_MET_Ratio', 0.2) if current_moderate >= 150: if language == 'en': recommendations.append(RecommendationItem( title="Continue Moderate-Intensity Exercise", description=f"You currently engage in {current_moderate:.0f} minutes of moderate exercise weekly, continue maintaining this", priority="Medium", target_change="Maintain at least 150 minutes of moderate exercise weekly", expected_impact="Maintain cardiopulmonary function and muscle endurance" )) else: recommendations.append(RecommendationItem( title="继续中等强度运动", description=f"您当前每周进行{current_moderate:.0f}分钟中等强度运动,建议继续保持", priority="中", target_change="维持每周至少150分钟中等强度运动", expected_impact="保持心肺功能和肌肉耐力" )) if current_vigorous >= 0.2: if language == 'en': recommendations.append(RecommendationItem( title="Maintain High-Intensity Exercise", description="Your high-intensity exercise ratio is good, continue maintaining strength training and high-intensity exercise", priority="Medium", target_change="Maintain current high-intensity exercise level", expected_impact="Maintain muscle mass and strength" )) else: recommendations.append(RecommendationItem( title="保持高强度运动", description="您的高强度运动比例良好,建议继续保持力量训练和高强度运动", priority="中", target_change="维持当前的高强度运动水平", expected_impact="保持肌肉质量和力量" )) # 预防性建议 age = user_dict.get('age_years', 45) if age > 50: if language == 'en': recommendations.append(RecommendationItem( title="Age-Related Prevention", description="As you age, it's recommended to regularly monitor muscle mass and appropriately increase protein intake", priority="Medium", target_change="Conduct muscle mass assessment every 6 months", expected_impact="Early detection of muscle mass changes, timely intervention adjustment" )) else: recommendations.append(RecommendationItem( title="年龄相关预防", description="随着年龄增长,建议定期监测肌肉质量,适当增加蛋白质摄入", priority="中", target_change="每6个月进行一次肌肉质量评估", expected_impact="早期发现肌肉质量变化,及时调整干预策略" )) return recommendations def _generate_sarcoII_rule_recommendations(self, user_dict: Dict, language: str = "zh") -> List[RecommendationItem]: """生成SarcoII基于规则的建议""" recommendations = [] texts = self._get_recommendation_texts(language) diversity_index = user_dict.get('Activity_Diversity_Index', 2) sedentary_minutes = user_dict.get('sedentary_minutes', 480) sedentary_ratio = user_dict.get('Activity_Sedentary_Ratio', 0.5) if diversity_index < 3: rec = texts[language]["increase_diversity"] recommendations.append(RecommendationItem( title=rec["title"], description=rec["description"], priority=rec["priority"], target_change=rec["target_change"], expected_impact=rec["expected_impact"] )) if sedentary_minutes > 480: rec = texts[language]["reduce_sedentary"] recommendations.append(RecommendationItem( title=rec["title"], description=rec["description"], priority=rec["priority"], target_change=rec["target_change"], expected_impact=rec["expected_impact"] )) if sedentary_ratio > 0.6: if language == 'en': recommendations.append(RecommendationItem( title="Reduce Sedentary Activity Ratio", description="Increase standing and light activity time, reduce sedentary activity proportion", priority="Medium", target_change="Reduce sedentary activity ratio to below 50%", expected_impact="Improve daily activity level, promote muscle health" )) else: recommendations.append(RecommendationItem( title="降低久坐活动比例", description="增加站立和轻度活动时间,减少久坐活动占比", priority="中", target_change="久坐活动比例降至50%以下", expected_impact="提高日常活动水平,促进肌肉健康" )) return recommendations def _generate_fallback_recommendations(self, user_dict: Dict, risk_types: List[str] = None, language: str = "zh") -> List[RecommendationItem]: """生成降级建议""" recommendations = [] texts = self._get_recommendation_texts(language) # 基础建议 rec = texts[language]["increase_activity"] recommendations.append(RecommendationItem( title=rec["title"], description=rec["description"], priority=rec["priority"], target_change=rec["target_change"], expected_impact=rec["expected_impact"] )) rec = texts[language]["increase_diversity"] recommendations.append(RecommendationItem( title=rec["title"], description=rec["description"], priority=rec["priority"], target_change=rec["target_change"], expected_impact=rec["expected_impact"] )) # 基于用户数据的个性化建议 bmi = user_dict.get('body_mass_index', 25) if bmi > 25: if language == 'en': recommendations.append(RecommendationItem( title="Weight Management", description="Control weight through balanced diet and exercise, target BMI between 18.5-24.9", priority="Medium", target_change=f"Reduce weight to BMI below {bmi-2:.1f}", expected_impact="Reduce sarcopenia risk, improve overall health" )) else: recommendations.append(RecommendationItem( title="体重管理", description="通过合理饮食和运动控制体重,目标BMI在18.5-24.9之间", priority="中", target_change=f"减重至BMI {bmi-2:.1f}以下", expected_impact="降低肌少症风险,改善整体健康" )) sedentary_minutes = user_dict.get('sedentary_minutes', 480) if sedentary_minutes > 480: # 超过8小时 if language == 'en': recommendations.append(RecommendationItem( title="Reduce Sedentary Time", description="Stand and move for 5-10 minutes every hour, reduce continuous sitting time", priority="Medium", target_change="Control daily sedentary time within 8 hours", expected_impact="Improve blood circulation, maintain muscle vitality" )) else: recommendations.append(RecommendationItem( title="减少久坐时间", description="每小时起身活动5-10分钟,减少连续久坐时间", priority="中", target_change="每日久坐时间控制在8小时以内", expected_impact="改善血液循环,维持肌肉活力" )) return recommendations def _generate_basic_recommendations(self, language: str = "zh") -> List[RecommendationItem]: """生成基础建议""" if language == 'en': return [ RecommendationItem( title="Consult Professional Doctor", description="Seek professional medical guidance and conduct detailed health assessment", priority="High", target_change="Obtain personalized medical advice", expected_impact="Develop scientific health management plan" ), RecommendationItem( title="Regular Exercise", description="Maintain regular physical activity, including aerobic exercise and strength training", priority="Medium", target_change="Establish long-term exercise habits", expected_impact="Maintain and improve physical health" ) ] else: return [ RecommendationItem( title="咨询专业医生", description="建议寻求专业医疗指导,进行详细的健康评估", priority="高", target_change="获得个性化医疗建议", expected_impact="制定科学的健康管理方案" ), RecommendationItem( title="规律运动", description="保持规律的体力活动,包括有氧运动和力量训练", priority="中", target_change="建立长期运动习惯", expected_impact="维持和改善身体健康" ) ] def _generate_priority_actions(self, sarcoI_recs: List[RecommendationItem], sarcoII_recs: List[RecommendationItem]) -> List[str]: """生成优先级行动列表""" actions = [] # 收集高优先级建议 for rec in sarcoI_recs + sarcoII_recs: if rec.priority == "高": actions.append(rec.title) # 如果没有高优先级建议,添加基础建议 if not actions: actions = ["增加体力活动", "改善生活方式", "定期健康检查"] return actions[:3] # 最多返回3个优先行动 def _generate_target_metrics(self, user_dict: Dict, sarcoI_recs: List[RecommendationItem], sarcoII_recs: List[RecommendationItem]) -> Dict[str, Any]: """生成目标指标""" metrics = {} # 基于用户当前状态和建议生成目标 current_bmi = user_dict.get('body_mass_index', 25) current_activity = user_dict.get('Total_Moderate_Minutes_week', 150) if current_bmi > 25: metrics['目标BMI'] = f"{current_bmi - 2:.1f} - {current_bmi - 1:.1f}" if current_activity < 150: metrics['每周运动时间'] = "150分钟以上" metrics['评估周期'] = "4-6周" metrics['下次检查'] = "3个月后" return metrics # 创建全局建议服务实例 advisory_service = AdvisoryService()