基于Webio交互的Pyecharts数据分析脚本小记

1 概述

由于工作原因,需要把期货品种的交易数据以板块分类,然后统计出来。每次在Excel上做虽然不难,但也繁琐,遂想把这一工作写成脚本,一劳永逸。

实现效果如下:

对数据分类和统计倒是不难,但使用了第三方库 #Webio来交互,#Pyecharts来显示,#Pyinstaller来打包,尤其是打包过程颇费心力。趁着热乎劲儿记到这里,即为自己以后查看,也为有需要的人提供借鉴。

直接贴上代码:

#!/usr/bin/env python
# coding: utf-8

import warnings
import pandas as pd
from pywebio.input import file_upload
from pyecharts.charts import Bar  #导入需要使用的图表
from pyecharts import options as opts  #导入配置项
from pyecharts.globals import ThemeType
from pywebio.output import put_column, put_html

warnings.filterwarnings("ignore")

# 读取文件函数
def read_in():
    file = file_upload('请选择需要加载的数据')
    df1 = pd.read_excel(file['content'])
    df2 = (df1['合约'].str.extract(r'(?P<品种>.*?)(?P<月份>\d+(?:\.\d+)?)')
           .applymap(str.strip))
    data = pd.concat([df1, df2], axis=1).drop('合约', axis=1)
    return data

# 计算函数
def process(data):

    # 读入数据
    data = data
    data['板块'] = '---'

    # 根据类别打标签
    for i in range(len(data)):
        if data['品种'][i] in 贵金属:    data['板块'][i] = '贵金属'
        if data['品种'][i] in 有色:    data['板块'][i] = '有色'
        if data['品种'][i] in 煤焦钢矿:    data['板块'][i] = '煤焦钢矿'
        if data['品种'][i] in 非金属建材:    data['板块'][i] = '非金属建材'
        if data['品种'][i] in 能源:    data['板块'][i] = '能源'
        if data['品种'][i] in 化工:    data['板块'][i] = '化工'
        if data['品种'][i] in 谷物:    data['板块'][i] = '谷物'
        if data['品种'][i] in 油脂油料:    data['板块'][i] = '油脂油料'
        if data['品种'][i] in 软商品:    data['板块'][i] = '软商品'
        if data['品种'][i] in 农副产品:    data['板块'][i] = '农副产品'
        if data['品种'][i] in 金融期货:    data['板块'][i] = '金融期货'

    # 构造输出结果
    result = pd.DataFrame(columns=['多头市值', '多头市值占比', '空头市值', '空头市值占比', '轧差市值'], index=sector)

    # 根据源数据计算多头空头市值
    for i in range(len(sector)):
        result.loc[sector[i]].多头市值 = data[(data['板块'] == sector[i]) & (data['卖持仓'] == 0)].sum().持仓市值  # 多头
        result.loc[sector[i]].空头市值 = data[(data['板块'] == sector[i]) & (data['买持仓'] == 0)].sum().持仓市值  # 空头

    # 计算总市值
    TMV = result.多头市值.sum() - result.空头市值.sum()  # TMV:Total Market Value

    # 计算市值占比和轧差市值
    result.多头市值占比 = pd.DataFrame.abs(result.多头市值 / TMV)
    result.空头市值占比 = pd.DataFrame.abs(result.空头市值 / TMV)
    result.轧差市值 = result.多头市值 - result.空头市值

    long = []
    short = []
    for i in range(len(result.多头市值.tolist())):
        long.append(round(float(result.多头市值占比.tolist()[i]), 2))
        short.append(round(float(result.空头市值占比.tolist()[i]), 2))

    result.多头市值占比 = pd.DataFrame.abs(result.多头市值 / TMV).map(lambda x: format(x, '.0%'))
    result.空头市值占比 = pd.DataFrame.abs(result.空头市值 / TMV).map(lambda x: format(x, '.0%'))

    return result, long, short

