数据可视化

1.数据可视化技术概述

1.1 前端开发可视化技术

Web开发是指创建和维护网站或网络应用程序的过程,涉及从前端用户界面到后端数据管理和逻辑处理的所有方面。Web开发是一个多领域交叉的活动,它融合了设计、编码、数据库管理、服务器配置等多种技能。

而前端开发可视化技术是web开发的一部分内容,常用的前端开发可视化技术有ECharts、D3.js、Highcharts 等。

(1)ECharts

ECharts 是一个由百度开源的 JavaScript 图表库,提供了丰富的图表类型,包括折线图、柱状图、饼图、地图、热力图等,并且支持自定义图表。其特点是:

  • 丰富的图表类型:几乎涵盖了所有常见的数据展示需求。
  • 高度可定制:可以通过配置项调整图表的样式和行为。
  • 交互性:支持鼠标事件,如缩放、漫游、提示框等。
  • 性能优化:对于大数据量有较好的性能表现。
  • 跨平台:兼容各种浏览器和移动设备。

(2)D3.js

D3.js (Data-Driven Documents) 是一个强大的 JavaScript 库,用于操作文档中的数据并将其转换为可视化。D3.js 更偏向于数据驱动的设计,允许开发者完全控制每个细节。其特点是:

  • 灵活性:可以完全控制SVG元素,适用于复杂和定制化的可视化需求。
  • 数据绑定:自动将数据与DOM元素关联。
  • 过渡和动画:可以创建平滑的动画效果。

(3)Highcharts

Highcharts 是一个商业化的图表库,也提供免费的版本。它专注于易于集成和快速生成图表。其特点是:

  • 易用性:提供简单而强大的API。
  • 多平台支持:除了Web,还支持移动设备和桌面应用。

1.2 Python可视化技术

Python是数据科学和数据分析领域中最流行的语言之一,它拥有丰富的可视化库,可以满足从简单的数据探索到复杂的交互式图表的各种需求。matplotlib、seaborn和pyecharts是三个常用的Python可视化库。

(1)Matplotlib

matplotlib是Python中历史最悠久、最基础的可视化库之一。它提供了大量的绘图API,可以创建静态、动态、交互式的图表。matplotlib的灵活性很高,允许用户精确地控制图表的每一个细节,从线条样式、标记到轴标签、图例等。其特点是:

  • 高度可定制:可以控制图表的几乎每一部分。
  • 广泛的图表类型:包括线图、散点图、条形图、直方图、饼图、3D图表等。
  • 支持多种输出格式:可以将图表保存为PNG、PDF、EPS、SVG等格式。
  • 社区支持:有大量的文档、教程和示例代码。

使用场景有:

  • 适用于需要高度定制和精确控制的科研和工程应用。
  • 数据探索和初步分析。
  • 创建出版质量的图表。

(2)Seaborn

seaborn建立在matplotlib之上,旨在提供更高级的接口,简化了许多常见数据可视化任务,特别是在统计图形方面。seaborn的设计哲学是使得创建复杂的统计图表变得容易,同时保持美观的默认样式。其特点是:

  • 统计图形:专门针对统计数据的分布和关系。
  • 美观的默认样式:默认的色彩方案和字体设置更现代、美观。
  • 集成数据分析库:与pandas数据结构紧密集成,方便数据处理和绘图。
  • 高级图形:如热力图、小提琴图、联合图等。

使用场景有:

  • 数据分析和统计可视化,特别是对于复杂数据集的分布和相关性研究。
  • 快速生成专业级的统计图表,无需过多的样式定制。

(3)Pyecharts

pyecharts是一个用于生成ECharts图表的Python库,ECharts是由百度开发的一个强大的JavaScript图表库。pyecharts允许开发者在Python环境中使用ECharts的全部功能,生成的图表可以嵌入到Web应用中。可以参考其官网:https://pyecharts.org/#/zh-cn/intro,进行学习。其特点是:

  • 丰富的图表类型:提供多种动态和交互式图表,包括地图、桑基图、漏斗图、K线图等。
  • 交互性:生成的图表支持鼠标事件、缩放、漫游等交互功能。
  • Web兼容性:生成的图表可以直接在Web浏览器中查看和操作。
  • 中文支持:内置中文标签和注释,适合中文数据的可视化。

使用场景有:

  • 创建动态和交互式的Web图表,适合集成到Web应用或数据大屏中。
  • 大数据量的可视化,pyecharts在处理大量数据时表现良好。
  • 需要高度交互性和动态效果的图表展示。

1.3 专业数据可视化软件

随着数据可视化技术的发展,专用可视化软件成为部分企业开发数据可视化应用的选择,目前比较流行的数据可视化软件有:Tableau、Power BI等。

(1)Tableau

Tableau 是一款流行的商业智能工具,用于创建交互式的仪表板和报告。其特点是:

  • 拖放界面:无需编程知识即可创建可视化。
  • 数据连接:支持多种数据源,包括本地文件、数据库和云服务。
  • 共享和协作:可以轻松地在组织内部分享可视化。

(2)Power BI

Microsoft Power BI 是一个业务分析服务,提供数据可视化和报告功能。其特点是:

  • 企业级集成:与Office 365和其他Microsoft产品无缝集成。

自服务和IT管理:支持自服务BI同时提供IT控制选项。

我们就用ECharts来演示一下

ECharts

基础折线图:

