【Python Onramp】7. web端可视化:北京地铁数据统计分析实例以及简易Echarts绘图

系列文章目录

见【Python Onramp】 0. 卷首语

上一篇:【Python Onramp】6.一篇文章了解web开发要点:用Python开发简易的网页端成绩查询系统
下一篇:【Python Onramp】8.Python爬虫(1)基于requests和BeautifulSoup的全国区划数据爬虫

本文目录

  • 系列文章目录
  • 开篇总结
  • 项目描述
    • task1
    • task2
    • task3
    • task4
  • 要点总览
    • 要点1:Echarts
    • 要点2:时间函数
    • 要点3:pandas.dataframe复习
    • 要点4:JavaScript数据结构:JSON
  • 具体实现
    • step1:统计
      • 时间提取
      • 时间轴统计
    • step2:利用jsonify传递
    • step3:直接保存为js数据实现

开篇总结

一个思想:查找资料!将关键词百度一下,比如你想得到Echarts的模板,就可以直接百度。由于编程学习内容非常多样,查找不仅是方法,更是技能
几个要点

  • Echarts
  • 时间datetime模块
  • JSON
  • pandas.dataframe的一些知识复习

项目描述

详见:https://github.com/Honour-Van/CS50/tree/master/Visualization2

这个项目中你将通过北京地铁数据的展示进行

对地铁数据进行分析之后的可视化分析。

数据见 data,数据是北京地铁刷卡数据。第一行是数据标题,其含义如下:

  • a) carType:SUB 是唯一取值,表明数据是地铁,本次作业可以不用;
  • b)icID:交通卡编号,每个编号对应一张卡,已经消密脱敏;
  • c) cardType:卡类型
  • d) tradeType:交易类型
  • e)UpLine:上车线路编号
  • f) UpTime:上车时间
  • g) UpStation:上车站编号
  • h) DownLine:下车线路编号
  • i)DownTime:下车时间
  • j) DownStation:下车站编号
  • k) State:弃用

压缩包内的 PDF 文档是地铁线路编号和车站编号,供参考。

为便于程序调试,数据中有 Subway_20190301_top100000.txt(实际上是 Subway_20180301_top100000.txt,误为 2019),是 Subway_20180301.txt 的前 100000 行数据,可以用于初步调试程序,待调试完毕后再用完整数据跑,节省时间

task1

计算乘车耗时时段内的人数。每条记录是一次乘车行为,下车时间减去上车时间,即为耗时,题目要求统计耗时 10 分钟有多少人,耗时 15 分钟有多少人。以分钟为单位,计算出每个时间段内的人数。如 1 分钟:10 人,2 分钟 20 人,……,10 分钟 500 人,30 分钟 5000 人。计算耗时,用 round()函数四舍五入到分中。对下车时间早于或等于上车时间的数据予以清除,对超过 120 分钟的数据予以清除,对于不是 20180301 的数据清除。输出时,按耗时升序排序输出到 PeopleInSubwayTime.txt,并在 Excel 中打开该文件,形成条形图。

task2

所有地铁内人数时间分布。以 10 分钟为间隔,统计地铁内人数多少。进站相当于人数增加,出站相当于人数减少。以凌晨 00:00 开始计数,此刻人数为 0。统计 00:00-00:09,00:10-00:19,以此类推,要求标记为:00:00-00:00,00:10-00:19(以下同)。清除数据的规则与 5 相同。按时间自然升序输出到 PeopleInSubwayCount.txt,形成折线图;

task3

利用两个数据文件,通过 Python+Flask 模式,将数据输出到网页,利用 eCharts 显示出形如 Excel 的统计图。每个统计图都支持柱状图和折线图切换;

task4

利用两个数据文件,通过包含 JS 数据文件方式,显示出形如 Excel 的统计图。与 task3 页面要求一致,仅数据源不同而已。

要点总览

主要的内容包括Flask和JavaScript我们在之前都已经学过了,如果你已经掌握了,那么这个项目将会是一个非常愉悦的项目。

要点1:Echarts

Echarts是一个基于 JavaScript 的开源可视化图表库,版式精美简约,具有可交互性。

之前我们已经使用过其Python版Pyecharts,但实际上,PyEcharts只是Echarts的一个封装,自动生成对应的JavaScript代码。我们现在直接动手编辑HTML和JavaScript。

在网页端,可以直接利用图形界面生成所需的options对象,在实际操作时,只需选取指定对象进行options设置即可。
【Python Onramp】7. web端可视化:北京地铁数据统计分析实例以及简易Echarts绘图_第1张图片

    <script type="text/javascript">
      var EChart1 = echarts.init(document.getElementById("stat1"));
      var EChart2 = echarts.init(document.getElementById("stat2"));
      var option = {
        title: { text: "" },
        tooltip: {},
        legend: { data: ["折线图", "柱形图"] },
        toolbox: {
          feature: {
            magicType: { type: ["line", "bar"] },
            saveAsImage: {},
          },
        },
        xAxis: { data: [] },
        yAxis: {},
        series: [
          { name: "折线图", type: "line", data: [] },
          { name: "柱形图", type: "bar", data: [] },
        ],
      }; /*设置统计图形参数*/
      $.get("/getData1", function (data) {
        option.title.text = "地铁乘车用时统计";
        option.xAxis.data = data[0];
        option.series[0].data = eval("[" + data[1] + "]");
        option.series[1].data = eval("[" + data[1] + "]");
        EChart1.setOption(option);
      });
      $.get("/getData2", function (data) {
        option.title.text = "地铁站内人员统计";
        option.xAxis.data = data[0];
        option.series[0].data = eval("[" + data[1] + "]");
        option.series[1].data = eval("[" + data[1] + "]");
        EChart2.setOption(option);
      });
    script>

