十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

全国犯罪人数大数据可视化平台:从数据处理到ECharts大屏实战

全国犯罪人数大数据可视化平台:从数据处理到ECharts大屏实战 简介面向中国大学生计算机设计大赛数据可视化赛道参赛者一份全国犯罪人数大数据可视化平台源码可作为直接参考的国赛水平模板重点解决参赛者快速搭建数据展示与分析平台的需求。资源压缩包共114个文件大小仅约760KB包含1个HTML入口页面、14个JavaScript交互脚本、8个CSS样式表、5个JSON数据文件以及77张PNG图表素材和8个GIF动效文件类型覆盖页面构建、数据存储、视觉呈现与动态反馈整体目录紧凑便于二次修改与本地部署。资源以全国犯罪人数数据为样本不仅展示了数据可视化图表还包含对数据的统计分析与结论提炼可为政府和企业制定公共安全政策提供参考。目前已有358人学习浏览适合需要快速完成赛题原型、理解大屏可视化实现逻辑或提升作品完整度的学生与开发者。1. 全国犯罪人数大数据可视化平台的模块拆解从竞赛模板到可运行源码全国犯罪人数大数据可视化平台在一堆国赛模板里属于“看着低调但信息量很足”的一类选题。它把省级维度的犯罪统计、年份趋势、犯罪类型占比整合成一张完整的数据大屏评委会问的通常是三个问题数据从哪里来、指标如何计算、图表结果是否经得起问。这个压缩包源码的价值正好在这——它不是把 ECharts 示例堆在一起而是用统计口径把图层串起来适合数据可视化赛道、大数据毕业设计以及对“免费数据可视化大屏”有快速落地需求的开发者。拿到手第一件事不是打开 index.html而是先看数据目录。把数据结构和指标口径理清楚后面所有图表的参数都只是配置问题。2. 数据接入层重写犯罪人数事实表如何从静态 JSON 变成可聚合指标2.1 模板目录里被忽略的layui样式与数据入口解压后能看到 style.css、layer.css、layim.css、laydate.css 这一批文件它们是 layui 生态的控件样式真正承担可视化的是独立引入的 echarts.min.js。相比 reactts 的 data 大屏展示类项目动辄十几个 UI 组件这个模板已经算轻量但依然有一个常见误区把 layim.css、layer.css 不做裁剪直接引入首屏样式请求多主题定制时还会被默认样式干扰。我一般先把目录拆成 css/layui、js/lib、data、assets 四个子目录页面只保留用到的 layer 与 laydate 样式。数据文件在模板里通常不是crime_data.json就是平铺的data.js全局变量。全局变量的好处是双击 index.html 就能看坏处是“数据定义与展示逻辑耦合”。后续想换年度、按省份过滤都要改 JS 再刷新页面。为了达到“换数据不换页面”的效果我一般先把这类静态数据抽到data/目录再通过一个小工具脚本读取。2.2 事实表字段与统计口径“犯罪人数”这类指标不建议直接叫 crime_count。原始统计可能区分受案人数、审结人数、生效判决人数不同口径放到同一张图里会引发“图表本身没错但结论被质疑”的问题。先把表结构定清楚。字段名类型说明示例province_codeCHAR(2)省份编码用于地图映射44province_nameVARCHAR(16)省份名称必须与 GeoJSON 的 name 一致广东report_yearSMALLINT统计年份2023crime_typeVARCHAR(32)案件类型如盗窃、危险驾驶、诈骗盗窃person_countINT生效判决人数13582case_countINT案件数10244省份名称必须与 GeoJSON 里“广东省”写法一致否则地图 series data 匹配不上。我在实际排查里遇到最多的就是“地图显示出来但数据不上色”十有八九是 name 不一致。表里加 province_code 是为了后续做横向排序和区域编码关联不只是给地图用。2.2.1 原始数据清洗的关键细节原始 CSV 经常出现三种脏数据年份字段带“年”字、person_count 有空值、同一省份下名称写法不一致如“广西”与“广西壮族自治区”。清洗时统一对省份做别名映射年份转 int人数负数视为无效数据剔除。2.3 Python 清洗脚本大数据和 Python 的毕设里最常见的处理路径就是 pandas 清洗 echarts 展示这里给出可替换的清洗脚本。import pandas as pd df pd.read_csv(crime_raw.csv, encodingutf-8) df df.dropna(subset[province, year, crime_type, person_count]) df[person_count] df[person_count].astype(int) df df[df[person_count] 0] province_alias { 广西: 广西壮族自治区, 内蒙: 内蒙古自治区, 新疆维吾尔族自治区: 新疆维吾尔自治区, } df[province] df[province].replace(province_alias) df df[(df[year] 2015) (df[year] 2024)] df.to_parquet(crime_fact.parquet, indexFalse) print(df.groupby(crime_type)[person_count].sum().sort_values(ascendingFalse).head(10))这段脚本把文本数据规整成适合后续 SQL 查询的事实表。dropna只针对三列关键字段而非全部字段避免把有效行误删省份别名替换要在过滤年份之前做否则带别名的行会因为暂时缺省份信息被提前剔除。最后输出 parquet比 CSV 读写快字段类型也更稳定。如果 Python 环境太重直接写聚合 SQL 也行。按 province_name、report_year、crime_type 分组统计人数与案件数SELECT province_name, report_year, crime_type, SUM(person_count) AS person_count FROM crime_fact WHERE report_year BETWEEN 2015 AND 2024 GROUP BY province_name, report_year, crime_type ORDER BY person_count DESC;这个查询保留了省份、年份、类型三个维度正好对应模板里地图、年份筛选和堆叠柱状图。如果模板需要全量省份每年都有数据缺失年份要单独做CROSS JOIN补 0否则折线图会出现断点。2.4 输出 ECharts 可用的聚合 JSONECharts 的 map series 接收的是对象数组每项包含name、value。上一节的 SQL 结果还不能直接给图表用需要按图表维度转换。import json result {} for row in df.itertuples(): key f{row.province}_{row.report_year}_{row.crime_type} result[key] row.person_count with open(map_data.json, w, encodingutf-8) as f: json.dump([{name: r.province_name, value: r.person_count} for r in df[df.report_year 2023].itertuples()], f, ensure_asciiFalse)这里的逻辑是地图只需要年份维度所以先过滤目标年份雷达图和柱状图需要类型维度则单独生成另一份数组。不要期望一份 JSON 满足所有图表宁可多存几份结构清晰的中间文件也不要在前端再做二次聚合渲染性能会差很多。提示如果省份名称与 GeoJSON 不一致在 Python 里用 replace 映射最省事等到了 echarts 的 setOption 里再改名很容易遗漏联动场景。3. ECharts 地图、堆叠柱状图与仪表盘联动犯罪大屏的核心图表实现3.1 省级统计地图GeoJSON 注册与 visualMap 配置在 echarts 5 中中国地图不再默认内置必须先拿到 china.json或带南海诸岛版本的 GeoJSON并注册。import * as echarts from echarts; import chinaGeo from ./geo/china.json; echarts.registerMap(china, chinaGeo); const mapChart echarts.init(document.getElementById(crime-map)); mapChart.setOption({ tooltip: { trigger: item, formatter: (p) ${p.name}br/生效判决人数${Number(p.value || 0).toLocaleString()} }, visualMap: { min: 0, max: 20000, splitNumber: 5, inRange: { color: [#e8f1ff, #2f6fed, #102a56] }, textStyle: { color: #ccc } }, series: [{ type: map, map: china, roam: true, aspectScale: 0.85, label: { show: false }, emphasis: { label: { show: true, color: #fff } }, itemStyle: { areaColor: #1e3a5f, borderColor: #5b8ff9 }, data: window.crimeMapData }] });视觉映射里max不要写死而是取当年数据的 95 分位数。直接用真实最大值会出现“广东一省深色、其他省份全浅色”的画面。roam: true允许拖拽缩放方便答辩时放大西部省份如果页面还有年份 slider每切换一次年份要重新setOption并把max重算否则颜色区间不会跟随数据变化。3.2 犯罪类型堆叠柱状图和年份趋势折线堆叠柱状图比普通柱状图更能回答“总量变化 结构变化”两个问题。const stackChart echarts.init(document.getElementById(crime-stack)); stackChart.setOption({ tooltip: { trigger: axis }, legend: { type: scroll, bottom: 0, textStyle: { color: #aaa } }, xAxis: { type: category, data: [2019, 2020, 2021, 2022, 2023] }, yAxis: { type: value, name: 人数 }, series: [ { name: 盗窃, type: bar, stack: total, data: [3200, 2800, 2600, 2400, 2200] }, { name: 诈骗, type: bar, stack: total, data: [1200, 1500, 1800, 2100, 2600] }, { name: 危险驾驶, type: bar, stack: total, data: [900, 860, 820, 790, 740] } ] });stack: total是关键参数三个系列共用同一个 stack 名称ECharts 会以第一个系列为起点叠加。年份建议用字符串而不是时间对象否则 x 轴会自动补出缺失年份的占位和统计口径对不上。地区筛选时给每个系列重新传 data 数组即可不需要销毁图表实例。折线图与堆叠柱状图共用一份数据时要注意图例项别冲突。折线系列不要加stack字段加了会变成面积叠加图。3.3 综合安全指数仪表盘仪表盘在答辩环节是加分项因为它把“犯罪人数”这种负面指标转成直观的层级。const gaugeChart echarts.init(document.getElementById(crime-gauge)); gaugeChart.setOption({ series: [{ type: gauge, min: 0, max: 100, progress: { show: true, width: 18 }, axisLine: { lineStyle: { width: 18, color: [[0.6, #2f6fed], [0.9, #f5a623], [1, #d64541]] } }, data: [{ value: 72, name: 治安指数 }], detail: { formatter: {value} 分 }, title: { color: #888 } }] });指数不能拍脑袋定我在模板里通常把指数写成“非暴力犯罪占比 × 0.6 总量同比降幅 × 0.4”的组合指标在数据脚本里算好再赋给仪表盘。评委问指数定义时能讲清楚不会卡壳。axisLine的 color 二维数组定义分段颜色区间也从数据分布算不写死 60/90。下面是一张常用的图表配置速查表适合在改模板参数时对照。场景关键参数注意点省级地图map: china、visualMap.maxname 必须与 GeoJSON 一致堆叠柱状图stack: total图例顺序决定堆叠顺序仪表盘progress.show、axisLine.colorindex 定义要先能自圆其说3.4 组件联动与刷新真正把这几个组件连起来的是年份筛选器。比如用 laydate 绑定一个年份选择每次 change 后统一执行refreshAll(year)。function refreshAll(year) { const mapData getAggData(year, map); const typeData getAggData(year, type); mapChart.setOption({ visualMap: { max: quantile(mapData, 0.95) }, series: [{ data: mapData }] }); stackChart.setOption({ series: typeData.map((item, i) ({ data: item.values })) }); gaugeChart.setOption({ series: [{ data: [{ value: calcIndex(year) }] }] }); } laydate.render({ elem: #yearPicker, trigger: click, done: (value) refreshAll(Number(value)) });getAggData直接从本地 JSON 里读取对应年份数据而不是依赖后端接口。对于这种教材级模板没必要引入 axios。注意setOption第二次调用时没有传的配置项会沿用旧值所以只需传变化的 series如果强制notMerge: true会把 tooltip、legend 的细节配置也清掉我不建议在联动场景里开。4. 视觉主题治理把模板风格改成企业级数据可视化风格4.1 硬编码色值的隐藏成本模板默认 title 字体与柱状图颜色都是一处处写死的想从深蓝改成浅色分析台风格就得全文替换还容易漏掉 legend 和 tooltip。企业级数据可视化项目通常第一步就是把色板抽成变量。:root { --bg-primary: #0b1b2b; --bg-panel: rgba(21, 46, 78, 0.85); --brand-blue: #3a7afe; --accent-cyan: #22d3ee; --warn-orange: #f5a623; --text-main: #e6f1ff; --text-sub: #8aa0b5; }CSS 变量管住了页面背景、面板、标题字色。但 ECharts 实例内部的文字和系列颜色不认 CSS 变量必须在初始化前读取一次getComputedStyle把色值统一塞进 CHART_COLOR 常量。这样页面换肤时图表才跟得上。const cssVars getComputedStyle(document.documentElement); const CHART_COLOR { blue: cssVars.getPropertyValue(--brand-blue).trim(), cyan: cssVars.getPropertyValue(--accent-cyan).trim(), orange: cssVars.getPropertyValue(--warn-orange).trim() };色板变量与 ECharts 配置项的对应关系整理如下改样式时按表查即可。CSS 变量用途ECharts 对应项--brand-blue主导柱状图series.itemStyle.color--accent-cyan地图强调visualMap.inRange.color--warn-orange告警/异常仪表盘 axisLine 分段--text-sub辅助文案legend.textStyle.color4.2 深色大屏与浅色分析台的自动切换大屏答辩现场通常环境光暗深色背景不容易反光但老师可能导出 PDF 看报告浅色页面打印更清晰。我一般保留.theme-dark和.theme-light两套类挂在 body 上。function applyTheme(theme) { document.body.className theme-${theme}; refreshAll(selectedYear); }ECharts 实例初始化后textStyle.color是快照只切 CSS 不会改图表文字颜色。所以 refreshAll 里要把textStyle、backgroundColor等重设。这里有个性能取舍整页重新 setOption 会有明显闪烁我一般给容器加 300ms 过渡动画视觉上会平滑很多。4.3 自适应与移动端预览模板页面多宽写多宽常见 1920×1080拿到笔记本上演示会出现滚动条。处理方式外层容器固定设计稿尺寸再按比例 transform scale。#screen { width: 1920px; height: 1080px; transform-origin: top left; transition: transform .2s; } media (max-width: 1366px) { #screen { transform: scale(0.71); } }transform-origin必须置顶否则缩放后整体偏下。按 1366 宽配 0.71是基于 1366/1920 算出来的。手机预览时图表里的 visualMap 会偏小建议在竖屏媒体查询里隐藏部分装饰元素但保留三个核心图表。注意 transform scale 不改变布局尺寸容器外要加overflow: hidden否则会出现空白留边。5. 大屏启动瘦身与上线自检从按需加载到地图排错5.1 按需加载 echarts 模块这类千万行以内明细数据的统计和需要大数据集群部署策略的实时计算场景不一样瓶颈在第一屏资源的下载与解析。很多模板直接引echarts.min.js全量包控制器、SVG 渲染器、所有图表类型都打进来首屏体积 1MB 以上。对本地加载影响不大但如果要做在线部署带宽成本会明显增加。用 echarts/core 按需注册是更好的做法。import * as echarts from echarts/core; import { BarChart, LineChart, MapChart, GaugeChart, RadarChart } from echarts/charts; import { GridComponent, TooltipComponent, LegendComponent, VisualMapComponent } from echarts/components; import { CanvasRenderer } from echarts/renderers; echarts.use([BarChart, LineChart, MapChart, GaugeChart, RadarChart, GridComponent, TooltipComponent, LegendComponent, VisualMapComponent, CanvasRenderer]);这行配置是模板从“打开能看”到“部署不慌”的关键一步。以后再加图表类型只需在 import 和use数组各加一行。最容易被漏掉的是VisualMapComponent漏了之后地图不会报错但 visualMap 完全不生效排查时很费时间。5.2 地图空白与数据不上色的排查顺序地图空白是模板使用里出现频率最高的问题一个问题可能由五种原因叠加产生。先从 Network 面板看 china.json 有没有 404再确认容器高度不是 0然后在 console 里打印echarts.getMap(china)是否有数据最后看 series 里 data 的 name 与地图注册名称是否匹配。console.log(echarts.getMap(china).geoJson.features[0].properties.name); const mapNames new Set(echarts.getMap(china).geoJson.features.map(f f.properties.name)); const missing window.crimeMapData.filter(d !mapNames.has(d.name)); console.log(未命中的省份, missing);这段批量校验代码可以直接放在 index.html 里上线前跑一次。未命中省份通常有三种广西、内蒙古简称没展开直辖市把“北京市”写成“北京”港澳台名称与 GeoJSON 的版本不一致。缺一个省会在地图上表现为“没有颜色但能 hover”比完全空白更难发现所以一定要跑这段校验。5.3 启动验证清单检查项方法典型问题数据口径核对 SQL 聚合后 SUM 与原始库总量重复统计、负数未过滤地图命中批量比对 name 与 GeoJSON别名未展开容器尺寸console 取 offsetWidth父级用了 display:none年份联动快速切换 2015-2024setOption 未更新 max资源体积Network 面板看总大小未按需引入 echarts启动验证别在浏览器最后一个 tab 里做另开一个无痕窗口排除插件干扰。6. 用雷达图收尾把犯罪类型占比做成可复用分析组件6.1 从聚合数据生成雷达图数据雷达图适合展示单个省份内各犯罪类型的相对强度比多个饼图更紧凑。先用 Python 把某省份各类型人数归一化def radar_data(province: str, df: pd.DataFrame) - dict: sub df[(df[province_name] province) (df[report_year] 2022)] totals sub.groupby(crime_type)[person_count].sum() mx, mn totals.max(), totals.min() return { categories: list(totals.index), values: [round((v - mn) / (mx - mn) * 100, 1) for v in totals] }归一化用 min-max而不是除以总数。除以总数会把所有省份图形压成一个近似圆形看不出结构差异min-max 保留每个维度上的相对强弱省份间对比更直观。维度数量控制在 5~8 个超过 10 个雷达图会挤成一团。6.2 option 配置要点const { categories, values } radarData; radarChart.setOption({ radar: { indicator: categories.map(name ({ name, max: 100 })), center: [50%, 55%], radius: 62%, axisName: { color: #9ab } }, series: [{ type: radar, areaStyle: { opacity: 0.2 }, lineStyle: { width: 2 }, data: [{ value: values, name: 广东 }] }] });indicator.max统一为 100 而不是各维度真实最大值因为数据已经归一化。areaStyle.opacity设为 0.2避免大面积色块遮挡雷达坐标轴要比较两个省份结构再加一组 datalineStyle 区分颜色即可。这样雷达图作为模板最后一个可复用组件没有改变原有数据流又能快速回答“某个省份以什么类型犯罪为主”这类分析问题。本文还有配套的精品资源点击获取
返回列表