基于Python的山东省2025年高考志愿投档数据可视化分析系统:从数据到洞察的完整实践

一文带你从零搭建、运行并扩展一个针对山东省2025年高考志愿投档数据的可视化分析系统。包含技术栈选型、目录结构解析、核心代码讲解、可视化方案与扩展预留、部署上线与性能优化建议。

目录

  • 一、项目背景与目标
  • 二、技术栈与方案选型
  • 三、项目目录结构解析
  • 四、核心数据模型与导入流程
  • 五、视图与路由:从数据到页面
  • 六、可视化展示:前端 ECharts 与后端 pyecharts 双栈
  • 七、运行与部署
  • 八、数据校验与质量评估
  • 九、性能优化与安全实践
  • 十、可扩展性设计与后续规划
  • 十一、致谢与版权说明
  • 十二、联系方式

🥰 项目源码获取,码界筑梦坊各平台同名,博客底部含联系方式卡片,欢迎咨询!

一、项目背景与目标

  • 背景:高考志愿投档数据量大、维度多,人工梳理难以快速获取洞察。本系统聚焦山东省2025年投档数据,提供面向院校、专业、地区、分数段的多维分析与可视化能力。
  • 目标
    • 汇聚真实投档数据,统一清洗与入库
    • 提供可检索、可筛选、可视化的交互式分析页面
    • 支持按分数定位潜在院校,辅助决策
    • 预留二次开发与图表扩展示例

二、技术栈与方案选型

  • 后端:Django 5(MVT 架构,ORM 快速开发),MySQL 8(事务与索引优化)
  • 数据处理:pandas、numpy、openpyxl、xlrd
  • 可视化
    • 前端:ECharts 5(柱状图、饼图、地图、词云等)
    • 后端:pyecharts(服务端生成可嵌入 HTML 的图表片段)
  • 管理后台:django-simpleui(美化 Django Admin)
  • 运维与工具:redis(可选缓存)、pytest/pytest-django、flake8/black、Sphinx

依赖见 requirements.txt

Django==5.0.4
mysqlclient==2.2.0
django-simpleui==2024.11.15
pyecharts==2.0.4
pandas==2.1.4
numpy==1.24.3
openpyxl==3.1.2
xlrd==2.0.1
django-debug-toolbar==4.2.0
django-compressor==4.4
redis==5.0.1
Pillow==10.1.0
python-dotenv==1.0.0
pytest==7.4.3
pytest-django==4.7.0
flake8==6.1.0
black==23.11.0
Sphinx==7.2.6

三、项目目录结构解析

code/
├── data/                          # 数据文件目录
│   ├── 2025年山东高考成绩一分一段表.csv
│   ├── 2025年山东高考成绩一分一段表.xls
│   ├── 山东省2025年普通类常规批第1次志愿投档情况表.csv
│   ├── 山东省2025年普通类常规批第1次志愿投档情况表.xlsx
│   └── 山东省2025年高考志愿投档数据分析可视化.ipynb
├── gaokao/                        # 主要业务应用
│   ├── admin.py                   # 后台管理配置
│   ├── models.py                  # 数据模型(分数段/投档/系统日志)
│   ├── urls.py                    # 模块路由
│   ├── views.py                   # 视图与统计逻辑
│   ├── utils.py                   # 工具与日志记录
│   └── management/commands/
│       └── import_gaokao_data.py  # 一键导入CSV数据
├── gaokao_project/                # 项目配置
│   ├── settings.py                # Django 设置(数据库、静态、SimpleUI等)
│   └── urls.py                    # 全局路由
├── templates/                     # 模板
│   ├── base.html
│   └── gaokao/                    # 各功能页(仪表板/分析/搜索等)
├── html/                          # 静态资源(CSS/JS/IMG/第三方模块)
├── static/                        # 静态文件(补充)
├── check_stats.py                 # 数据校验脚本
├── manage.py
├── README.md
└── requirements.txt

四、核心数据模型与导入流程

4.1 数据模型(精简片段)

# gaokao/models.py
from django.db import models

class ScoreSegment(models.Model):
    score = models.IntegerField("分数", db_index=True)
    count = models.IntegerField("全体本段人数")
    total = models.IntegerField("全体累计人数")
    # 选考科目(物理/化学/生物/政治/历史/地理)计数与累计
    phy_count = models.IntegerField(null=True, blank=True)
    chem_count = models.IntegerField(null=True, blank=True)
    bio_count = models.IntegerField(null=True, blank=True)
    pol_count = models.IntegerField(null=True, blank=True)
    his_count = models.IntegerField(null=True, blank=True)
    geo_count = models.IntegerField(null=True, blank=True)