# 画柱状图函数
def create_bar(bar_dict):
    # 建立百分比的柱状图
    bar_item = bar_dict['item']
    bar_head = bar_dict['head']
    bar_data = bar_dict['data']
    bar = (
        Bar(init_opts=opts.InitOpts(height="400px", width="600px", theme=ThemeType.WHITE))
        .add_xaxis(bar_item)
    )
    for i in range(len(bar_head)):
        bar.add_yaxis(bar_head[i], bar_data[i], label_opts=opts.LabelOpts(formatter="{c} %"))
    bar.set_global_opts(
        yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(formatter="{value} %"), interval=10))
    bar.set_global_opts(title_opts=opts.TitleOpts(title='板块市值占比'),
                        xaxis_opts=opts.AxisOpts(name_rotate=60, name="板块", axislabel_opts={"rotate": 45}),
                        yaxis_opts=opts.AxisOpts(name="市值占比(%)"))
    return bar

# 获取数据函数
def get_data_dict():
    # 这里获取要显示的数据 , 可以改成连接数据库
    data_a = [round(n * 100, 2) for n in long]
    data_b = [round(n * 100, 2) for n in short]
    pdt_list = sector.tolist()
    data_dict = {'data': [data_a, data_b], 'head': ['多头市值', '空头市值'], 'item': pdt_list}
    return data_dict

# 分类方法
贵金属 = ['au', 'ag']
有色 = ['al', 'pb', 'ni', 'zn', 'sn', 'cu', 'bc']
煤焦钢矿 = ['SM', 'SF', 'i', 'jm', 'hc', 'ss', 'wr', 'j', 'rb']
非金属建材 = ['FG', 'bb', 'v', 'fb']
能源 = ['pg', 'lu', 'sc', 'fu', 'ZC']
化工 = ['MA', 'eg', 'TA', 'UR', 'eb', 'PF', 'l', 'bu', 'sp', 'nr', 'pp', 'ru', 'SA']
谷物 = ['WH', 'c', 'rr', 'RI', 'JR', 'LR', 'PM']
油脂油料 = ['y', 'a', 'RM', 'p', 'RS', 'OI', 'm', 'b', 'PK']
软商品 = ['CF', 'CY', 'SR']
农副产品 = ['CJ', 'jd', 'cs', 'AP', 'lh']
金融期货 = ['T', 'TS', 'TF', 'IF', 'IH', 'IC', 'IM']

# 板块分类
sector = pd.Series(['贵金属', '有色', '煤焦钢矿', '非金属建材', '能源', '化工', '谷物', '油脂油料', '软商品', '农副产品', '金融期货'])


if __name__ == "__main__":
    data = read_in()    # 读取数据
    result, long, short = process(data)  #
    data_dict = get_data_dict()
    bar = create_bar(data_dict)
    put_column([
        put_html(bar.render_notebook()),
        put_html(result.to_html(border=0)),
    ]).show()

2 说一下值得记录的点:

2.1 根据字符和数字分列

原数据是这样的格式:

 其中【合约】由前半部分的字符和后半部分的数字组成,为了处理方便需要将二者分开,然后放到不同的列里。此动作的核心代码如下:

df2 = (df1['合约'].str.extract(r'(?P<品种>.*?)(?P<月份>\d+(?:\.\d+)?)')
           .applymap(str.strip))

1.2 Pyecharts生成带百分比的柱状图

本来Pyecharts画图不难,但生成百分比的标签有些繁琐,核心代码如下:

# 画柱状图函数
def create_bar(bar_dict):
    # 建立百分比的柱状图
    bar_item = bar_dict['item']
    bar_head = bar_dict['head']
    bar_data = bar_dict['data']
    bar = (
        Bar(init_opts=opts.InitOpts(height="400px", width="600px", theme=ThemeType.WHITE))
        .add_xaxis(bar_item)
    )
    for i in range(len(bar_head)):
        bar.add_yaxis(bar_head[i], bar_data[i], label_opts=opts.LabelOpts(formatter="{c} %"))
    bar.set_global_opts(
        yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(formatter="{value} %"), interval=10))
    bar.set_global_opts(title_opts=opts.TitleOpts(title='板块市值占比'),
                        xaxis_opts=opts.AxisOpts(name_rotate=60, name="板块", axislabel_opts={"rotate": 45}),
                        yaxis_opts=opts.AxisOpts(name="市值占比(%)"))
    return bar

麻烦的关键点在于数字格式只能是小数,百分比格式只能是字符串。

这是因为Pyecharts对输入数据的要求比较苛刻,要求规整的【list】形式,且【list】内的元素必须是【int】。