option = {
  xAxis: {
    type: 'category',
    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  },
  yAxis: {
    type: 'value'
  },
  series: [
    {
      data: [820, 932, 901, 934, 1290, 1330, 1320],
      type: 'line',
      smooth: true
    }
  ]
};

基础柱状图:

option = {
  xAxis: {
    type: 'category',
    data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
  },
  yAxis: {
    type: 'value'
  },
  series: [
    {
      data: [120, 200, 150, 80, 70, 110, 130],
      type: 'bar'
    }
  ]
};

某站点用户Access From:

option = {
  title: {
    text: 'Referer of a Website',
    subtext: 'Fake Data',
    left: 'center'
  },
  tooltip: {
    trigger: 'item'
  },
  legend: {
    orient: 'vertical',
    left: 'left'
  },
  series: [
    {
      name: 'Access From',
      type: 'pie',
      radius: '50%',
      data: [
        { value: 1048, name: 'Search Engine' },
        { value: 735, name: 'Direct' },
        { value: 580, name: 'Email' },
        { value: 484, name: 'Union Ads' },
        { value: 300, name: 'Video Ads' }
      ],
      emphasis: {
        itemStyle: {
          shadowBlur: 10,
          shadowOffsetX: 0,
          shadowColor: 'rgba(0, 0, 0, 0.5)'
        }
      }
    }
  ]
};

基础散点图:

option = {
  xAxis: {},
  yAxis: {},
  series: [
    {
      symbolSize: 20,
      data: [
        [10.0, 8.04],
        [8.07, 6.95],
        [13.0, 7.58],
        [9.05, 8.81],
        [11.0, 8.33],
        [14.0, 7.66],
        [13.4, 6.81],
        [10.0, 6.33],
        [14.0, 8.96],
        [12.5, 6.82],
        [9.15, 7.2],
        [11.5, 7.2],
        [3.03, 4.23],
        [12.2, 7.83],
        [2.02, 4.47],
        [1.05, 3.33],
        [4.05, 4.96],
        [6.03, 7.24],
        [12.0, 6.26],
        [12.0, 8.84],
        [7.08, 5.82],
        [5.02, 5.68]
      ],
      type: 'scatter'
    }
  ]
};

基础 K 线图:

option = {
  xAxis: {
    data: ['2017-10-24', '2017-10-25', '2017-10-26', '2017-10-27']
  },
  yAxis: {},
  series: [
    {
      type: 'candlestick',
      data: [
        [20, 34, 10, 38],
        [40, 35, 30, 50],
        [31, 38, 33, 44],
        [38, 15, 5, 42]
      ]
    }
  ]
};

笛卡尔坐标系上的热力图:

// prettier-ignore
const hours = [
    '12a', '1a', '2a', '3a', '4a', '5a', '6a',
    '7a', '8a', '9a', '10a', '11a',
    '12p', '1p', '2p', '3p', '4p', '5p',
    '6p', '7p', '8p', '9p', '10p', '11p'
];
// prettier-ignore
const days = [
    'Saturday', 'Friday', 'Thursday',
    'Wednesday', 'Tuesday', 'Monday', 'Sunday'
];
// prettier-ignore
const data = [[0, 0, 5], [0, 1, 1], [0, 2, 0], [0, 3, 0], [0, 4, 0], [0, 5, 0], [0, 6, 0], [0, 7, 0], [0, 8, 0], [0, 9, 0], [0, 10, 0], [0, 11, 2], [0, 12, 4], [0, 13, 1], [0, 14, 1], [0, 15, 3], [0, 16, 4], [0, 17, 6], [0, 18, 4], [0, 19, 4], [0, 20, 3], [0, 21, 3], [0, 22, 2], [0, 23, 5], [1, 0, 7], [1, 1, 0], [1, 2, 0], [1, 3, 0], [1, 4, 0], [1, 5, 0], [1, 6, 0], [1, 7, 0], [1, 8, 0], [1, 9, 0], [1, 10, 5], [1, 11, 2], [1, 12, 2], [1, 13, 6], [1, 14, 9], [1, 15, 11], [1, 16, 6], [1, 17, 7], [1, 18, 8], [1, 19, 12], [1, 20, 5], [1, 21, 5], [1, 22, 7], [1, 23, 2], [2, 0, 1], [2, 1, 1], [2, 2, 0], [2, 3, 0], [2, 4, 0], [2, 5, 0], [2, 6, 0], [2, 7, 0], [2, 8, 0], [2, 9, 0], [2, 10, 3], [2, 11, 2], [2, 12, 1], [2, 13, 9], [2, 14, 8], [2, 15, 10], [2, 16, 6], [2, 17, 5], [2, 18, 5], [2, 19, 5], [2, 20, 7], [2, 21, 4], [2, 22, 2], [2, 23, 4], [3, 0, 7], [3, 1, 3], [3, 2, 0], [3, 3, 0], [3, 4, 0], [3, 5, 0], [3, 6, 0], [3, 7, 0], [3, 8, 1], [3, 9, 0], [3, 10, 5], [3, 11, 4], [3, 12, 7], [3, 13, 14], [3, 14, 13], [3, 15, 12], [3, 16, 9], [3, 17, 5], [3, 18, 5], [3, 19, 10], [3, 20, 6], [3, 21, 4], [3, 22, 4], [3, 23, 1], [4, 0, 1], [4, 1, 3], [4, 2, 0], [4, 3, 0], [4, 4, 0], [4, 5, 1], [4, 6, 0], [4, 7, 0], [4, 8, 0], [4, 9, 2], [4, 10, 4], [4, 11, 4], [4, 12, 2], [4, 13, 4], [4, 14, 4], [4, 15, 14], [4, 16, 12], [4, 17, 1], [4, 18, 8], [4, 19, 5], [4, 20, 3], [4, 21, 7], [4, 22, 3], [4, 23, 0], [5, 0, 2], [5, 1, 1], [5, 2, 0], [5, 3, 3], [5, 4, 0], [5, 5, 0], [5, 6, 0], [5, 7, 0], [5, 8, 2], [5, 9, 0], [5, 10, 4], [5, 11, 1], [5, 12, 5], [5, 13, 10], [5, 14, 5], [5, 15, 7], [5, 16, 11], [5, 17, 6], [5, 18, 0], [5, 19, 5], [5, 20, 3], [5, 21, 4], [5, 22, 2], [5, 23, 0], [6, 0, 1], [6, 1, 0], [6, 2, 0], [6, 3, 0], [6, 4, 0], [6, 5, 0], [6, 6, 0], [6, 7, 0], [6, 8, 0], [6, 9, 0], [6, 10, 1], [6, 11, 0], [6, 12, 2], [6, 13, 1], [6, 14, 3], [6, 15, 4], [6, 16, 0], [6, 17, 0], [6, 18, 0], [6, 19, 0], [6, 20, 1], [6, 21, 2], [6, 22, 2], [6, 23, 6]]
    .map(function (item) {
    return [item[1], item[0], item[2] || '-'];
});
option = {
  tooltip: {
    position: 'top'
  },
  grid: {
    height: '50%',
    top: '10%'
  },
  xAxis: {
    type: 'category',
    data: hours,
    splitArea: {
      show: true
    }
  },
  yAxis: {
    type: 'category',
    data: days,
    splitArea: {
      show: true
    }
  },
  visualMap: {
    min: 0,
    max: 10,
    calculable: true,
    orient: 'horizontal',
    left: 'center',
    bottom: '15%'
  },
  series: [
    {
      name: 'Punch Card',
      type: 'heatmap',
      data: data,
      label: {
        show: true
      },
      emphasis: {
        itemStyle: {
          shadowBlur: 10,
          shadowColor: 'rgba(0, 0, 0, 0.5)'
        }
      }
    }
  ]
};