class Admission(models.Model):
    college = models.CharField(max_length=128)       # 院校名称
    major = models.CharField(max_length=128, null=True, blank=True)
    plan = models.IntegerField(null=True, blank=True) # 投档计划数
    min_rank = models.IntegerField(null=True, blank=True)
    province = models.CharField(max_length=50, null=True, blank=True)
    is_985 = models.CharField(max_length=10, null=True, blank=True)
    is_211 = models.CharField(max_length=10, null=True, blank=True)
    is_double = models.CharField(max_length=10, null=True, blank=True) # 双一流

4.2 一键导入数据(CSV)

# gaokao/management/commands/import_gaokao_data.py
class Command(BaseCommand):
    help = '自动导入高考一分一段表和投档情况表数据(包含所有字段)'

    def handle(self, *args, **options):
        # 清空旧数据
        ScoreSegment.objects.all().delete()
        Admission.objects.all().delete()
        # 导入一分一段表与投档表 ...(略)

执行导入:

python manage.py import_gaokao_data

五、视图与路由:从数据到页面

5.1 典型视图:投档情况分析

# gaokao/views.py(节选)
from django.db.models import Count, Sum

def admission_analysis(request):
    admissions = Admission.objects.all().order_by('-plan')[:100]
    type_stats = Admission.objects.values('type').annotate(count=Count('id')).order_by('-count')[:10]
    province_stats = (Admission.objects.values('province')
                      .annotate(college_count=Count('college', distinct=True), total_plan=Sum('plan'))
                      .filter(province__isnull=False).exclude(province='')
                      .order_by('-total_plan')[:15])
    return render(request, 'gaokao/admission_analysis.html', {
        'admissions': admissions,
        'type_stats': type_stats,
        'province_stats': province_stats,
    })

5.2 AJAX 检索:院校搜索

# gaokao/views.py(节选)
from django.http import JsonResponse
from django.db.models import Q

def ajax_college_search(request):
    query = request.GET.get('q', '')
    results = []
    if query and len(query) >= 2:
        colleges = Admission.objects.filter(
            Q(college__icontains=query) | Q(ename__icontains=query) | Q(province__icontains=query) | Q(city__icontains=query)
        ).values('college', 'ename', 'province', 'city')[:100]
        # 后端去重(兼容不同数据库)
        seen = set()
        for c in colleges:
            name = c['college']
            if name and name not in seen:
                seen.add(name)
                results.append({
                    'college': name,
                    'ename': c['ename'] or '',
                    'province': c['province'] or '',
                    'city': c['city'] or '',
                })
            if len(results) >= 10:
                break
    return JsonResponse({'results': results})

5.3 路由总览

# gaokao/urls.py(节选)
urlpatterns = [
    path('', views.dashboard, name='dashboard'),
    path('score_distribution/', views.score_distribution, name='score_distribution'),
    path('admission_analysis/', views.admission_analysis, name='admission_analysis'),
    path('college_search/', views.college_search, name='college_search'),
    path('ajax_college_search/', views.ajax_college_search, name='ajax_college_search'),
    path('score_analysis/', views.score_analysis, name='score_analysis'),
    path('subject_analysis/', views.subject_analysis, name='subject_analysis'),
    path('college_ranking/', views.college_ranking, name='college_ranking'),
    path('region_analysis/', views.region_analysis, name='region_analysis'),
    path('major_analysis/', views.major_analysis, name='major_analysis'),
]

六、可视化展示:前端 ECharts 与后端 pyecharts 双栈

系统同时支持两种可视化方式,满足「高交互」与「低耦合服务端渲染」两类场景。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.1 前端 ECharts(模板中脚本渲染)

示例页面:templates/gaokao/score_distribution.html(预留方案,可按需新建)