笔者用Pandas构造的DataFrame,使用to_list命令后虽然可以得到【list】格式,但其中的元素却是【numpy.int64】,所以一直识别不了,卡住了很久。

所以使用下述函数对数据进行规整:

# 获取数据函数
def get_data_dict():
    # 这里获取要显示的数据 , 可以改成连接数据库
    data_a = [round(n * 100, 2) for n in long]
    data_b = [round(n * 100, 2) for n in short]
    pdt_list = sector.tolist()
    data_dict = {'data': [data_a, data_b], 'head': ['多头市值', '空头市值'], 'item': pdt_list}
    return data_dict

原理就是遍历旧list后放到新列表中,以待输入画图函数中。

2.3 使用Pywebio库输入和输出

这是个蛮有趣的库,可以利用浏览器进行数据交互,既可以做本地脚本,也可以部署到网络。本项目只是用了基础的脚本模式,做到输入和输出。

2.3.1 输入

输入还是比较简单的,只需要使用file_upload函数唤起浏览器即可。

唤起的浏览器输入页面如下,直接选择对应的文件就好,笔者这里的文件是Excel.xls格式。

 核心代码如下:

file = file_upload('请选择需要加载的数据')
df1 = pd.read_excel(file['content'])

2.3.2 输出

输出过程也只用到了基础的部分,本来想做成横向排版,但排版总不合适,最终选择的是竖向排列。输出的结果是一个图和一个表,其中表格是DataFrame格式转换成Html格式后输出的。

效果如下:

 可以看到左右留白还是很多的,如果是横向排版会更合适,就先留着日后深化吧。

核心代码如下:

put_column([
        put_html(bar.render_notebook()),
        put_html(result.to_html(border=0))

 可以看到不管是输入还是输出,Pywebio的代码实现都相当简洁,这里要给开发者点个大大的赞!

 2.4 打包

打包真的很麻烦,尤其是对Pywebio和Pyecharts的打包试了很多次都不行。这方面笔者还不太懂,这里只是将自己的排错过程放到这里仅供参考。

2.4.1 首先在工作目录下使用如下命令生成spec文件:

pyi-makespec -c -F main.py

其中“main.py”应该改成实际要打包的脚本名字。

2.4.2 然后在相应目录中找到spec文件并打开,进行以下编辑:

from pywebio import STATIC_PATH

a = Analysis(
    ...
    datas=[(STATIC_PATH, 'pywebio/html'), (STATIC_PATH+'/../platform/tpl', 'pywebio/platform/tpl')],
    ...
)

2.4.3 接着使用以下命令打包:

pyinstaller main.spec

打包完成后会在dist文件夹里找到.exe可执行文件,然后并没有完事大吉,运行此文件会发现报错,报错原因是“No Such File...”之类的,找不到的文件是Pywebio和Pyecharts里的东西,我也不管到底是什么了,就直接把这两个库copy到缺失的地方,然后再运行,就终于完事大吉啦!

 手动Copy补充的文件夹如上所示~

至此才终于走到Final...

3 结语

从写好这个小脚本到打包成可执行文件花了三天左右的时间,其中几乎一半都花在打包上,真可谓血泪满满,菜又爱玩。

目前的项目还很不成熟,下一步的完善方向是:

  1. 对脚本功能进行扩展。目前可以做的任务只是简单的分板块统计市值,以后还可以有更多方向,对有需求的画图任务还可以增加,具体的有灵感再说~
  2. 对代码和函数进行整理,争取写的更精炼~
  3. 试试看能不能减小打包后的体积~
  4. 尝试线上发布,这样大家就都可以使用我的小脚本啦,但笔者不懂网络这些,尚且不知道难度如何,总之先挖个坑吧~

最后希望疫情早点过去,世界和平~

Reference

【1】字母和数字分列:https://www.coder.work/article/2020734

【2】字母和数字分列:https://stackoverflow.com/questions/56672613/separate-string-from-numeric-in-single-pandas-dataframe-column-and-create-two-ne

【3】带百分比的柱状图,数据规整:https://blog.csdn.net/seakingx/article/details/105135110

【4】打包:https://github.com/pywebio/PyWebIO/issues/22

【5】打包:https://zhuanlan.zhihu.com/p/459009973

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值