可视化练习一
4.1新建Flask可视化项目
项目命名为ViewData, 项目中新建文件夹app/models存放数据库映射类,app/static/js存放js的外部工具包,app/templates/main存放html前端页面,app/templates/errors存放404之后的html前端页面,app/views存放SQLAlchemy的语法的数据库访问类。
ViewData/
├── app/
│ ├── models/ # 数据库映射类
│ │ └── __init__.py
│ ├── static/ # 外部工具包
│ │ └── js/
│ ├── templates/ # 模板
│ │ ├── main/ # 存放html前端页面
│ │ └── errors/
│ ├── views/ # SQLAlchemy语法数据库访问类
│ │ └──__init__.py
│ ├── __init__.py
│ ├── config.py
│ └── extensions.py
├── manage.py
4.2新建app/init.py
新建flask的初始化类,自定义函数def create_app(config_name)封装一个方法,专门用于创建Flask实例;自定义函数def config_errorhandler(app)编写访问失败之后的404页面的跳转设置。
`__init__.py`的作用是让一个呈结构化分布(以文件夹形式组织)的代码文件夹变成可以被导入`import`的软件包。
app/__init__.py
# 封装一个方法,专门用于创建Flask实例
def create_app(config_name): # development
# 创建应用实例
app = Flask(__name__)
# 初始化配置
app.config.from_object(config.get(config_name) or config['default'])
# 调用初始化函数
config[config_name].init_app(app)
# 配置扩展 函数位置 app/ extensions.py
config_extensions(app)
# 配置蓝本
config_blueprint(app)
# 错误页面定制
config_errorhandler(app)
# 返回应用实例
return app
# 访问失败之后的404页面的跳转设置
def config_errorhandler(app):
# 如果在蓝本定制,只针对本蓝本的错误有效,
# 可以使用app_errorhandler定制全局有效的错误显示
@app.errorhandler(404)
def page_not_found(e):
return render_template('errors/404.html')
4.3新建app/config.py
新建flask连接数据库的配置类,将flask框架连接数据库的配置编写成函数,包括数据库的ip、端口、用户名、密码等。
import os
base_dir = os.path.abspath(os.path.dirname(__file__))
# os.path.abspath 返回绝对路径
# os.path.dirname(__file__) 去掉文件名,返回目录
# 通用配置
# 类中的配置项必须是大写的,否则读取配置失败
class Config:
# 秘钥
SECRET_KEY = os.environ.get('SECRET_KEY') or '123456'
# 没有设置secret_key,可能报Must provide secret_key to use csrf错误提醒
# Session, Cookies以及一些第三方扩展都会用到SECRET_KEY值
# 有些字符不宜明文写进代码里,比如数据库密码,个人账户密码,如果写进自己本机的环境变量里,程序用的时候通过os.environ.get()取出来就行了。这样开发人员本机测试的时候用的是自己本机的一套密码,生产环境部署的时候,用的是公司的公共账号和密码,这样就能增加安全性。os.environ是一个字典,是环境变量的字典。"SECRET_KEY"是这个字典里的一个键,如果有这个键,返回对应的值,如果没有,则返回none
# 数据库
SQLALCHEMY_COMMIT_ON_TEARDOWN = True # 每次请求结束后都会自动提交数据库中的变动。
SQLALCHEMY_TRACK_MODIFICATIONS = False # 动态追踪修改设置,如未设置只会提示警告, 不建议开启
# 额外的初始化操作,即使什么内容都没有写,也是有意义的
@staticmethod
def init_app(app):
pass
# 开发环境 语法:mysql+pymysql://用户名:密码@ip:端口/数据库名
class DevelopmentConfig(Config):
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:passwd@localhost:3306/visiondata'
# 测试环境
class TestingConfig(Config):
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:passwd@localhost:3306/visiondata'
# 生产环境
class ProductionConfig(Config):
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:passwd@localhost:3306/visiondata'
# 配置字典, 导入子类配置
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
4.4新建app/extensions.py(扩展)
导入flask相关模块,创建SQLAlchemy对象并初始化Bootstrap。自定义函数def config_extensions(app)完成SQLAlchemy和Bootstrap的初始化
# 导入类库
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
# Flask-Migrate是用于处理SQLAlchemy 数据库迁移的扩展工具。当Model出现变更的时候,通过migrate去管理数据库变更。修改数据库不会直接手动的去修改,而是去修改ORM对应的模型,然后再把模型映射到数据库中。
from flask_moment import Moment
# Flask-Moment 用来处理时间日期等信息。用这个模块主要是考虑到两点,第一是为了让不同时区的用户看到的都是各自时区的实际时间,而不是服务器所在地的时间。第二是对于一些时间间隔的处理,如果要手动处理很麻烦,如果有模块就很好了。
# 创建对象
bootstrap = Bootstrap()
db = SQLAlchemy()
moment = Moment()
migrate = Migrate(db=db) # 第一个参数是Flask的实例,第二个参数是Sqlalchemy数据库实例
# 初始化
def config_extensions(app):
bootstrap.init_app(app)
db.init_app(app)
moment.init_app(app)
migrate.init_app(app)
4.5新建app/models/表名.py
编写flask的数据库表的映射类,每个表格对应一个文件,映射类命名与表名一致。
# app/models/avg_money_bigdata.py
class AvgMoneyBigData(db.Model):
__tablename__ = 'avg_money_bigdata'
id = db.Column(db.Integer, primary_key=True)
city = db.Column(db.String(255))
avg_money = db.Column(db.String(255))
# app/models/avg_money_city.py
class AvgMoneyCity(db.Model):
__tablename__ = "avg_money_city"
id = db.Column(db.Integer, primary_key=True)
city = db.Column(db.String(255))
avg_money = db.Column(db.String(255))
def __repr__(self):
return 'Id: %d , City: %s ,Avg_money: %s' %(self.id, self.city, self.avg_money)
# app/models/bigdata_work.py
class BigDataWork(db.Model):
__tablename__ = 'bigdata_work'
id = db.Column(db.Integer, primary_key=True)
job_name = db.Column(db.String(255))
company_name = db.Column(db.String(255))
city = db.Column(db.String(255))
job_require = db.Column(db.Text)
recruit_number = db.Column(db.String(255))
money = db.Column(db.String(255))
skill_require = db.Column(db.String(255))
release_date = db.Column(db.String(255))
sex = db.Column(db.String(255))
company_detail = db.Column(db.String(255))
education = db.Column(db.String(255))
# app/models/hot_work.py
class HotWork(db.Model):
__tablename__ = 'hot_work'
id = db.Column(db.Integer, primary_key=True)
job_name = db.Column(db.String(255))
job_number = db.Column(db.Integer)
4.6新建app/models/init.py
编写flask的访问数据库的初始化文件,导入数据库映射表类。
from app.extensions import db
from .hot_work import HotWork
from .bigdata_work import BigDataWork
from .avg_money_city import AvgMoneyCity
from .avg_money_bigdata import AvgMoneyBigData
4.7新建app/static/js
将html前段需要用到的js工具包存放在js文件夹下。
4.8新建ViewData/mamage.py
新建flask启动类,创建flask实例和数据库迁移命令,以及程序的入口。
import os
from app import create_app
from flask_script import Manager, Server
from flask_migrate import MigrateCommand
# migrate的作用就是在数据库字段改变时不用drop表直接做更新操作
# 获取配置
config_name = os.environ.get('FLASK_CONFIG') or 'default'
# os模块获取环境变量的一个方法
# 创建Flask实例
app = create_app(config_name)
# 创建命令行启动控制对象
#三种方法创建命令,即创建Command子类、使用@command修饰符、使用@option修饰符;
manager = Manager(app)
# Command子类必须定义一个run方法
manager.add_command("runserver", Server(use_debugger=True))
# 添加数据库迁移命令
manager.add_command('db', MigrateCommand)
# 启动项目
if __name__ == '__main__':
# 调用manager.run()启动Manager实例接收命令行中的命令
manager.run()
4.9新建app/views/init.py
新建flask的蓝本配置初始化类,进行蓝本的配置,自定义函数def config_blueprint(app)以及封装函数完成蓝本的注册。
from .main import main
# 蓝本配置
DEFAULT_BLUEPRINT = (
# 蓝本,前缀
(main, ''),
)
# 封装函数,完成蓝本注册
def config_blueprint(app):
for blueprint, prefix in DEFAULT_BLUEPRINT:
app.register_blueprint(blueprint, url_prefix=prefix)
4.10新建app/views的main.py
(1)根据数据库表格hotwork,分析并统计招聘数量最多的前十名热门职位,分别使用柱状图表达
数据源:数据库visiondata中hot_work表
在app/views下的main.py添加函数def get_hot_work() ,使用SQLAlchemy语法编写sql的查询语句,查询统计招聘数据前十的热门职位。
(2)请根据指定表中的数据,统计出全国某些城市指定招聘岗位平均工资,通过南丁格尔玫瑰图进行呈现。
数据源:数据库visiondata中avg_money_city和avg_money_bigdata
在app/views下的main.py添加函数def get_avg_money_city()和def get_avg_money_BigData(),使用SQLAlchemy语法编写sql的查询语句,查询两个表中的城市平均工资数据。
(3)分析并统计招聘数量"大数据"相关职位招聘数量,分别使用柱状图表达,同时在网页后台输出相关数据打印语句。
数据源:数据库visiondata中bigdata_work
在app/views下的main.py添加函数def get_big_data_work(),使用SQLAlchemy语法编写sql的查询语句,对相同职位进行数量汇总。
import json
from flask import Blueprint, render_template, jsonify
from app.models import HotWork, BigDataWork, AvgMoneyCity, AvgMoneyBigData
from app.extensions import db
from sqlalchemy import *
main = Blueprint('main', __name__) # 实例化路由
@main.route('/')
def index():
return render_template('/main/echarts.html') # 当url访问 / 直接跳转到主页
@main.route('/index/') # url访问 /index/ 跳转主页 调用display方法 将查询结果通过render_template()传入html页面
def display():
rs_ghw = get_hot_work()
rs_gbdw = get_big_data_work()
rs_gamc = get_avg_money_city()
rs_gamb = get_avg_money_BigData()
return render_template('/main/echarts.html', rs_ghw=rs_ghw, rs_gbdw=rs_gbdw, rs_gamc=rs_gamc, rs_gamb=rs_gamb)
# 统计招聘数量最多的前十热门职位 获取结果:
def get_hot_work(): # 编写查询语句 返回前10条
# sql: select * from hot_work group by job_name limit 10
ghw_rs = HotWork.query.order_by(desc('job_number')).limit(10)
print(ghw_rs)
return ghw_rs
# 所有城市招聘数据的平均工资 获取结果
def get_avg_money_city():
# sql: select * from avg_money_city
gamc_rs = AvgMoneyCity.query.all()
print('所有城市招聘数据的平均工资 获取结果成功')
return gamc_rs
# “大数据”相关职位所有城市招聘数据的平均工资 获取结果
def get_avg_money_BigData():
# sql: select * from avg_money_big_data
gamb_rs = AvgMoneyBigData.query.all()
print('“大数据”相关职位所有城市招聘数据的平均工资 获取结果成功')
return gamb_rs
# 对相同职位进行数量汇总,"大数据"相关职位招聘数量比较
def get_big_data_work():
# sql: select job_name,count(*) from bigdata_work where job_name like '%大数据%' GROUP BY job_name
gbdw_rs = db.session.query(BigDataWork.job_name, func.count('*').label('job_count'))\
.group_by(BigDataWork.job_name).order_by(desc('job_count')).all()
print('"大数据"相关职位招聘数量:' + str(gbdw_rs))
return gbdw_rs
4.11新建app/templates/errors/404.html
编写url访问失败之后的提示页面。
4.12新建app/templates/main/echarts.html
编写js获取到flask传过来的数据,通过echarts组件进行结果的展示(包括css样式)。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>招聘信息统计</title>
<script type="text/javascript" src="/static/js/echarts.js"></script>
<script type="text/javascript" src="/static/js/jquery.min.js"></script>
<script type="text/javascript" src="/static/js/js2wordcloud.js"></script>
</head>
<style>
html , body , .content {
width:100%;
height:100%;
padding: 0;
margin: 0;
box-sizing: border-box;
background-color: #ccc;
}
.content {
padding: 40px;
}
.header {
height: 10%;
width: 100%;
font-size: 24px;
font-weight: 700;
line-height: 60px;
text-align: center;
}
.body {
height: 100%;
width: 100%;
text-align: center;
}
.chartBox {
width: 100%;
height: 60%;
margin-bottom:40px;
}
</style>
<body>
<div class="content">
<div class="header"></div>
<div class="body">
<div class="chartBox" id="hotWork"></div>
<div class="chartBox" id="bigDataWork"></div>
<div class="chartBox" id="pieAvgMoneyCity"></div>
</div>
</div>
</body>
<script>
//折线图 招聘数量最多的前十热门职位
var hotWork = echarts.init(document.getElementById('hotWork')); //获取div的id 实例化echarts组件
var data_name = [{% for r in rs_ghw %} "{{r.job_name}}", {% endfor %}] //将职位名job_name存放在一个数组中作为x轴数据
var data_y = [{% for r in rs_ghw %}"{{r.job_number}}",{% endfor %}] //将职位数量job_number存放在一个数组中作为y轴数据
console.log('十大热门职位:' + data_name);
console.log('数据分别为:' + data_y);
console.log('最大值: ' + Math.max.apply(null, data_y) + ', 最小值:' + Math.min.apply(null, data_y));
workOption = {
title: {
text: '职位分析', //主标题
subtext: ' ---10大热门职位分析', //副标题
x: '45%' //设置标题位置
},
xAxis: {
type: 'category',
name: '岗位名称',
data: data_name,
axisLabel : {
interval: 0,
rotate: "25" //x轴字体的旋转度
}
},
yAxis: {
name: '招聘数量',
type: 'value'
},
series: [{
data: data_y,
type: 'line', //设置图形为折线图
label: {
normal: {
show: true,
position: 'top' //折线图顶部显示对应的x轴数值
}
}
}]
};
hotWork.setOption(workOption); //设置echarts的option参数 加载并显示图形
//南丁格尔玫瑰图
var pieAvgMoneyCity = echarts.init(document.getElementById('pieAvgMoneyCity'));
var city = [{% for r in rs_gamc %}"{{r.city}}",{% endfor %}]
var avg_money_pie_city = [{% for r in rs_gamc %}{value:"{{r.avg_money}}", name:"{{r.city}}"},{% endfor %}]
var avg_money_pie_bigdata = [{% for r in rs_gamb %}{value:"{{r.avg_money}}", name:"{{r.city}}"},{% endfor %}]
var avg_money_city = [{% for r in rs_gamc %} "{{r.avg_money}}", {% endfor %}]
var avg_money_big_data = [{% for r in rs_gamb %}"{{r.avg_money}}",{% endfor %}]
var dataInt_city = [];
var dataInt_big_data = [];
dataInt_city.forEach(function(data){
avg_money_city.push(+parseInt(data)); ///遍历数组 将每个元素变成整型
});
dataInt_big_data.forEach(function(data){
avg_money_big_data.push(+parseInt(data)); ///遍历数组 将每个元素变成整型
});
console.log('所有城市平均薪资:' + city);
console.log('数据分别为:' + avg_money_city);
console.log("所有城市最大平均薪资为" + Math.max.apply(null, avg_money_city));
console.log("“大数据”相关职位城市招聘数据的平均工资" + city);
console.log('数据分别为:' + avg_money_big_data);
console.log("“大数据”相关职位最大平均薪资为" + Math.max.apply(null, avg_money_big_data));
pieAvgMoneyCityOption = {
title : {
text: '所有城市招聘数据的平均工资 vs “大数据”相关职位所有城市招聘数据的平均工资',
subtext: '南丁格尔玫瑰图',
x:'center'
},
tooltip : {
trigger: 'item',
formatter: "{b}:{c}({d}%)" //当鼠标移动到图形 显示数据(格式): 佛山(1111) 10%
},
legend: {
x : 'center',
y : 'bottom',
data:city
},
color:[
'#C1232B','#B5C334','#FCCE10','#E87C25','#27727B',
'#668ffe','#00ca54','#00dbfa','#f3006a','#60C0DD',
'#d714b7','#84433c','#f490f3','#000000','#26C0C0'],
series : [
{
type:'pie',
radius : [20, 110], //图像的大小
center : ['25%', '50%'], //图形的位置
roseType : 'radius', //南丁格尔玫瑰图的参数
data:avg_money_pie_city
},
{
type:'pie',
radius : [30, 110],
center : ['75%', '50%'],
roseType : 'area',
data:avg_money_pie_bigdata
}
]
};
pieAvgMoneyCity.setOption(pieAvgMoneyCityOption);
//柱状图 "大数据"相关职位招聘数量
var bigDataWork = echarts.init(document.getElementById('bigDataWork'));
var job_name = [{% for r in rs_gbdw %} "{{r.job_name}}", {% endfor %}]
var quantity = [{% for r in rs_gbdw %}"{{r.job_count}}",{% endfor %}] // 获取元组中的第二个元素('AI大数据工程师', 1095)
console.log('招聘职位: ' + job_name);
console.log('对应职位招聘数量: ' + quantity);
console.log('岗位需求量最大: ' + Math.max.apply(null, quantity) + ', 岗位需求量最少:' + Math.min.apply(null, quantity));
bigdataworkOption = {
title: {
text: '大数据相关职位招聘数量',
subtext: ' ----职位招聘对比',
x: '45%',
//modified 0523
textStyle:{
//文字颜色
color:'#f30008',
//字体风格,'normal','italic','oblique'
fontStyle:'oblique',
//字体粗细 'normal','bold','bolder','lighter',100 | 200 | 300 | 400...
fontWeight:'bold',
//字体系列
fontFamily:'FangSong'
//fontFamily: 'KaiTi'
//字体大小
//fontSize:18
}
//modified end
},
xAxis: {
type: 'category',
name: '职位名称',
data: job_name,
axisLabel : {
interval: 0,
rotate: "15" //x轴字体的旋转度
}
},
yAxis: {
type: 'value',
name: '招聘数量'
},
series: [{
data: quantity,
type: 'bar',
label: {
normal: {
show: true,
position: 'top'
}
},
//modified0523
itemStyle: {
normal: {color: 'black'}
}
//modified end
}]
};
bigDataWork.setOption(bigdataworkOption);
</script>
</html>
4.13运行Flask程序查看结果
(1)分析并统计招聘数量最多的前十名热门职位,分别使用折线图表达
(2)分析并统计招聘数量"大数据"相关职位招聘数量,分别使用柱状图表达。
(3)统计出全国某些城市指定招聘岗位平均工资,通过南丁格尔玫瑰图进行呈现。