漏斗图:

option = {
  title: {
    text: 'Funnel'
  },
  tooltip: {
    trigger: 'item',
    formatter: '{a} <br/>{b} : {c}%'
  },
  toolbox: {
    feature: {
      dataView: { readOnly: false },
      restore: {},
      saveAsImage: {}
    }
  },
  legend: {
    data: ['Show', 'Click', 'Visit', 'Inquiry', 'Order']
  },
  series: [
    {
      name: 'Funnel',
      type: 'funnel',
      left: '10%',
      top: 60,
      bottom: 60,
      width: '80%',
      min: 0,
      max: 100,
      minSize: '0%',
      maxSize: '100%',
      sort: 'descending',
      gap: 2,
      label: {
        show: true,
        position: 'inside'
      },
      labelLine: {
        length: 10,
        lineStyle: {
          width: 1,
          type: 'solid'
        }
      },
      itemStyle: {
        borderColor: '#fff',
        borderWidth: 1
      },
      emphasis: {
        label: {
          fontSize: 20
        }
      },
      data: [
        { value: 60, name: 'Visit' },
        { value: 40, name: 'Inquiry' },
        { value: 20, name: 'Order' },
        { value: 80, name: 'Click' },
        { value: 100, name: 'Show' }
      ]
    }
  ]
};

基础雷达图:

option = {
  title: {
    text: 'Basic Radar Chart'
  },
  legend: {
    data: ['Allocated Budget', 'Actual Spending']
  },
  radar: {
    // shape: 'circle',
    indicator: [
      { name: 'Sales', max: 6500 },
      { name: 'Administration', max: 16000 },
      { name: 'Information Technology', max: 30000 },
      { name: 'Customer Support', max: 38000 },
      { name: 'Development', max: 52000 },
      { name: 'Marketing', max: 25000 }
    ]
  },
  series: [
    {
      name: 'Budget vs spending',
      type: 'radar',
      data: [
        {
          value: [4200, 3000, 20000, 35000, 50000, 18000],
          name: 'Allocated Budget'
        },
        {
          value: [5000, 14000, 28000, 26000, 42000, 21000],
          name: 'Actual Spending'
        }
      ]
    }
  ]
};

ECharts可以画的远不如此

有需要的也可以搜索此网站自行查询Apache EChartsApache ECharts

我们来用ECharts来画一张数据大屏

以柳州螺狮粉为例,画出它的数据大屏

