初始归档:健康管理资料库(大医网/药膳/体质监测/中医理论等12个资料集,7712个文件)

This commit is contained in:
512song committed 2026-09-23 21:59:25 +08:00
commit 80ae3811cf
7712 files changed
+4628547

No files matched your search

@@ -0,0 +1,580 @@
#!/usr/bin/env python3
"""
国民体质测定标准 — 评测计算引擎
输入:年龄、性别、各项测试指标原始值
输出:单项得分、综合加权得分、综合评级
"""
import json
import math
import sys
import os
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DATA_PATH = os.path.join(BASE_DIR, '评分标准结构化数据.json')
def load_scoring_data(path=DATA_PATH):
with open(path, 'r', encoding='utf-8') as f:
return json.load(f)
def determine_section(age):
"""根据年龄确定所属部分"""
if 3 <= age <= 6:
return '幼儿'
elif 20 <= age <= 59:
return '成年人'
elif 60 <= age <= 79:
return '老年人'
else:
raise ValueError(f"年龄 {age} 不在标准覆盖范围(3-79岁)")
def determine_age_group(age, section):
"""确定年龄组名称(与JSON中一致)"""
if section == '幼儿':
# 3-5岁每0.5岁一组,6岁单独一组
if 3 <= age < 6:
# 取最近的半岁
base = math.floor(age)
remainder = age - base
if remainder < 0.25:
return f"{base}岁"
elif remainder < 0.75:
return f"{base}.5岁"
else:
return f"{base + 1}岁"
else: # age == 6
return "6岁"
elif section == '成年人':
# 每5岁一组
low = (age // 5) * 5
high = low + 4
return f"{low}-{high} 岁"
elif section == '老年人':
# 每5岁一组
low = (age // 5) * 5
high = low + 4
return f"{low}-{high} 岁"
def find_score_in_standard_table(score_bands, age_group, value):
"""
在标准评分表中查找得分
score_bands: [{"score": 10, "ranges": {"3岁": {"max": 92.1}, ...}}, ...]
规则:从高到低匹配,第一个匹配的分数
"""
# 从高到低排序
sorted_bands = sorted(score_bands, key=lambda x: x['score'], reverse=True)
for band in sorted_bands:
score = band['score']
ranges = band['ranges']
if age_group not in ranges:
continue
r = ranges[age_group]
if 'min' in r and 'max' in r:
if r['min'] <= value <= r['max']:
return score
elif 'min' in r and value >= r['min']:
return score
elif 'max' in r and value <= r['max']:
return score
elif 'exact' in r and abs(value - r['exact']) < 0.001:
return score
return 0 # 超出所有范围
def find_score_in_bmi_table(bmi_data, age_group, value):
"""
在BMI/体脂率分段评分表中查找得分
bmi_data.data: {"36月": [{"score": 60, "max": 13.4}, {"score": 100, "min": 13.4, "max": 18.4}, ...], ...}
这些表是"banded"类型,按行=年龄,列=分数档
"""
age_records = bmi_data.get(age_group, [])
if not age_records:
# 对于成年人BMI,年龄组可能是"20-59 岁"(只有一个组)
# 尝试找第一个key(可能是唯一组)
if bmi_data:
first_key = list(bmi_data.keys())[0]
age_records = bmi_data.get(first_key, [])
if not age_records:
return 0
for record in age_records:
score = record['score']
r = record # 直接包含 min/max
if 'min' in r and 'max' in r:
if r['min'] <= value <= r['max']:
return score
elif 'min' in r and value >= r['min']:
return score
elif 'max' in r and value <= r['max']:
return score
return 0
def get_age_group_key_for_section(age, section, lookup):
"""获取JSON中该年龄对应的年龄组key"""
if section == '幼儿':
# 先尝试半岁组
age_group = determine_age_group(age, section)
return age_group
elif section == '成年人':
# 成人5岁一组
return determine_age_group(age, section)
else:
return determine_age_group(age, section)
def get_section_for_age_key(age_group, section_data):
"""针对幼儿BMI表需要用月度key"""
return age_group # 默认一致
def score_indicator(section, gender, indicator, value, age, sl):
"""
对单个指标评分
返回: (score, indicator_name)
"""
if section == '幼儿' and gender == '男':
gender_key = 'male'
elif section == '幼儿' and gender == '女':
gender_key = 'female'
elif section == '成年人' and gender == '男':
gender_key = 'male'
elif section == '成年人' and gender == '女':
gender_key = 'female'
elif section == '老年人' and gender == '男':
gender_key = 'male'
elif section == '老年人' and gender == '女':
gender_key = 'female'
else:
gender_key = 'male'
# 通用指标(BMI等性别中立的)
table = None
if indicator in sl[section].get('通用', {}):
table = sl[section]['通用'][indicator]
elif indicator in sl[section].get(gender_key, {}):
table = sl[section][gender_key][indicator]
else:
return (0, f"未找到 {section}/{gender}/{indicator} 的评分表")
tbl_type = table['type']
age_group = determine_age_group(age, section)
if tbl_type == 'standard_scoring':
age_groups = table['data']['age_groups']
# 对幼儿处理BMI的月份key(如果age_group不是月格式)
if section == '幼儿' and indicator == 'BMI':
# 幼儿BMI用月龄key
month_key = f"{int(age * 12)}月"
if month_key in age_groups:
age_group = month_key
# 匹配年龄组
if age_group not in age_groups:
age_group = normalize_age_group(age_group, age_groups)
score = find_score_in_standard_table(
table['data']['score_bands'],
age_group,
value
)
return (score, indicator)
elif tbl_type == 'bmi_banded':
data = table['data']['data']
age_groups = list(data.keys())
if section == '幼儿':
# 幼儿BMI用月龄
month_key = f"{int(age * 12)}月"
if month_key in age_groups:
age_group = month_key
score = find_score_in_bmi_table(data, age_group, value)
return (score, indicator)
return (0, indicator)
def normalize_age_group(age_group, available_groups):
"""匹配年龄组名,处理空格/格式差异"""
# 精确匹配
if age_group in available_groups:
return age_group
# 去空格匹配
age_stripped = age_group.replace(' ', '')
for ag in available_groups:
if ag.replace(' ', '') == age_stripped:
return ag
# 前缀匹配
for ag in available_groups:
if ag.startswith(age_group[:4]) or age_group.startswith(ag[:4]):
return ag
return age_group # 找不到则原样返回
def compute_bmi(height_cm, weight_kg):
"""计算BMI"""
height_m = height_cm / 100
return weight_kg / (height_m ** 2)
def evaluate_person(age, gender, test_values, scoring_data=None):
"""
评测一个人
参数:
age: 年龄(周岁)
gender: '男' 或 '女'
test_values: dict,指标名→测试值
- 幼儿: 身高(cm), 体重(kg), 握力(kg), 立定跳远(cm), 坐位体前屈(cm),
双脚连续跳(秒), 15米绕障碍跑(秒), 走平衡木(秒)
- 成年人: 身高(cm), 体重(kg), 体脂率(%), 肺活量(ml), 功率车(ml/kg/min),
握力(kg), 纵跳(cm), 俯卧撑(次)/跪卧撑(次), 仰卧起坐(次/分),
坐位体前屈(cm), 闭眼单脚站立(秒), 选择反应时(秒)
- 老年人: 身高(cm), 体重(kg), 体脂率(%), 肺活量(ml), 2分钟高抬腿(次),
握力(kg), 坐位体前屈(cm), 30秒坐站(次), 闭眼单脚站立(秒),
选择反应时(秒)
返回: dict
"""
if scoring_data is None:
scoring_data = load_scoring_data()
sl = scoring_data['scoring_lookup']
section = determine_section(age)
# 确定年龄段标签
age_group = determine_age_group(age, section)
results = {
'age': age,
'gender': gender,
'section': section,
'age_group': age_group,
'indicators': {},
'total_score': 0,
'rating': '',
'errors': []
}
# 1. 先计算BMI(如果有身高体重)
bmi_value = None
height_cm = test_values.get('身高(cm)', test_values.get('身高'))
weight_kg = test_values.get('体重(kg)', test_values.get('体重'))
if height_cm and weight_kg:
bmi_value = compute_bmi(height_cm, weight_kg)
test_values['BMI'] = round(bmi_value, 1)
# 2. 对各指标评分
# 标准化指标名映射(去除单位后缀)
indicator_alias = {
'体脂率(%)': '体脂率', '体脂率': '体脂率',
'肺活量(ml)': '肺活量', '肺活量': '肺活量',
'功率车(ml/kg/min)': '功率车', '功率车': '功率车',
'握力(kg)': '握力', '握力': '握力',
'纵跳(cm)': '纵跳', '纵跳': '纵跳',
'俯卧撑(次)': '俯卧撑', '俯卧撑': '俯卧撑',
'跪卧撑(次)': '跪卧撑', '跪卧撑': '跪卧撑',
'仰卧起坐(次/分)': '仰卧起坐', '仰卧起坐': '仰卧起坐',
'坐位体前屈(cm)': '坐位体前屈', '坐位体前屈': '坐位体前屈',
'立定跳远(cm)': '立定跳远', '立定跳远': '立定跳远',
'双脚连续跳(秒)': '双脚连续跳', '双脚连续跳': '双脚连续跳',
'15米绕障碍跑(秒)': '绕障碍跑', '15米绕障碍跑': '绕障碍跑',
'走平衡木(秒)': '走平衡木', '走平衡木': '走平衡木',
'闭眼单脚站立(秒)': '闭眼单脚站立', '闭眼单脚站立': '闭眼单脚站立',
'选择反应时(秒)': '选择反应时', '选择反应时': '选择反应时',
'2分钟高抬腿(次)': '高抬腿', '高抬腿': '高抬腿',
'30秒坐站(次)': '坐站', '坐站': '坐站',
'身高(cm)': '身高', '身高': '身高',
'体重(kg)': '体重', '体重': '体重',
}
for raw_indicator, value in test_values.items():
if value is None or value == '':
continue
try:
value = float(value)
except (ValueError, TypeError):
continue
# 标准化指标名
indicator = indicator_alias.get(raw_indicator, raw_indicator)
# 体重不直接评分(通过BMI反映)
if indicator == '体重':
continue
# 身高在成年人/老年人不单独评分(通过BMI反映)
if section != '幼儿' and indicator == '身高':
continue
score, matched_name = score_indicator(section, gender, indicator, value, age, sl)
results['indicators'][indicator] = {
'value': value,
'score': score,
'matched_indicator': matched_name,
'input_key': raw_indicator
}
# 3. 计算加权总分
total_weighted = 0.0
total_weight_used = 0.0
# 确定使用的权重表
if section == '幼儿':
weight_table = sl['weights'].get('幼儿', {})
elif section == '成年人':
if age <= 49:
weight_table = sl['weights'].get('成年人_20-49岁', sl['weights'].get('成年人', {}))
else:
weight_table = sl['weights'].get('成年人_50-59岁', sl['weights'].get('成年人', {}))
elif section == '老年人':
weight_table = sl['weights'].get('老年人_60-79岁', sl['weights'].get('老年人', {}))
else:
weight_table = {}
# 权重表里的指标名可能与评分时的指标名不同,需要映射
weight_to_indicator = {
'体重指数(BMI)': 'BMI',
'身高': '身高',
'体脂率': '体脂率',
'肺活量': '肺活量',
'功率车二级负荷试验': '功率车',
'握力': '握力',
'纵跳': '纵跳',
'俯卧撑(男)/跪卧撑(女)': '俯卧撑',
'1分钟仰卧起坐': '仰卧起坐',
'坐位体前屈': '坐位体前屈',
'立定跳远': '立定跳远',
'双脚连续跳': '双脚连续跳',
'15 米绕障碍跑': '绕障碍跑',
'走平衡木': '走平衡木',
'闭眼单脚站立': '闭眼单脚站立',
'选择反应时': '选择反应时',
'2分钟原地高抬腿': '高抬腿',
'30秒坐站': '坐站',
}
# 反向映射:评分指标名 → 权重表指标名
rev_map = {v: k for k, v in weight_to_indicator.items()}
scoring_details = []
for wt_name, weight in weight_table.items():
indicator_key = weight_to_indicator.get(wt_name, wt_name)
scored = results['indicators'].get(indicator_key)
# 如果直接映射没找到,尝试模糊匹配:权重键包含评分指标名
if scored is None:
for scored_key in results['indicators']:
if scored_key in wt_name or wt_name in scored_key:
scored = results['indicators'][scored_key]
indicator_key = scored_key
break
if scored is None:
# 尝试直接匹配
scored = results['indicators'].get(wt_name)
if scored:
pts = scored['score'] * weight
total_weighted += pts
total_weight_used += weight
scoring_details.append({
'indicator': indicator_key,
'weight': weight,
'score': scored['score'],
'weighted_pts': round(pts, 2)
})
# 处理剩余未在权重表里但已评分的指标
for indicator, scored in results['indicators'].items():
if indicator not in [s['indicator'] for s in scoring_details]:
scoring_details.append({
'indicator': indicator,
'weight': 0,
'score': scored['score'],
'weighted_pts': 0,
'note': '未在权重表中'
})
results['scoring_detail'] = scoring_details
results['total_score'] = round(total_weighted, 1)
results['weight_coverage'] = round(total_weight_used, 2)
# 4. 确定综合评级
rating_levels = sl['rating_levels'].get(section, {})
rating = '未知'
for level_name, criteria in rating_levels.items():
if 'min' in criteria and 'max' in criteria:
if criteria['min'] <= total_weighted < criteria['max']:
rating = level_name
break
elif 'min' in criteria and total_weighted >= criteria['min']:
rating = level_name
break
elif 'max' in criteria and total_weighted < criteria['max']:
rating = level_name
break
results['rating'] = rating
return results
def format_report(result):
"""格式化输出评测报告"""
lines = []
lines.append("=" * 60)
lines.append(" 国民体质测定标准 — 评测报告")
lines.append("=" * 60)
lines.append(f" 年龄:{result['age']}岁 性别:{result['gender']}")
lines.append(f" 年龄段:{result['section']}({result['age_group']}组)")
lines.append("-" * 60)
lines.append(f" {'指标':<16} {'实测值':<10} {'得分':<6} {'权重':<6} {'加权分':<8}")
lines.append("-" * 60)
for item in result.get('scoring_detail', []):
indicator = item['indicator']
# 找实测值
value = result['indicators'].get(indicator, {}).get('value', '')
if isinstance(value, (int, float)):
value_str = f"{value:.1f}"
else:
value_str = str(value)
note = item.get('note', '')
weight_str = f"{item['weight']:.2f}" if item['weight'] > 0 else '-'
pts_str = f"{item['weighted_pts']:.1f}" if item['weighted_pts'] > 0 else '-'
indicator_show = indicator[:14]
lines.append(f" {indicator_show:<16} {value_str:<10} {item['score']:<6} {weight_str:<6} {pts_str:<8}")
lines.append("-" * 60)
lines.append(f" 综合得分:{result['total_score']:.1f}")
lines.append(f" 权重覆盖率:{result['weight_coverage']:.0%}")
lines.append(f" 综合评级:{result['rating']}")
if result.get('errors'):
lines.append("-" * 60)
for err in result['errors']:
lines.append(f" ⚠ {err}")
lines.append("=" * 60)
return '\n'.join(lines)
def interactive_eval():
"""交互式评测"""
print("国民体质测定标准 — 交互式评测")
print("=" * 50)
try:
age = int(input("年龄(周岁): "))
gender = input("性别(男/女): ").strip()
if gender not in ['男', '女']:
print("性别输入错误")
return
except ValueError:
print("年龄输入错误")
return
section = determine_section(age)
print(f"\n→ 适用部分:{section}({determine_age_group(age, section)}组)")
# 提示应输入哪些指标
if section == '幼儿':
prompts = ['身高(cm)', '体重(kg)', '握力(kg)', '立定跳远(cm)',
'坐位体前屈(cm)', '双脚连续跳(秒)', '15米绕障碍跑(秒)', '走平衡木(秒)']
elif section == '成年人':
prompts = ['身高(cm)', '体重(kg)', '体脂率(%)', '肺活量(ml)',
'功率车(ml/kg/min)', '握力(kg)', '纵跳(cm)',
'俯卧撑(次)', '跪卧撑(次)', '仰卧起坐(次/分)',
'坐位体前屈(cm)', '闭眼单脚站立(秒)', '选择反应时(秒)']
else:
prompts = ['身高(cm)', '体重(kg)', '体脂率(%)', '肺活量(ml)',
'2分钟高抬腿(次)', '握力(kg)', '坐位体前屈(cm)',
'30秒坐站(次)', '闭眼单脚站立(秒)', '选择反应时(秒)']
test_values = {}
print(f"\n请输入测试值(直接回车跳过可选指标):")
for prompt in prompts:
val = input(f" {prompt}: ").strip()
if val:
try:
test_values[prompt] = float(val)
except ValueError:
test_values[prompt] = val
result = evaluate_person(age, gender, test_values)
print()
print(format_report(result))
return result
if __name__ == '__main__':
if len(sys.argv) > 1 and sys.argv[1] in ('--demo', '-d'):
# 演示模式
data = load_scoring_data()
print("=" * 60)
print(" 演示1:成年男性,35岁")
print("=" * 60)
result = evaluate_person(35, '男', {
'身高(cm)': 175, '体重(kg)': 78, '体脂率(%)': 22,
'肺活量(ml)': 3500, '功率车': 38,
'握力(kg)': 42, '纵跳(cm)': 32,
'俯卧撑(次)': 25, '仰卧起坐(次/分)': 20,
'坐位体前屈(cm)': 8, '闭眼单脚站立(秒)': 35,
'选择反应时(秒)': 0.42
})
print(format_report(result))
print()
print("=" * 60)
print(" 演示2:幼儿女性,5岁")
print("=" * 60)
result2 = evaluate_person(5, '女', {
'身高(cm)': 110, '体重(kg)': 18,
'握力(kg)': 5, '立定跳远(cm)': 80,
'坐位体前屈(cm)': 6, '双脚连续跳(秒)': 7.5,
'15米绕障碍跑(秒)': 10.5, '走平衡木(秒)': 12
})
print(format_report(result2))
print()
print("=" * 60)
print(" 演示3:老年男性,68岁")
print("=" * 60)
result3 = evaluate_person(68, '男', {
'身高(cm)': 168, '体重(kg)': 70, '体脂率(%)': 24,
'肺活量(ml)': 2500, '2分钟高抬腿(次)': 80,
'握力(kg)': 32, '坐位体前屈(cm)': -2,
'30秒坐站(次)': 15, '闭眼单脚站立(秒)': 12,
'选择反应时(秒)': 0.65
})
print(format_report(result3))
elif len(sys.argv) > 1 and sys.argv[1] in ('--interactive', '-i'):
interactive_eval()
else:
print("用法:")
print(" python3 fitness_eval.py -d # 运行演示模式")
print(" python3 fitness_eval.py -i # 交互式评测")
print(" python3 -c \"import fitness_eval; print(fitness_eval.format_report(fitness_eval.evaluate_person(35, '男', {...})))\"")