要点2:时间函数

datetime 的使用方法:

  • strptime()函数:将datetime对象转换为指定时间格式的字符串
  • timedelta类型:用于求时间差

我们每十分钟一分组,还需要使用如下两个函数,分别为时间编组和对应组别时间的格式化输出。

def time_group(etime: datetime.time) -> int:
    return etime.hour * 6 + (etime.minute // 10)


def group2time(group_num: int) -> str:
    hour = group_num // 6
    dec_min = group_num % 6
    return str(hour).rjust(2, '0') + ":" + str(dec_min * 10).rjust(2, '0') + '-' + str(hour).rjust(2, '0') + ":" + str(dec_min * 10 + 9).rjust(2, '0')

要点3:pandas.dataframe复习

导出行列数以表示进度:https://blog.csdn.net/lwgkzl/article/details/80988126

dataframe 更名:https://blog.csdn.net/weixin_43745169/article/details/89306686

dataframe 定位:https://blog.csdn.net/W_weiying/article/details/81411257

另外,不会把 python 列表直接变成 js 中的变量,从而使用了复制粘贴。

要点4:JavaScript数据结构:JSON

https://www.runoob.com/json/json-tutorial.html
JSON是JavaScript的内置数据结构,也通用于各种编程语言中,在flask端的数据传递过程中,我们可以利用jsonify函数将dataframe编码为JSON,随后传到网页端进行显示。这样的程序,数据不在网页端显示,保密等级上升一层。
详细可以自行搜索用法。

具体实现

step1:统计

时间提取

不正则数据:

  • 下车早于上车
  • 消除不在当日的数据
  • 清除前后超过 120 分钟的数据

返回上车和下车时间即可,上述的工作可以交由具体实现时。

第二次决定加入时间差作为第三个返回值

from datetime import datetime, time
import pandas as pd
from mytool import progress_bar, time_group, group2time

# filename = "test/test.txt"
# filename = "data/Subway_20190301_Top100000.txt"
filename = "data/Subway_20180301.txt"

df = pd.read_csv(filename)
res = {}

pb = progress_bar(df.shape[0], 100)

for _, line in df.iterrows():
    starttime = datetime.strptime(str(line["UpTime"]), "%Y%m%d%H%M%S")
    endtime = datetime.strptime(str(line["DownTime"]), "%Y%m%d%H%M%S")
    if starttime.day != 1 or endtime.day != 1:
        continue
    delta = endtime - starttime
    if delta.days < 0:
        continue
    if delta.seconds > 7200:
        continue
    tg = time_group(starttime)
    res[tg] = res.get(tg, 0) + 1
    tg = time_group(endtime)
    res[tg] = res.get(tg, 0) - 1
    pb.progress(_)

for i in range(143):
    res[i+1] = res.get(i+1, 0) + res.get(i, 0)

res = sorted(res.items(), key=lambda x: x[0])
res = [(group2time(x[0]),x[1]) for x in res]
pd.DataFrame(res, columns=['时间组', '人数']).to_csv(
    "./out/PeopleInSubwayCount.csv",index=False)

其中,mytool是自己实现的几个组件,包括时间处理以及进度条组件:

from datetime import datetime


class progress_bar():
    def __init__(self, workload, length) -> None:
        self._workload = workload
        self.stage_d = int(workload/length)
        self.stage_c = 0
        self.stage_n = 0
        self.length = length

    def progress(self, cur):
        if cur > self.stage_c:
            self.stage_c += self.stage_d
            self.stage_n += 1
        print('\r' + "[" + (self.stage_n *
              'o').ljust(self.length) + "]" + "loading...", end='')


def time_group(etime: datetime.time) -> int:
    return etime.hour * 6 + (etime.minute // 10)


def group2time(group_num: int) -> str:
    hour = group_num // 6
    dec_min = group_num % 6
    return str(hour).rjust(2, '0') + ":" + str(dec_min * 10).rjust(2, '0') + '-' + str(hour).rjust(2, '0') + ":" + str(dec_min * 10 + 9).rjust(2, '0')

时间轴统计

如果记录的进入离开时间在特定范围内,记增减。注意,每次要做部分和,保证当前是之前所有进站出站数据的综合结果。

from datetime import datetime
import pandas as pd
from mytool import progress_bar

# filename = "data/Subway_20190301_top100000.txt"
filename = "data/Subway_20180301.txt"
res = {}
df = pd.read_csv(filename)


pb = progress_bar(df.shape[0], 100)

for _, line in df.iterrows():
    starttime = datetime.strptime(str(line["UpTime"]), "%Y%m%d%H%M%S")
    endtime = datetime.strptime(str(line["DownTime"]), "%Y%m%d%H%M%S")
    if starttime.day != 1 or endtime.day != 1:
        continue
    delta = endtime - starttime
    if delta.days < 0:
        continue
    if delta.seconds > 7200:
        continue
    minutes = round(delta.seconds / 60)
    res[minutes] = res.get(minutes, 0) + 1
    pb.progress(_)


res = sorted(res.items(), key=lambda x: x[0])
pd.DataFrame(res, columns=['耗时(分钟)', '人数']).to_csv(
    "./out/PeopleInSubwayTime.csv", index=False)

step2:利用jsonify传递

from flask import Flask, jsonify  # 新增代码。装入Flask
import pandas as pd

app = Flask(__name__)  # 新增代码


@app.route("/")  # 新增代码,对应执行root()函数
def root():
    return app.send_static_file("visual.html")


@app.route("/getData1")
def getData1():
    df = pd.read_csv("./out/PeopleInSubwayTime.csv")
    data = [df.iloc[:, 0].tolist(), df.iloc[:, 1].tolist()]
    print(data)
    return  jsonify(data)


@app.route("/getData2")
def getData2():
    df = pd.read_csv("./out/PeopleInSubwayCount.csv")
    data = [df.iloc[:, 0].tolist(), df.iloc[:, 1].tolist()]
    print(data)
    return jsonify(data)



if __name__ == "__main__":
    app.run(host="0.0.0.0", port=80, debug=True)
# eof


<html>
  <head>
    <meta charset="utf-8" />
    <title>EChartstitle>
    <script src="/static/echarts.min.js">script>
    <script src="/static/jquery-3.6.0.min.js">script>
    
  head>
  <body>
    <div id="stat1" style="width: 600px; height: 400px">div>
    <div id="stat2" style="width: 600px; height: 400px">div>
    
    <script type="text/javascript">
      var EChart1 = echarts.init(document.getElementById("stat1"));
      var EChart2 = echarts.init(document.getElementById("stat2"));
      var option = {
        title: { text: "" },
        tooltip: {},
        legend: { data: ["折线图", "柱形图"] },
        toolbox: {
          feature: {
            magicType: { type: ["line", "bar"] },
            saveAsImage: {},
          },
        },
        xAxis: { data: [] },
        yAxis: {},
        series: [
          { name: "折线图", type: "line", data: [] },
          { name: "柱形图", type: "bar", data: [] },
        ],
      }; /*设置统计图形参数*/
      $.get("/getData1", function (data) {
        option.title.text = "地铁乘车用时统计";
        option.xAxis.data = data[0];
        option.series[0].data = eval("[" + data[1] + "]");
        option.series[1].data = eval("[" + data[1] + "]");
        EChart1.setOption(option);
      });
      $.get("/getData2", function (data) {
        option.title.text = "地铁站内人员统计";
        option.xAxis.data = data[0];
        option.series[0].data = eval("[" + data[1] + "]");
        option.series[1].data = eval("[" + data[1] + "]");
        EChart2.setOption(option);
      });
    script>
  body>
html>

step3:直接保存为js数据实现

这样的话,flask端需要做的工作就很少了

from flask import Flask, jsonify  # 新增代码。装入Flask
import pandas as pd

app = Flask(__name__)  # 新增代码


@app.route("/")  # 新增代码,对应执行root()函数
def root():
    return app.send_static_file("visjs.html")

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=80, debug=True)
# eof

js端需要稍微多加一点数据重组的操作:


<html>
  <head>
    <meta charset="utf-8" />
    <title>EChartstitle>
    <script src="./static/echarts.min.js">script>
    <script src="./static/myData.js">script>
  head>
  <body>
    <div id="stat1" style="width: 600px; height: 400px">div>
    <div id="stat2" style="width: 600px; height: 400px">div>
    
    <script type="text/javascript">
      var EChart1 = echarts.init(document.getElementById("stat1"));
      var EChart2 = echarts.init(document.getElementById("stat2"));
      var option = {
        title: { text: "" },
        tooltip: {},
        legend: { data: ["折线图", "柱形图"] },
        toolbox: {
          feature: {
            magicType: { type: ["line", "bar"] },
            saveAsImage: {},
          },
        },
        xAxis: { data: [] },
        yAxis: {},
        series: [
          { name: "折线图", type: "line", data: [] },
          { name: "柱形图", type: "bar", data: [] },
        ],
      }; /*设置统计图形参数*/
      option.title.text = "地铁乘车用时统计";
      option.xAxis.data = userData1[0];
      option.series[0].data = userData1[1];
      option.series[1].data = userData1[1];
      EChart1.setOption(option);

      option.title.text = "地铁站内人员统计";
      option.xAxis.data = userData2[0];
      option.series[0].data = userData2[1];
      option.series[1].data = userData2[1];
      EChart2.setOption(option);
    script>
  body>
html>

你可能感兴趣的:(Python,可视化,python,数据可视化,jquery,pandas)