首先准备好一个文件夹

  1. static:

    • css: 用于存放CSS文件,这些文件定义了网页的样式。
    • font: 用于存放字体文件,这些文件可以用于网页上的自定义字体。
    • images: 用于存放图片文件,这些文件可以用于网页上的图像展示。
    • js: 用于存放JavaScript文件,这些文件用于实现网页上的交互功能。
  2. templates: 通常用于存放HTML模板文件,这些文件定义了网页的结构和布局。在Web框架如Flask中,这些模板文件会被渲染并返回给客户端。

  3. app.py: 这是一个Python文件,通常用于定义Web应用的主程序。在Flask框架中,app.py 文件中会定义Flask应用实例,并配置路由、视图函数等。

  4. test.ipynb: 这是一个Jupyter Notebook文件,通常用于数据科学项目中的数据探索、分析和可视化。Jupyter Notebook是一种交互式计算环境,支持代码和文本的混合编写。

  5. utils.py: 这是一个Python文件,通常用于存放一些通用的辅助函数或类,这些函数或类可以在项目中被多个地方使用。

写一个python文件

from flask import Flask, request, jsonify, render_template,redirect,url_for
app = Flask(__name__)

import utils
import pandas as pd
import random

# 预设的账号密码和图形页面映射
# 模拟用户数据存储,实际应用中应使用数据库来保存用户信息。
USERS = {
    '12345': {'password': '123', 'graph': 'charts.html'},# 用户ID: {密码, 图形文件名}
}