<div id="scoreBar" style="width:100%;height:420px;"></div>
<script src="/html/assets/modules/echarts/echarts.min.js"></script>
<script>
  const scores = {{ scores|safe }};
  const counts = {{ counts|safe }};
  const chart = echarts.init(document.getElementById('scoreBar'));
  chart.setOption({
    title: { text: '分数段人数分布', left: 'center' },
    tooltip: { trigger: 'axis' },
    xAxis: { type: 'category', data: scores },
    yAxis: { type: 'value' },
    dataZoom: [{ type: 'slider' }, { type: 'inside' }],
    series: [{ name: '人数', type: 'bar', data: counts, itemStyle: { color: '#5470C6' } }]
  });
  window.addEventListener('resize', () => chart.resize());
</script>
  • 数据来源:视图 score_distributionscorescounts 注入模板上下文
  • 交互增强:开启 dataZoomtooltip 与自适应 resize

6.2 后端 pyecharts(服务端生成 HTML 片段)

示例:热门专业词云(已在 views.major_analysis 使用)

from pyecharts.charts import WordCloud
from pyecharts import options as opts

words = [(item['major'], item['total_plan']) for item in hot_majors_extended]
wordcloud = (
    WordCloud()
    .add('', words, word_size_range=[15, 100], shape='circle')
    .set_global_opts(title_opts=opts.TitleOpts(title='专业热度词云', pos_left='center'))
)
wordcloud_html = wordcloud.render_embed()  # 在模板中 {{ wordcloud_html|safe }}

模板中直接嵌入:

<div class="card">
  <div class="card-header">专业热度词云</div>
  <div class="card-body">{{ wordcloud_html|safe }}</div>
</div>

6.3 地图可视化(预留)

  • 方案一:ECharts Map,加载中国地图 JSON,渲染省份投档计划合计
  • 方案二:pyecharts Map,后端生成,适用于导出报表

预留模板片段:

<div id="provinceMap" style="width:100%;height:520px;"></div>
<script>
  const mapData = [
    // 示例:[{ name: '山东', value: 12345 }, ...]
    {{ province_stats|safe }}
  ];
  // TODO: 按需加载地图 JSON 并完成 setOption
</script>

七、运行与部署

7.1 本地运行

git clone <repo>
cd code
python -m venv venv
venv\Scripts\activate   # Windows
pip install -r requirements.txt

# 创建数据库(示例)
# MySQL 中创建 design_281_gaokao 并配置 settings.py 连接
python manage.py makemigrations && python manage.py migrate
python manage.py createsuperuser
python manage.py import_gaokao_data
python manage.py runserver

访问:http://localhost:8000/(前台) 与 http://localhost:8000/admin/(后台)

7.2 生产部署(示例)

pip install gunicorn
gunicorn gaokao_project.wsgi:application --bind 0.0.0.0:8000

Nginx 反向代理(示例):

server {
  listen 80;
  server_name your_domain.com;
  location /static/ { alias /path/to/project/static/; }
  location / { proxy_pass http://127.0.0.1:8000; }
}

八、数据校验与质量评估

运行 check_stats.py 进行快速体检:

python check_stats.py

示例输出关注点:总院校数、985/211/双一流数量、字段值分布,辅助识别异常值与缺失。

九、性能优化与安全实践

  • 数据库:关键字段加索引(如 ScoreSegment.score)、避免 N+1、使用聚合查询
  • 缓存:热门统计与下拉列表可使用 redis 缓存
  • 前端:图表按需加载、CDN 静态资源、异步加载与懒加载
  • 安全:启用 CSRF、精简密码验证器但保留最小长度,管理端仅对内网或白名单开放
  • 部署:收集静态文件、合理的 ALLOWED_HOSTS、关闭 DEBUG

十、可扩展性设计与后续规划

  • 更多图表:趋势对比(历年分数线)、桑基图(专业去向)、箱线图(分布概览)
  • 智能推荐:结合位次与科目偏好给出院校/专业组合建议
  • 导出能力:导出 Excel/PDF 报告
  • 多数据源:支持 CSV/Excel/API 多源对接与增量更新

预留接口(示例):

# gaokao/views.py(建议新增)
def api_statistics(request):
    """统一统计 API:供前端跨页图表按需拉取"""
    # TODO: 聚合 Admission / ScoreSegment 并返回 JSON
    return JsonResponse({"ok": True, "data": {}})

十一、致谢与版权说明

  • 本系统数据来自公开渠道,仅用于学习与研究,勿作商业用途。
  • 致谢 Django、ECharts、pyecharts、Bootstrap、SimpleUI 等优秀开源项目。

十二、联系方式

  • 码界筑梦坊各大平台同名

— 期待你的反馈与 Star,欢迎 Fork 与二开!

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