Merge branch 'main' of http://10.10.14.176:3000/huanghai/YunNanProject
This commit is contained in:
350
Model/EducationDataModel.py
Normal file
350
Model/EducationDataModel.py
Normal file
@@ -0,0 +1,350 @@
|
||||
import os
|
||||
import json
|
||||
|
||||
|
||||
class EducationDataModel:
|
||||
# 定义支持的教育阶段映射
|
||||
EDUCATION_STAGES = {
|
||||
'preschool': '学前',
|
||||
'primary': '小学',
|
||||
'junior': '初中',
|
||||
'senior': '高中',
|
||||
'vocational': '中职'
|
||||
}
|
||||
|
||||
# 获取脚本所在目录的绝对路径
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
# 使用绝对路径定义数据目录和文件
|
||||
DATA_DIR = os.path.join(BASE_DIR, '../Data')
|
||||
SCHOOL_COUNT_FILE = os.path.join(DATA_DIR, 'SchoolCount.json')
|
||||
TEACHER_COUNT_FILE = os.path.join(DATA_DIR, 'TeacherCount.json')
|
||||
ENROLLMENT_COUNT_FILE = os.path.join(DATA_DIR, 'ZaiXiaoShengCount.json')
|
||||
ADMISSION_COUNT_FILE = os.path.join(DATA_DIR, 'ZhaoShengCount.json')
|
||||
POPULATION_FILE = os.path.join(DATA_DIR, 'RenKou.json') # 新增:人口数据文件路径
|
||||
|
||||
@staticmethod
|
||||
def load_json_file(file_path):
|
||||
"""加载JSON文件数据"""
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
except Exception as e:
|
||||
print(f"加载文件 {file_path} 失败: {e}")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_province_data(data):
|
||||
"""从数据中提取云南省的数据"""
|
||||
if not data or not isinstance(data, list):
|
||||
return None
|
||||
|
||||
# 查找云南省的数据
|
||||
for item in data:
|
||||
if item.get('area_name') == '云南省':
|
||||
return item
|
||||
|
||||
print("未找到云南省的数据")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_school_count_by_year_and_stage(year, stage):
|
||||
"""获取指定年份和学段的学校总数"""
|
||||
school_data = EducationDataModel.load_json_file(EducationDataModel.SCHOOL_COUNT_FILE)
|
||||
province_data = EducationDataModel.get_province_data(school_data)
|
||||
|
||||
if not province_data:
|
||||
return 0
|
||||
|
||||
# 根据学段获取对应的学校数据字段名
|
||||
stage_field_mapping = {
|
||||
'preschool': 'preschool_schools',
|
||||
'primary': 'primary_schools',
|
||||
'junior': 'junior_schools',
|
||||
'senior': 'senior_schools',
|
||||
'vocational': 'vocational_schools'
|
||||
}
|
||||
|
||||
field_name = stage_field_mapping.get(stage)
|
||||
if not field_name or field_name not in province_data:
|
||||
print(f"学段 {stage} 对应的学校数据字段不存在")
|
||||
return 0
|
||||
|
||||
year_data = province_data[field_name].get(str(year))
|
||||
if not year_data:
|
||||
print(f"年份 {year} 的学校数据不存在")
|
||||
return 0
|
||||
|
||||
# 计算学校总数 - 处理vocational_schools的特殊格式
|
||||
try:
|
||||
# 对于vocational学段,数据可能是直接的数值而不是字典
|
||||
if stage == 'vocational' and isinstance(year_data, int):
|
||||
return year_data
|
||||
# 对于其他学段或正常字典格式
|
||||
elif isinstance(year_data, dict):
|
||||
if 'urban' in year_data and 'town' in year_data and 'rural' in year_data:
|
||||
total_schools = year_data.get('urban', 0) + year_data.get('town', 0) + year_data.get('rural', 0)
|
||||
return total_schools
|
||||
elif 'total' in year_data:
|
||||
# 如果没有分区数据但有total字段,使用total
|
||||
return year_data.get('total', 0)
|
||||
else:
|
||||
print(f"年份 {year} 的学校数据缺少必要的字段")
|
||||
return 0
|
||||
else:
|
||||
print(f"年份 {year} 的学校数据格式不正确")
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f"计算学校总数时出错: {e}")
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def get_teacher_count_by_year_and_stage(year, stage):
|
||||
"""获取指定年份和学段的教职工总数"""
|
||||
teacher_data = EducationDataModel.load_json_file(EducationDataModel.TEACHER_COUNT_FILE)
|
||||
province_data = EducationDataModel.get_province_data(teacher_data)
|
||||
|
||||
if not province_data:
|
||||
return 0
|
||||
|
||||
# 根据学段获取对应的教职工数据字段名
|
||||
stage_field_mapping = {
|
||||
'preschool': 'preschool_teachers',
|
||||
'primary': 'primary_teachers',
|
||||
'junior': 'junior_teachers',
|
||||
'senior': 'senior_teachers',
|
||||
'vocational': 'vocational_teachers'
|
||||
}
|
||||
|
||||
field_name = stage_field_mapping.get(stage)
|
||||
if not field_name or field_name not in province_data:
|
||||
print(f"学段 {stage} 对应的教职工数据字段不存在")
|
||||
return 0
|
||||
|
||||
year_data = province_data[field_name].get(str(year))
|
||||
if not year_data or not isinstance(year_data, dict):
|
||||
print(f"年份 {year} 的教职工数据不存在或格式不正确")
|
||||
return 0
|
||||
|
||||
# 优先使用total_staff,如果为0或不存在,则返回教师总数
|
||||
try:
|
||||
total_staff = year_data.get('total_staff', 0)
|
||||
if total_staff == 0:
|
||||
total_staff = year_data.get('total_teacher', 0)
|
||||
return total_staff
|
||||
except Exception as e:
|
||||
print(f"获取教职工总数时出错: {e}")
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def get_enrollment_count_by_year_and_stage(year, stage):
|
||||
"""获取指定年份和学段的在校生总数"""
|
||||
enrollment_data = EducationDataModel.load_json_file(EducationDataModel.ENROLLMENT_COUNT_FILE)
|
||||
province_data = EducationDataModel.get_province_data(enrollment_data)
|
||||
|
||||
if not province_data or 'student_data' not in province_data:
|
||||
print("未找到学生数据")
|
||||
return 0
|
||||
|
||||
stage_data = province_data['student_data'].get(stage)
|
||||
if not stage_data:
|
||||
print(f"学段 {stage} 的学生数据不存在")
|
||||
return 0
|
||||
|
||||
year_data = stage_data.get(str(year))
|
||||
if not year_data or not isinstance(year_data, dict):
|
||||
print(f"年份 {year} 的学生数据不存在或格式不正确")
|
||||
return 0
|
||||
|
||||
# 返回在校生总数 - 处理vocational数据只有total字段的情况
|
||||
try:
|
||||
if 'urban' in year_data and 'town' in year_data and 'rural' in year_data:
|
||||
# 通过累加城区、镇区、乡村数据计算总数
|
||||
total_enrollment = year_data.get('urban', 0) + year_data.get('town', 0) + year_data.get('rural', 0)
|
||||
return total_enrollment
|
||||
elif 'total' in year_data and stage == 'vocational':
|
||||
# 对于vocational学段,如果只有total字段,使用total
|
||||
return year_data.get('total', 0)
|
||||
else:
|
||||
# 对于其他学段,如果没有分区数据,直接报错
|
||||
print(f"年份 {year} 的学生数据缺少城区、镇区或乡村字段")
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f"获取在校生总数时出错: {e}")
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def get_admission_count_by_year_and_stage(year, stage):
|
||||
"""获取指定年份和学段的招生总数"""
|
||||
admission_data = EducationDataModel.load_json_file(EducationDataModel.ADMISSION_COUNT_FILE)
|
||||
province_data = EducationDataModel.get_province_data(admission_data)
|
||||
|
||||
if not province_data or 'education_data' not in province_data:
|
||||
print("未找到教育数据")
|
||||
return 0
|
||||
|
||||
stage_data = province_data['education_data'].get(stage)
|
||||
if not stage_data:
|
||||
print(f"学段 {stage} 的教育数据不存在")
|
||||
return 0
|
||||
|
||||
year_data = stage_data.get(str(year))
|
||||
if not year_data or not isinstance(year_data, dict):
|
||||
print(f"年份 {year} 的教育数据不存在或格式不正确")
|
||||
return 0
|
||||
|
||||
# 返回招生总数 - 处理vocational数据只有total字段的情况
|
||||
try:
|
||||
if 'urban' in year_data and 'town' in year_data and 'rural' in year_data:
|
||||
# 通过累加城区、镇区、乡村数据计算总数
|
||||
total_admission = year_data.get('urban', 0) + year_data.get('town', 0) + year_data.get('rural', 0)
|
||||
return total_admission
|
||||
elif 'total' in year_data and stage == 'vocational':
|
||||
# 对于vocational学段,如果只有total字段,使用total
|
||||
return year_data.get('total', 0)
|
||||
else:
|
||||
# 对于其他学段,如果没有分区数据,直接报错
|
||||
print(f"年份 {year} 的教育数据缺少城区、镇区或乡村字段")
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f"获取招生总数时出错: {e}")
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def get_education_data_by_year(year):
|
||||
"""获取指定年份所有学段的教育数据"""
|
||||
result = []
|
||||
|
||||
for stage_code, stage_name in EducationDataModel.EDUCATION_STAGES.items():
|
||||
# 获取各类数据(增加异常处理)
|
||||
try:
|
||||
school_count = EducationDataModel.get_school_count_by_year_and_stage(year, stage_code)
|
||||
teacher_count = EducationDataModel.get_teacher_count_by_year_and_stage(year, stage_code)
|
||||
enrollment_count = EducationDataModel.get_enrollment_count_by_year_and_stage(year, stage_code)
|
||||
admission_count = EducationDataModel.get_admission_count_by_year_and_stage(year, stage_code)
|
||||
|
||||
# 转换为万人单位
|
||||
teacher_count_10k = round(teacher_count / 10000, 2) if teacher_count else 0
|
||||
enrollment_count_10k = round(enrollment_count / 10000, 2) if enrollment_count else 0
|
||||
admission_count_10k = round(admission_count / 10000, 2) if admission_count else 0
|
||||
|
||||
# 添加到结果列表
|
||||
result.append({
|
||||
'education_stage': stage_name,
|
||||
'school_count': school_count,
|
||||
'teacher_count_10k': teacher_count_10k,
|
||||
'enrollment_count_10k': enrollment_count_10k,
|
||||
'admission_count_10k': admission_count_10k
|
||||
})
|
||||
except Exception as e:
|
||||
print(f"处理学段 {stage_name} 时出错: {e}")
|
||||
# 出错时添加一个默认的空数据条目
|
||||
result.append({
|
||||
'education_stage': stage_name,
|
||||
'school_count': 0,
|
||||
'teacher_count_10k': 0,
|
||||
'enrollment_count_10k': 0,
|
||||
'admission_count_10k': 0
|
||||
})
|
||||
|
||||
return result
|
||||
|
||||
# 新增:人口数据相关方法
|
||||
@staticmethod
|
||||
def get_population_data_by_year(year):
|
||||
"""获取指定年份的云南省人口数据"""
|
||||
# 加载人口数据文件
|
||||
population_data = EducationDataModel.load_json_file(EducationDataModel.POPULATION_FILE)
|
||||
# 获取云南省数据
|
||||
yunnan_data = EducationDataModel.get_province_data(population_data)
|
||||
|
||||
if not yunnan_data:
|
||||
print(f"未获取到云南省{year}年的人口数据")
|
||||
return None
|
||||
|
||||
year_str = str(year)
|
||||
result = {
|
||||
'year': year,
|
||||
'total_population': yunnan_data.get('total_population', {}).get(year_str, 0), # 万人
|
||||
'birth_population': yunnan_data.get('birth_population', {}).get(year_str, 0), # 原数据单位为个
|
||||
'urban_population': yunnan_data.get('urban_population', {}).get(year_str, 0), # 万人
|
||||
'rural_population': yunnan_data.get('rural_population', {}).get(year_str, 0), # 万人
|
||||
'urbanization_rate': yunnan_data.get('urbanization_rate', {}).get(year_str, 0) # %
|
||||
}
|
||||
|
||||
# 将新生人口转换为万人(如果是整数)
|
||||
if isinstance(result['birth_population'], int):
|
||||
result['birth_population'] = round(result['birth_population'] / 10000, 1)
|
||||
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def print_population_data(population_data):
|
||||
"""打印人口数据"""
|
||||
if not population_data:
|
||||
print("没有可打印的人口数据")
|
||||
return
|
||||
|
||||
year = population_data.get('year', '未知')
|
||||
print(f"云南省 {year}年人口概览")
|
||||
print("-------------------")
|
||||
print(f"总人口 {population_data.get('total_population', 0):,} 万人")
|
||||
print(f"新生人口 {population_data.get('birth_population', 0)} 万人")
|
||||
print(f"城镇人口 {population_data.get('urban_population', 0):,} 万人")
|
||||
print(f"乡村人口 {population_data.get('rural_population', 0):,} 万人")
|
||||
print(f"城镇化率 {population_data.get('urbanization_rate', 0):.2f} %")
|
||||
|
||||
# 验证数据一致性
|
||||
total = population_data.get('total_population', 0)
|
||||
urban_rural_sum = population_data.get('urban_population', 0) + population_data.get('rural_population', 0)
|
||||
if abs(total - urban_rural_sum) > 0.01:
|
||||
print("\n注意:总人口与城镇人口+乡村人口存在差异")
|
||||
print(f"差异值: {abs(total - urban_rural_sum):.2f} 万人")
|
||||
|
||||
@staticmethod
|
||||
def print_education_data(data, year):
|
||||
"""打印教育数据"""
|
||||
print(f"\n===== {year}年云南省各学段教育数据统计(单位:学校总数-个,其余-万人)=====")
|
||||
print(f"{'学段':<8}{'学校总数':<10}{'教职工总数':<12}{'在校生总数':<12}{'招生总数':<12}")
|
||||
print("=" * 60)
|
||||
|
||||
for item in data:
|
||||
print(f"{item['education_stage']:<8}{item['school_count']:<10}{item['teacher_count_10k']:<12.2f}{item['enrollment_count_10k']:<12.2f}{item['admission_count_10k']:<12.2f}")
|
||||
|
||||
@staticmethod
|
||||
def main(year=2023):
|
||||
"""主函数"""
|
||||
try:
|
||||
print(f"正在获取 {year} 年数据...")
|
||||
print(f"数据目录: {EducationDataModel.DATA_DIR}")
|
||||
|
||||
# 检查数据文件是否存在
|
||||
for file_path in [EducationDataModel.SCHOOL_COUNT_FILE,
|
||||
EducationDataModel.TEACHER_COUNT_FILE,
|
||||
EducationDataModel.ENROLLMENT_COUNT_FILE,
|
||||
EducationDataModel.ADMISSION_COUNT_FILE,
|
||||
EducationDataModel.POPULATION_FILE]: # 新增:检查人口数据文件
|
||||
if os.path.exists(file_path):
|
||||
print(f"找到数据文件: {file_path}")
|
||||
else:
|
||||
print(f"警告:数据文件不存在: {file_path}")
|
||||
|
||||
# 获取并打印教育数据
|
||||
education_data = EducationDataModel.get_education_data_by_year(year)
|
||||
if education_data:
|
||||
EducationDataModel.print_education_data(education_data, year)
|
||||
else:
|
||||
print("未获取到任何教育数据")
|
||||
|
||||
# 新增:获取并打印人口数据
|
||||
print("\n" + "=" * 50)
|
||||
population_data = EducationDataModel.get_population_data_by_year(year)
|
||||
EducationDataModel.print_population_data(population_data)
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取数据时发生错误: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
EducationDataModel.main()
|
@@ -1,14 +1,15 @@
|
||||
import json
|
||||
from pyecharts import options as opts
|
||||
from pyecharts.charts import Bar, Line
|
||||
from pyecharts.globals import CurrentConfig
|
||||
|
||||
from Config.Config import ONLINE_HOST
|
||||
|
||||
CurrentConfig.ONLINE_HOST = ONLINE_HOST
|
||||
|
||||
|
||||
class RuYuanZaiYuanModel:
|
||||
# 定义支持的教育阶段映射
|
||||
EDUCATION_STAGES = {
|
||||
'preschool': '学前',
|
||||
'primary': '小学',
|
||||
'junior': '初中',
|
||||
'senior': '高中'
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def load_student_data():
|
||||
try:
|
||||
@@ -26,7 +27,11 @@ class RuYuanZaiYuanModel:
|
||||
return [], []
|
||||
|
||||
@staticmethod
|
||||
def generate_preschool_education_config():
|
||||
def generate_preschool_education_config(education_stage='preschool'):
|
||||
# 验证教育阶段参数
|
||||
if education_stage not in RuYuanZaiYuanModel.EDUCATION_STAGES:
|
||||
education_stage = 'preschool' # 默认使用学前
|
||||
|
||||
# 获取学前教育相关数据
|
||||
enrollment_data, in_school_data = RuYuanZaiYuanModel.load_student_data()
|
||||
# 提取云南省级数据
|
||||
@@ -39,17 +44,18 @@ class RuYuanZaiYuanModel:
|
||||
urban_data = [] # 城区数据
|
||||
town_data = [] # 镇区数据
|
||||
rural_data = [] # 乡村数据
|
||||
total_enroll = [] # 总入园数
|
||||
total_enroll = [] # 总人数
|
||||
|
||||
# 提取年份数据(2015-2024)
|
||||
years = [str(year) for year in range(2015, 2025)]
|
||||
|
||||
for year in years:
|
||||
enroll_data = yunnan_enroll["education_data"]["preschool"].get(year, {})
|
||||
# 使用传入的教育阶段参数
|
||||
enroll_data = yunnan_enroll["education_data"].get(education_stage, {}).get(year, {})
|
||||
urban_data.append(enroll_data.get("urban", 0) / 10000) # 转换为万人
|
||||
town_data.append(enroll_data.get("town", 0) / 10000) # 转换为万人
|
||||
rural_data.append(enroll_data.get("rural", 0) / 10000) # 转换为万人
|
||||
# 计算总和作为总入园数,而非使用文件中的total字段
|
||||
# 计算总和作为总人数
|
||||
calculated_total = enroll_data.get("urban", 0) + enroll_data.get("town", 0) + enroll_data.get("rural", 0)
|
||||
total_enroll.append(calculated_total / 10000) # 转换为万人
|
||||
|
||||
@@ -57,7 +63,7 @@ class RuYuanZaiYuanModel:
|
||||
base_year = "2022"
|
||||
# 找到2022年在years中的索引位置
|
||||
base_index = years.index(base_year) if base_year in years else 0
|
||||
# 获取2022年的总入园数作为基数
|
||||
# 获取2022年的总人数作为基数
|
||||
base_value = total_enroll[base_index] if base_index < len(total_enroll) else 0
|
||||
# 创建2022年基数折线数据(2022-2024年)
|
||||
base_2022_line = []
|
||||
@@ -67,18 +73,24 @@ class RuYuanZaiYuanModel:
|
||||
base_2022_line.append(base_value)
|
||||
else:
|
||||
base_2022_line.append(None) # 2022年之前不显示
|
||||
data = {"xAxis_data": years,
|
||||
"series_data_0": urban_data, # 城区
|
||||
"series_data_1": town_data, # 镇区
|
||||
"series_data_2": rural_data, # 乡村
|
||||
"series_data_3": total_enroll, # 总入园数
|
||||
"series_data_4": base_2022_line # 2022年基数
|
||||
}
|
||||
data = {
|
||||
"xAxis_data": years,
|
||||
"series_data_0": urban_data, # 城区
|
||||
"series_data_1": town_data, # 镇区
|
||||
"series_data_2": rural_data, # 乡村
|
||||
"series_data_3": total_enroll, # 总人数
|
||||
"series_data_4": base_2022_line, # 2022年基数
|
||||
"education_stage": RuYuanZaiYuanModel.EDUCATION_STAGES.get(education_stage, '学前') # 添加教育阶段名称
|
||||
}
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def generate_in_school_education_config():
|
||||
# 获取学前教育相关数据
|
||||
def generate_in_school_education_config(education_stage='preschool'):
|
||||
# 验证教育阶段参数
|
||||
if education_stage not in RuYuanZaiYuanModel.EDUCATION_STAGES:
|
||||
education_stage = 'preschool' # 默认使用学前
|
||||
|
||||
# 获取在校生相关数据
|
||||
enrollment_data, in_school_data = RuYuanZaiYuanModel.load_student_data()
|
||||
|
||||
# 提取云南省级数据
|
||||
@@ -90,15 +102,15 @@ class RuYuanZaiYuanModel:
|
||||
# 提取年份数据(2015-2024)
|
||||
years = [str(year) for year in range(2015, 2025)]
|
||||
|
||||
# 构建学前教育数据
|
||||
# 构建数据
|
||||
urban_data = [] # 城区数据
|
||||
town_data = [] # 镇区数据
|
||||
rural_data = [] # 乡村数据
|
||||
total_in_school = [] # 总在园数
|
||||
total_in_school = [] # 总人数
|
||||
|
||||
for year in years:
|
||||
# 将education_data改为student_data
|
||||
in_school_year_data = yunnan_in_school["student_data"]["preschool"].get(year, {})
|
||||
# 使用传入的教育阶段参数
|
||||
in_school_year_data = yunnan_in_school["student_data"].get(education_stage, {}).get(year, {})
|
||||
|
||||
# 先获取原始数据
|
||||
urban_val = in_school_year_data.get("urban", 0)
|
||||
@@ -118,7 +130,7 @@ class RuYuanZaiYuanModel:
|
||||
base_year = "2022"
|
||||
# 找到2022年在years中的索引位置
|
||||
base_index = years.index(base_year) if base_year in years else 0
|
||||
# 获取2022年的总在园数作为基数
|
||||
# 获取2022年的总人数作为基数
|
||||
base_value = total_in_school[base_index] if base_index < len(total_in_school) else 0
|
||||
# 创建2022年基数折线数据(2022-2024年)
|
||||
base_2022_line = []
|
||||
@@ -135,7 +147,8 @@ class RuYuanZaiYuanModel:
|
||||
"series_data_1": town_data,
|
||||
"series_data_2": rural_data,
|
||||
"series_data_3": total_in_school,
|
||||
"series_data_4": base_2022_line
|
||||
"series_data_4": base_2022_line,
|
||||
"education_stage": RuYuanZaiYuanModel.EDUCATION_STAGES.get(education_stage, '学前') # 添加教育阶段名称
|
||||
}
|
||||
|
||||
return data
|
||||
|
BIN
Model/__pycache__/EducationDataModel.cpython-310.pyc
Normal file
BIN
Model/__pycache__/EducationDataModel.cpython-310.pyc
Normal file
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user