@app.route('/')
def index():
    """
    主页路由。
    
    当用户访问网站根路径时,此函数会被调用,它会渲染并返回index.html模板。
    """
    return render_template('index.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    """
    登录页面路由,支持GET和POST请求。
    
    - GET请求:当用户直接访问/login路径时,显示登录表单(login.html)。
    - POST请求:处理用户提交的登录信息,验证用户名和密码是否匹配。
      如果匹配成功,则重定向到图形页面;否则,重定向回登录页面。
    """
    if request.method == 'POST':
        # 从POST请求中获取用户输入的用户名和密码
        username = request.form['username']
        password = request.form['password']
        # 尝试从模拟的用户字典中获取用户信息
        user_info = USERS.get(username)
        
        # 验证用户名和密码是否正确
        if user_info and user_info['password'] == password:
            # 登录成功,重定向到对应的图形页面
            return redirect(url_for('graph', graph=user_info['graph']))
        else:
            # 登录失败,重定向回登录页面
            return redirect(url_for('login'))
    
    # 如果是GET请求,或者POST请求但验证失败,渲染登录页面
    return render_template('login.html')

@app.route('/graph/<graph>')
def graph(graph):
    """
    图形页面路由,接受一个名为'graph'的URL参数,该参数指定了要渲染的图形HTML文件名。
    此函数尝试根据提供的图形文件名渲染相应的HTML页面。
    """

    return render_template(graph)



# 柱形图数据
@app.route('/bar')
def bar():
    """
    定义一个Flask路由,当用户访问'/bar'路径时触发此函数。
    此函数负责获取并处理各省的总销量数据,最后以JSON格式返回给客户端。
    """

     # 从utils模块中调用get_province_data函数来读取MySQL数据库中的省销量数据
    bar_data = utils.get_province_data()

    # 将获取到的数据转换成pandas DataFrame对象,方便后续的数据处理
    df_pro = pd.DataFrame(bar_data,columns=['省份','总销量'])   #把数据转成表格
    
     # 对总销量数据进行处理:将每个销量值除以10000,并保留两位小数
    df_pro['总销量'] = (df_pro['总销量']/10000).round(2) 

    # 对DataFrame按照'总销量'列进行降序排序,并取前10行
    # 这样可以确保只展示销量最高的10个省份
    df_pro = df_pro.sort_values(by='总销量',ascending=False).head(10)
    
    return jsonify(df_pro.to_dict(orient='list'))

# 折线图数据
@app.route('/line')
def line():
    """
    定义一个Flask路由,当用户访问'/line'路径时触发此函数。
    此函数负责获取并处理每日销售数据,最后以JSON格式返回给客户端。
    """
     # 从utils模块中调用get_daily_data函数来获取原始销售数据
    line_data = utils.get_daily_data()

    # 将获取到的数据转换成pandas DataFrame对象,方便后续的数据处理
    df = pd.DataFrame(line_data, columns=['日期','销量'])
    
    # 对销量数据进行处理:将每个销量值除以10000,并保留两位小数
    df['销量'] = (df['销量'] / 10000).round(2) 

    # 处理日期列,将其转换为月-日的字符串格式(例如:03-15)
    df['日期'] = df['日期'].dt.strftime('%m-%d')   
    
    # 使用pandas的to_dict方法将DataFrame转换为字典,orient='list'参数表示将每列作为列表
    return jsonify(df.to_dict(orient='list'))

# 地图数据-all
@app.route('/map')
def map():
    """
    定义一个Flask路由,当用户访问'/map'路径时触发此函数。
    此函数负责获取并处理各省的销售数据和辣度数据,最后以JSON格式返回给客户端。
    """

    # 从utils模块中调用get_province_data函数来读取MySQL数据库中的省销量数据
    map_data = utils.get_province_data()

    # 从utils模块中调用get_spicy_data函数来读取MySQL数据库中的省辣度数据
    map_spicy = utils.get_spicy_data()

    # 初始化两个空列表,用于存储处理后的销售数据和辣度数据
    list_map = []
    spicy_map = []

    # 遍历map_data,将每个元组转换为字典格式,并添加到list_map列表中
    # 每个字典包含两个键:'name'(省份名称)和'value'(销售数据)
    for i in map_data:
        list_map.append({'name':i[0], 'value':i[1]})    #对数据库中的数据处理成 地图 所需要的格式[{},{}]
    
    # 遍历map_spicy,将每个元组转换为字典格式,并添加到spicy_map列表中
    # 每个字典包含两个键:'name'(省份名称)和'value'(辣度数据)
    for i in map_spicy:
        spicy_map.append({'name':i[0], 'value':i[1]})
    # 将两个列表封装在一个字典中
    data = {
        'list_map': list_map, # 销售数据列表
        'spicy_map': spicy_map # 辣度数据列表
    }
    # 使用Flask的jsonify函数将Python字典转换为JSON响应对象,并返回给客户端
    return jsonify(data)

# 饼图数据
@app.route('/taste')
def pie():
    """
    定义一个Flask路由,当用户访问'/taste'路径时触发此函数。
    此函数负责获取并处理不同口味的销售数据,最后以JSON格式返回给客户端。
    """

     # 从utils模块中调用get_taste_data函数来读取MySQL数据库中的口味销售数据
    pie_data = utils.get_taste_data()

    # 将获取到的数据转换成pandas DataFrame对象,方便后续的数据处理
    # 这里指定了六列的名称:'原味', '加辣', '酸菜', '番茄', '加臭', '升级'
    df = pd.DataFrame(pie_data, columns=['原味', '加辣', '酸菜', '番茄', '加臭', '升级'])
    
    
     # 初始化一个空列表,用于存储处理后的口味销售数据
    list_pie = []

    # 遍历DataFrame的列名,将每个列名及其对应的第一个值(假设是总销量)转换为字典格式,并添加到list_pie列表中
    # 每个字典包含两个键:'name'(口味名称)和'value'(销量数据)
    for i in df:
        list_pie.append({'name':i,'value':df[i][0]})    #转成字典格式

    # 使用Flask的jsonify函数将Python列表转换为JSON响应对象,并返回给客户端
    return jsonify(list_pie)

# 词云图数据
@app.route('/word')
def wordCloud():
    """
    定义一个Flask路由,当用户访问'/word'路径时触发此函数。
    此函数负责获取各城市的名称数据,并为每个城市随机生成一个权重值,
    最后以JSON格式返回给客户端,用于绘制词云图。
    """

     # 从utils模块中调用get_city_data函数来读取MySQL数据库中的城市名称数据
    city_data = utils.get_city_data()

    # 初始化一个空列表,用于存储处理后的城市名称及其对应的随机权重值
    list_word = []

    # 遍历city_data,假设city_data是一个嵌套的列表(每个元素也是一个列表)
    # 对于每个内部列表中的每个城市名称,生成一个随机权重值,并将其转换为字典格式添加到list_word列表中
    for x in city_data:
        for y in x:
            list_word.append({'name':y, 'value':random.randint(1,10000)})

    # 使用Flask的jsonify函数将Python列表转换为JSON响应对象,并返回给客户端
    return jsonify(list_word)

# 今日销量
@app.route('/today')
def today_sale():
    """
    定义一个Flask路由,当用户访问'/today'路径时触发此函数。
    此函数负责获取今日的销售数据,并以JSON格式返回给客户端。
    """

    # 从utils模块中调用get_today_data函数来读取今日的销售数据
    today_sale = utils.get_today_data()

    # 使用Flask的jsonify函数将Python对象转换为JSON响应对象,并返回给客户端
    return jsonify(today_sale)

# 累计销量
@app.route('/total')
def total_sale():
    """
    定义一个Flask路由,当用户访问'/todal'路径时触发此函数。
    此函数负责获取累计的销售数据,并以JSON格式返回给客户端。
    """

    # 从utils模块中调用get_today_data函数来读取累计的销售数据
    total_sale = utils.get_total_data()

    # 使用Flask的jsonify函数将Python对象转换为JSON响应对象,并返回给客户端
    return jsonify(total_sale)


# 数据大屏的路由
@app.route('/')
def charts():
    return render_template('charts.html')

# json数据显示中文
app.config['JSON_AS_ASCII'] = False

if __name__ == '__main__':
    app.run('0.0.0.0', debug=True)
  • Flask 是Web应用的核心类。
  • request 用于处理HTTP请求。
  • jsonify 用于将Python对象转换为JSON格式响应。
  • render_template 用于渲染HTML模板。
  • redirect 和 url_for 用于重定向到其他页面或路由
USERS = {
    '12345': {'password': '123', 'graph': 'charts.html'},  # 用户ID: {密码, 图形文件名}
}

这是一个模拟的用户字典,实际应用中应该使用数据库来存储用户信息。每个用户有一个唯一的ID、密码以及对应的图形页面。

@app.route('/')
def index():
    return render_template('index.html')

定义了主页的路由,当用户访问根路径时,会渲染并返回index.html页面

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        user_info = USERS.get(username)
        if user_info and user_info['password'] == password:
            return redirect(url_for('graph', graph=user_info['graph']))
        else:
            return redirect(url_for('login'))
    return render_template('login.html')
  • 处理用户的登录逻辑,支持GET和POST请求。
  • 如果是POST请求,则验证用户名和密码是否匹配。如果匹配成功,重定向到对应的图形页面;否则,重定向回登录页面。
  • 如果是GET请求,或者POST请求但验证失败,渲染登录页面。
    @app.route('/graph/<graph>')
    def graph(graph):
        return render_template(graph)

    根据URL参数graph指定的图形文件名,渲染相应的HTML页面。

  • 数据接口

    接下来是一系列的数据接口,用于提供不同类型的图表数据(柱状图、折线图、地图、饼图、词云图等):

  • 柱状图数据 (/bar): 获取各省的总销量数据,并按销量降序排序后返回前10个省份的数据。
  • 折线图数据 (/line): 获取每日销售数据,并处理日期格式后返回。
  • 地图数据 (/map): 获取各省的销售数据和辣度数据,分别处理后以列表形式返回。
  • 饼图数据 (/taste): 获取不同口味的销售数据,并将其转换为适合饼图显示的格式。
  • 词云图数据 (/word): 获取各城市的名称数据,并为每个城市随机生成一个权重值,用于绘制词云图。
  • 今日销量 (/today) 和 累计销量 (/total): 分别获取今日和累计的销售数据。
    @app.route('/')
    def charts():
        return render_template('charts.html')

    定义了一个数据大屏的路由,实际上这个路由与主页路由冲突了,因为都是/。通常应该使用不同的路径来区分它们,比如/charts

app.config['JSON_AS_ASCII'] = False

设置Flask应用的配置项,确保返回的JSON响应中包含的中文字符不会被转义为ASCII编码。

if __name__ == '__main__':
    app.run('0.0.0.0', debug=True)
  • app.run('0.0.0.0', debug=True) 启动Flask应用,监听所有网络接口,并启用调试模式。这在开发环境中非常有用,因为它会在代码更改时自动重启服务器,并提供有用的错误信息。

创建一个HTML文件

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>数据大屏</title>
    <script src="../static/js/echarts.min.js"></script>
    <script src="../static/js/jquery-3.7.1.min.js"></script>
    <script src="../static/js/china.js"></script>
    <link rel="stylesheet" href="../static/css/charts.css" />
</head>

<body>
    <div class="title">袋装螺蛳粉销量数据大屏</div>
    <div class="mainbox">
        <div class="column-1">
            <div class="charts" id="bar"></div>
            <div class="charts" id="line"></div>
        </div>
        <div class="column-2">
            <div class="number-title">
                <div>今日销量</div>
                <div>累计销量</div>
            </div>
            <div class="number">
                <div id="today">000000</div>
                <div id="total">000000</div>
            </div>
            <div class="option">
                <label for="select">产品类型</label>&nbsp;
                <select name="select" id="select">
                    <option value="all">所有</option>
                    <option value="spicy">加辣款</option>
                </select>
            </div>
            <div class="map" id="map"></div>
        </div>
        <div class="column-1">
            <div class="charts" id="taste"></div>
            <div class="charts" id="word"></div>
        </div>
    </div>
    <script src="../static/js/bar.js"></script>
    <script src="../static/js/line.js"></script>
    <script src="../static/js/map.js"></script>
    <script src="../static/js/taste.js"></script>
    <script src="https://assets.pyecharts.org/assets/echarts.min.js"></script>
    <script src="https://assets.pyecharts.org/assets/echarts-wordcloud.min.js"></script>
    <script src="../static/js/word.js"></script>
    <script src="../static/js/today.js"></script>
    <script src="../static/js/total.js"></script>
</body>

</html>
  • <!DOCTYPE html> 声明文档类型为HTML5。
  • <html lang="en"> 设置文档的语言为英语。如果页面内容是中文,可以改为lang="zh"
  • <meta charset="UTF-8"> 设置字符编码为UTF-8,确保页面可以正确显示中文字符。
  • <meta name="viewport" content="width=device-width, initial-scale=1.0"> 设置视口(viewport),使页面在移动设备上能够正确缩放。
  • <title>数据大屏</title> 设置页面标题为“数据大屏”。
  • <script> 标签引入了多个JavaScript文件:
    • echarts.min.js: ECharts库的核心文件,用于绘制图表。
    • jquery-3.7.1.min.js: jQuery库,简化DOM操作和AJAX请求。
    • china.js: ECharts的中国地图扩展文件,用于绘制中国地图。
  • <link> 标签引入了CSS文件charts.css,用于设置页面样式。
  • <div class="title">袋装螺蛳粉销量数据大屏</div>: 页面顶部的标题,显示“袋装螺蛳粉销量数据大屏”。
  • <div class="mainbox">: 主要内容区域,包含三个列布局(column-1 和 column-2)。
    • 第一列 (column-1):
      • 包含两个图表容器:柱状图 (id="bar") 和折线图 (id="line").
    • 第二列 (column-2):
      • 数字显示区:
        • number-title 显示“今日销量”和“累计销量”的标签。
        • number 包含两个div,分别用于显示今日销量 (id="today") 和累计销量 (id="total"),初始值为000000
      • 选择器:
        • option 包含一个下拉菜单 (select),用户可以选择查看“所有”或“加辣款”的销售数据。
      • 地图:
        • id="map" 用于绘制中国地图,展示各省的销售数据。
    • 第三列 (column-1):
      • 包含两个图表容器:饼图 (id="taste") 和词云图 (id="word").
      • 这些<script>标签引入了多个JavaScript文件,每个文件负责初始化和更新特定的图表或数据:
        • bar.js: 初始化并更新柱状图。
        • line.js: 初始化并更新折线图。
        • map.js: 初始化并更新中国地图。
        • taste.js: 初始化并更新饼图。
        • word.js: 初始化并更新词云图。
        • today.js: 更新今日销量数据。
        • total.js: 更新累计销量数据。
      • 另外,还引入了两个来自pyecharts的CDN资源:
        • echarts.min.js: ECharts库的核心文件。
        • echarts-wordcloud.min.js: ECharts的词云图扩展插件。

柱形图:

var chartDom_bar = document.getElementById('bar');
var myChart_bar = echarts.init(chartDom_bar);
// 配置项
option_bar = {
    title: {
        text: '各省份销量柱形图',
        left: 'center',         // 标题居中
        textStyle: {            // 标题文字样式
            color: 'white',     // 设置标题颜色为深灰色
            fontSize: 14,       // 设置字体大小(可选)
            fontWeight: 'bold'  // 设置字体加粗(可选)
        }
    },
    axisLabel: {
        color: '#fff' // 设置xy轴数值标签颜色
    },
    xAxis: {
        type: 'category',
        data: [],
    },
    yAxis: {
        type: 'value'
    },
    series: [{
        data: [],
        type: 'bar',
        
    }],
    tooltip: {}
};
// ajax获取路由数据
$.ajax({
    url: "/bar",
    success: function (data) {
        option_bar.xAxis.data = data['省份']
        option_bar.series[0].data = data['总销量']
        // 配置项作用到图形中
        myChart_bar.setOption(option_bar);
    },
    error: function (xhr, type, errorThrown) { }
})
  • document.getElementById('bar'): 获取HTML中id="bar"的DOM元素,这个元素是用来容纳柱状图的容器。
  • echarts.init(chartDom_bar): 使用ECharts库初始化一个图表实例,绑定到chartDom_bar元素上。返回的myChart_bar对象就是ECharts的图表实例,后续可以通过它来设置图表的配置项和数据。
  • title: 配置图表的标题。
    • text: 标题文本为“各省份销量柱形图”。
    • left: 'center': 标题居中显示。
    • textStyle: 设置标题的文字样式,包括颜色、字体大小和字体加粗。
  • axisLabel: 设置x轴和y轴的标签颜色为白色,确保在深色背景下清晰可见。
  • xAxis: 配置x轴为类别轴(type: 'category'),初始时data为空数组,稍后会通过AJAX请求填充实际的省份数据。
  • yAxis: 配置y轴为数值轴(type: 'value')。
  • series: 定义图表的数据系列,类型为柱状图(type: 'bar'),初始时data为空数组,稍后会通过AJAX请求填充实际的销量数据。
  • tooltip: 工具提示框组件,目前未配置具体属性,可以后续根据需要添加更多的配置。
  • $.ajax: 使用jQuery的ajax方法发起HTTP GET请求,获取服务器端提供的销售数据。
    • url: "/bar": 请求的URL路径为/bar,这是Flask应用中的一个路由,负责返回各省的总销量数据。
    • success: function (data): 当请求成功时,回调函数会接收服务器返回的数据(data),并进行以下操作:
      • option_bar.xAxis.data = data['省份']: 将返回的省份数据赋值给x轴的data属性,即x轴的类别数据。
      • option_bar.series[0].data = data['总销量']: 将返回的总销量数据赋值给系列的data属性,即柱状图的高度数据。
      • myChart_bar.setOption(option_bar): 将更新后的配置项应用到图表实例myChart_bar上,从而动态更新图表的显示内容。
    • error: function (xhr, type, errorThrown): 当请求失败时,回调函数会输出错误信息到控制台,帮助调试问题。

折线图:

var chartDom_line = document.getElementById('line');
var myChart_line = echarts.init(chartDom_line);
// 配置项
option_line = {
    title: {
        text: '每日销量折线图',
        left: 'center',         // 标题居中
        textStyle: {            // 标题文字样式
            color: 'white',     // 设置标题颜色为深灰色
            fontSize: 14,       // 设置字体大小(可选)
            fontWeight: 'bold'  // 设置字体加粗(可选)
        }
    },
    xAxis: {
        type: 'category',
        data: [],
    },
    yAxis: {
        type: 'value'
    },
    series: [{
        data: [],
        type: 'line',
    }],
    tooltip: {}
};
// ajax获取路由数据
$.ajax({
    url: "/line",
    success: function (data) {
        option_line.xAxis.data = data['日期']
        option_line.series[0].data = data['销量']
        // 配置项作用到图形中
        myChart_line.setOption(option_line);
    },
    error: function (xhr, type, errorThrown) { }
})

地图数据:

// 选择DOM并创建echarts图形对象
var chartDom_map = document.getElementById('map');
var myChart_map = echarts.init(chartDom_map);
var colors = {
    'normal':['#006d77', '#00e9ff', '#a3f7ff'],
    'spicy': ['#FFF59D', '#FFCA28', '#E64A19']
}
// 配置项
var option_map = {
    title: {
        text: '各省份销量地图',
        left: 'center',         // 标题居中
        textStyle: {            // 标题文字样式
            color: 'white',     // 设置标题颜色为深灰色
            fontSize: 18,       // 设置字体大小(可选)
        }
    },
    series: [
        {
            type: "map",
            mapType: "china",
            label: {
                show: true
            },
            roam: true,
            data: [],
        }
    ],
    tooltip: {},
    visualMap: {
        inRange: {
            color: colors['normal']
        },
        textStyle: {
            color: 'orange'
        },
        min: 0,
        max: 500000,
        calculable: true
    }
};

// ajax获取路由数据
// 使用 jQuery 的 $.ajax 方法发起一个异步 HTTP 请求
$.ajax({
    // 设置请求的URL,这里指向服务器端的 "/map" 路径
    url: "/map",
    
    // 定义当请求成功时的回调函数
    success: function (data) {
        // 当从服务器接收到的数据(data)成功返回后执行以下操作
        
        // 更新图表系列数据。假设 'option_map' 是已经定义好的图表配置对象,
        // 'series[0].data' 指的是第一个数据系列的数据。
        // 'data['list_map']' 是从服务器响应中获取的地图数据列表。
        option_map.series[0].data = data['list_map'];
        
        // 将更新后的配置项应用到名为 'myChart_map' 的 ECharts 实例上,
        // 使图表显示最新的数据。
        myChart_map.setOption(option_map);
        
        // 为下拉选择框(id 为 'select' 的元素)绑定 change 事件处理函数。
        // 当用户改变下拉选项时触发此函数。
        $('#select').change(function () {
            // 获取当前选中的下拉菜单值
            var value = $(this).val();
            
            // 根据选中的值来更新地图显示内容
            if (value === 'spicy') {
                // 如果选择了 "加辣款" 选项
                
                // 更新图表数据为 'spicy_map' 数据集,这应该是与加辣款相关的销售数据
                option_map.series[0].data = data['spicy_map'];
                
                // 更改视觉映射的颜色范围为 'spicy' 类型的颜色方案
                option_map.visualMap.inRange.color = colors['spicy'];
                
                // 更新图表标题为 "加辣款销量地图"
                option_map.title.text = '加辣款销量地图';
                
                // 应用新的配置项,刷新图表显示
                myChart_map.setOption(option_map);
            } else if (value === 'all') {
                // 如果选择了 "所有款" 选项
                
                // 将图表数据恢复为原始的 'list_map' 数据集,即所有产品的销售数据
                option_map.series[0].data = data['list_map'];
                
                // 恢复默认颜色方案
                option_map.visualMap.inRange.color = colors['normal'];
                
                // 更新图表标题为 "各省份销量地图"
                option_map.title.text = '各省份销量地图';
                
                // 应用新的配置项,刷新图表显示
                myChart_map.setOption(option_map);
            }
        });
    },
    // 定义当请求失败时的回调函数
    error: function (xhr, type, errorThrown) {
        // xhr 表示 XMLHttpRequest 对象
        // type 表示错误类型
        // errorThrown 表示捕获的异常信息
        // 这里可以添加错误处理逻辑,比如显示错误消息给用户
    }
});

饼图:

// 选择DOM并创建echarts图形对象
var chartDom_pie = document.getElementById('taste');
var myChart_pie = echarts.init(chartDom_pie);
// 配置项
option_pie = {
    title: {
        text: '不同口味螺蛳粉分布图',
        left: 'center',         // 标题居中
        textStyle: {            // 标题文字样式
            color: '#fff',     // 设置标题颜色
            fontSize: 28,       // 设置字体大小(可选)
        }
    },
    series: [{
        data: [],
        // name: '访问来源',
        type: 'pie',
        radius: ['40%', '70%'], // 设置内外半径,实现环形饼图
        label: {
            formatter: '{a|{b}: {c}}\n{b|占比: {d}%}', // 设置标签的文本格式
            width: 100,
            height: 50,
            lineHeight: 30,
            backgroundColor: '#ADCCFF',     // 标签背景颜色
            borderColor: '#8C8D8E',          // 标签边框颜色
            borderWidth: 1,
            borderRadius: 4,
            // position: 'inside',
            rich: {
                a: {
                    align: 'center',
                    fontWeight: 'bold',
                    fontSize: 14
                },
                b: {
                    color: '#fff',
                    backgroundColor: '#4C5058',
                    borderRadius: 4,
                    align: 'center',
                    padding: [5, 4],
                    lineHeight: 10,
                    fontWeight: 'bold',
                    fontSize: 12
                }
            }
        },
    }],
};

// ajax获取路由数据
$.ajax({
    url: "/taste",
    success: function (data) {
        option_pie.series[0].data = data
        // 配置项作用到图形中
        myChart_pie.setOption(option_pie);
    },
    error: function (xhr, type, errorThrown) { }
})

词云图:

// 选择DOM并创建echarts图形对象
var chartDom_word = document.getElementById('word');
var myChart_word = echarts.init(chartDom_word);
// 配置项
var option_word = {
    series: [
        {
            data: [],
            type: 'wordCloud',
            shape: 'circle',
            width: '100%',
            height: '100%',
            left: 'center',
            top: 'center',
            gridSize: 5,

            sizeRange: [0.5, 30],
            rotationRange: [-90, 90],
            rotationStep: 45,
            textStyle: {
                fontFamily: 'sans-serif',
                normal: {
                    color: function () {
                        return 'rgb(' + [
                            Math.round(Math.random() * 95 + 160),
                            Math.round(Math.random() * 95 + 160),
                            Math.round(Math.random() * 95 + 160)
                        ].join(',') + ')';
                    }
                }
            }
        }
    ]
};
// ajax获取路由数据
$.ajax({
    url: "/word",
    success: function (data) {
        option_word.series[0].data = data
        // 配置项作用到图形中
        myChart_word.setOption(option_word);
    },
    error: function (xhr, type, errorThrown) { }
})

在处理亿些些细节

这样就画好了

详细代码可以下载资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值