python数据可视化:pyecharts v1版本(大屏展示自适应屏幕)


title: python数据可视化:pyecharts v1版本
date: 2021-06-05 10:29:26
tags: python数据分析
sticky: 996


Echarts是一个由百度开源的数据可视化,结合巧妙的交互性,精巧的图表设计;而 Python 是一门富有表达力的语言,很适合用于数据处理。分析遇上数据可视化时,pyecharts诞生了。
个人觉得可视化最好用的,不接受反驳,毕竟用echarts的都那么多;
官方文档

  • 自己挑了一些碰到的坑记了一下,分享一些自己学习过程中发现的资源,其他看文档就够了

基础柱状图

from pyecharts import options as opts
from pyecharts.charts import Bar
from pyecharts.faker import Faker
c = (
    Bar()
    .add_xaxis(Faker.choose())
    .add_yaxis("商家A", Faker.values())#gap="0%";category_gap="80%";stack="stack1"
    .add_yaxis("商家B", Faker.values())#gap="0%";is_selected=False;stack="stack1";label_opts=opts.LabelOpts(is_show=False)
    .set_global_opts(title_opts=opts.TitleOpts(title="Bar-基本示例", subtitle="我是副标题"))
                     # datazoom_opts=[opts.DataZoomOpts(), opts.DataZoomOpts(type_="inside")],#时间轴显示并可同通过鼠标滑动
    .set_series_opts(
        label_opts=opts.LabelOpts(is_show=False),
        markpoint_opts=opts.MarkPointOpts(
            data=[
                opts.MarkPointItem(type_="max", name="最大值"),
                opts.MarkPointItem(type_="min", name="最小值"),
#                 opts.MarkPointItem(type_="average", name="平均值"),
            ]
        ),
        markline_opts= opts.MarkLineOpts(data = [opts.MarkLineItem(type_ = "average",name = "平均值")])
    )
    .render_notebook()
)
c
image
  • 只能通过滑动水平轴缩放x轴:只传入datazoom_opts=[opts.DataZoomOpts()];省略掉 opts.DataZoomOpts(), 删除水平轴,就只能通过鼠标拖动;

  • 显示水平轴的比例(默认):
    opts.DataZoomOpts(range_start=20,range_end=80)

  • 取消柱子上方数据显示:
    label_opts=opts.LabelOpts(is_show=False)

  • 添加y轴水平轴:
    datazoom_opts=opts.DataZoomOpts(orient="vertical"),可缩放大小;

  • 柱间距离:
    加入参数gap="0%"(两个add_yaxis里面都要传);
    加入参数category_gap="80%",表示单系柱间距离;

  • 默认取消显示某 Series:
    加入参数is_selected=False,默认取消显示某 Series

  • 堆叠数据:传入stack="stack1",希望哪几个指标堆叠就传那几个add_yaxis中;

  • 显示ToolBox:在set_global_opts()中传入 toolbox_opts=opts.ToolboxOpts(), brush_opts=opts.BrushOpts(),;

  • XY轴名称:在set_globel_opts()中加入 yaxis_opts=opts.AxisOpts(name="我是 Y 轴"),xaxis_opts=opts.AxisOpts(name="我是 X 轴"), ;

  • Y轴数据单位:在set_globel_opts()中加入 yaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(formatter="{value} /月")), ;

  • 加入标记线

    .set_series_opts(
        label_opts=opts.LabelOpts(is_show=False),
        markline_opts=opts.MarkLineOpts(
            data=[opts.MarkLineItem(y=50, name="yAxis=50")]
        ),
    )
#set_global_opts()后加入set_series_opts,加入标记线,这里是y=50;
  • 标记线的维度(当然可以是横轴)
.set_series_opts(
    markline_opts=opts.MarkLineOpts(
                data=[opts.MarkLineItem(type_="average",value_dim="x", name="xAxis"),
                     opts.MarkLineItem(type_="average",value_dim="y", name="xAxis")
                     ]
  • 加入标记值
#set_series_opts中加入,表示传入标记,最大、最小、平均值。
        markpoint_opts=opts.MarkPointOpts(
            data=[
                opts.MarkPointItem(type_="max", name="最大值"),
                opts.MarkPointItem(type_="min", name="最小值"),
                opts.MarkPointItem(type_="average", name="平均值"),
            ]
  • xy轴互换
#设置完add_yaxis后接着
    .reversal_axis()#xy轴互换
    .set_series_opts(label_opts=opts.LabelOpts(position="right"))#显示数据在柱子右边
  • x轴名字标签过长,旋转角度
在set_global_opts()中传入
xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=-15)),
  • 连接空数据
import pyecharts.options as opts
from pyecharts.charts import Line
from pyecharts.faker import Faker

y = Faker.values()
y[3], y[5] = None, None
c = (
    Line()
    .add_xaxis(Faker.choose())
    .add_yaxis("商家A", y, is_connect_nones=True)
    .set_global_opts(title_opts=opts.TitleOpts(title="Line-连接空数据"))
    .render("line_connect_null.html")
)
  • 自定义标记点
import pyecharts.options as opts
from pyecharts.charts import Line
from pyecharts.faker import Faker

x, y = Faker.choose(), Faker.values()
c = (
    Line()
    .add_xaxis(x)
    .add_yaxis(
        "商家A",
        y,
        markpoint_opts=opts.MarkPointOpts(
            data=[opts.MarkPointItem(name="自定义标记点", coord=[x[2], y[2]], value=y[2])]
        ),
    )
    .set_global_opts(title_opts=opts.TitleOpts(title="Line-MarkPoint(自定义)"))
    .render("line_markpoint_custom.html")
)
  • 作条形图时由于有时候座标轴文字过长,缩放后导致无法完全显示,此时则需要调整座标轴与图像的边距。调整边距是采用grid()方法。在图形设置完成后,使用grid设置座标轴与图像边界的距离。
report_day_line = Grid()
report_day_line.add(report_day_line1,opts.GridOpts(pos_left="10%",pos_top ="10%"), is_control_axis_index=True)
report_day_line.render_notebook()
tooltip_opts=opts.TooltipOpts(
                formatter=JsCode(
                "function (params) {return params.value[2] + ' :复购率:' +(Number(params.value[0])* 100).toFixed(2) + '%'+',销售额:'+ params.value[1];}"
                )
#                 trigger="axis", axis_pointer_type="cross"
            ),

pyecharts画时间排序图

from pyecharts import options as opts
from pyecharts.charts import Bar,Timeline
from pyecharts.faker import Faker
import random
import pandas as pd
import numpy as np

# 受这篇文章启发https://blog.csdn.net/weixin_42512684/article/details/108176613
data = np.array([random.randint(30,150) for i in range(52*7) ]).reshape(52,7)
df = pd.DataFrame(data,index=[i for i in range(1969,2021)],columns = random.sample(attr, len(attr)))
df
df_sorted = [r[:].sort_values() for i,r in df.iterrows()]
df_sorted[0].values
t1  = Timeline()# 创建 Timeline对象

for j in range(1969,2021):
    bar = (
    Bar()
    .add_xaxis([str(i) for i in df_sorted[j-1969].index])
    .add_yaxis('Data',[int(i) for i in df_sorted[j-1969].values]
               ,label_opts = opts.LabelOpts(position = 'right'),
             )
    .set_series_opts(label_opts = opts.LabelOpts(is_show = True,position = 'right'))
    .reversal_axis()
    .set_global_opts(title_opts = opts.TitleOpts("{}".format(j),
                                                 pos_left = '50%',

                                                ),
                    legend_opts = opts.LegendOpts(pos_right = '10%'))

    )

    t1.add(bar,'{}年'.format(j))
    
t1.add_schema(
symbol = 'arrow',# 设置标记时间;
    #orient = 'vertical',
     symbol_size = 2,# 标记大小;
    play_interval = 1000,# 播放时间间隔;
    control_position = 'right',# 控制位置;
linestyle_opts = opts.LineStyleOpts(width = 5,
                                   type_ = 'dashed',
                                   color = 'rgb(255,0,0,0.5)'),
    label_opts = opts.LabelOpts(color = 'rgb(0,0,255,0.5)',
                                    font_size = 15,
                                    font_style = 'italic',
                                    font_weight = 'bold',
                                     font_family ='Time New Roman',

                                position = 'left',
                                interval = 20,
                                    )
)
t1.render_notebook()

pyecharts画地理图-geo,map

def my_geo(city, city_value):
    c = (
        Geo(init_opts=opts.InitOpts(theme=ThemeType.DARK,chart_id=1))
            .add_schema(maptype="china")
            .add("geo", [list(z) for z in zip(city, city_value)])
            .set_series_opts(label_opts=opts.LabelOpts(is_show=False))
            .set_global_opts(
            visualmap_opts=opts.VisualMapOpts(), title_opts=opts.TitleOpts(title="用户城市分布")
        )  # .render("geo_base.html")
    )
    return c

is_piecewise参数:在 set_global_opts()中的visualmap_opts=opts.VisualMapOpts(is_piecewise=True),表示分段显示;

def my_map(province, num):
    c = (
        Map(init_opts=opts.InitOpts(theme=ThemeType.DARK,chart_id=2))
            .add("map", [list(z) for z in zip(province, num)], "china")
            .set_series_opts(label_opts=opts.LabelOpts(is_show=False))
            .set_global_opts(visualmap_opts=opts.VisualMapOpts(), title_opts=opts.TitleOpts(title="用户省份分布"))
    )  # .render("map_base.html")
    return c

pyecharts画内层嵌套饼图

c = (
    Pie()
    .add("", [list(z) for z in zip(Faker.choose(), Faker.values())])#center=["35%", "50%"],
    .set_global_opts(title_opts=opts.TitleOpts(title="Pie-基本示例"))#legend_opts=opts.LegendOpts(pos_left="15%"),
    .set_series_opts(label_opts=opts.LabelOpts(formatter="{b}: {c}"))
#     .render("pie_base.html")
)
c.render_notebook()
image

内嵌饼图nested_pies

inner_x_data = ["直达", "营销广告", "搜索引擎"]
inner_y_data = [335, 679, 1548]
inner_data_pair = [list(z) for z in zip(inner_x_data, inner_y_data)]

outer_x_data = ["直达", "营销广告", "搜索引擎", "邮件营销", "联盟广告", "视频广告", "百度", "谷歌"]
outer_y_data = [335, 310, 234, 135, 1048, 251, 147, 102]
outer_data_pair = [list(z) for z in zip(outer_x_data, outer_y_data)]
(
    Pie(init_opts=opts.InitOpts())#theme=ThemeType.DARK 指定主题为黑色
    .add(
        series_name="访问来源",
        data_pair=inner_data_pair,#指定数据源
        radius=[0, "30%"],#半径 
        label_opts=opts.LabelOpts(position="inner"),#图形位置
    )
    .add(
        series_name="访问来源",
        radius=["40%", "55%"],
        data_pair=outer_data_pair,
        label_opts=opts.LabelOpts(
            position="outside",
            formatter="{a|{a}}{abg|}\n{hr|}\n {b|{b}: }{c} {per|{d}%}  ",#"{a} <br/>{b}: {c} ({d}%)" 文本样式
            background_color="#eee",  #背景颜色
            border_color="#aaa",  #边框颜色
            border_width=1,   #边框宽度
            border_radius=4,  #边界半径
            rich={
                "a": {"color": "#999", "lineHeight": 22, "align": "center"},
                "abg": {
                    "backgroundColor": "#e3e3e3",
                    "width": "100%",
                    "align": "right",
                    "height": 22,
                    "borderRadius": [4, 4, 0, 0],
                },
                "hr": {
                    "borderColor": "#aaa",
                    "width": "100%",
                    "borderWidth": 0.5,
                    "height": 0,
                },
                "b": {"fontSize": 16, "lineHeight": 33},#,"color":"#999"
#                 "c":{"color":"#999"},
                "per": {
                    "color": "#eee",
                    "backgroundColor": "#334455",
                    "padding": [2, 4],
                    "borderRadius": 2,
                },
            },
        ),
    )
    .set_global_opts(legend_opts=opts.LegendOpts(pos_left="left", orient="vertical",textstyle_opts={"color":"#999"}))#设置图例字体为白色
    .set_series_opts(
        tooltip_opts=opts.TooltipOpts(
            trigger="item", formatter="{a} <br/>{b}: {c} ({d}%)"    # 'item': 数据项图形触发,主要在散点图,饼图等无类目轴的图表中使用。
        )
    )
#     .render("nested_pies.html")
).render_notebook()
image

那个formatter配置项可以在这里了解:
{a}(系列名称),{b}(数据项名称),{c}(数值), {d}(百分比)
https://echarts.apache.org/zh/option.html#grid.tooltip.formatter

pyecharts中page的使用类似BI大屏展示

    page = Page(layout= Page.DraggablePageLayout, page_title= "大屏展示")
    # page = Page()
    page.add(
        bar_datazoom_slider(),
        line_markpoint(),
        pie_rosetype(),
        grid_mutil_yaxis(),
        liquid_data_precision(),
        table_base(),
    )
    page.render("temp.html")

然后自己点那开个temp.html文件进行拖拽,缩放,布局称自己想要的样子,最后点击save_config按钮,下载那个chart_config.json文件。

不带标题,修改配置文件为百分比

  • 修改json配置文件,改成百分比主要是为了页面自适应屏幕大小。
df = pd.read_json("chart_config.json")
df["width"] = df.width.apply(lambda x: x[:-2])
df["height"] = df.height.apply(lambda x: x[:-2])
df["top"] = df.top.apply(lambda x: x[:-2])
df["left"] = df.left.apply(lambda x: x[:-2])
# df.astype({'width':'float','height':'float','top':'float','height':'float'}).dtypes
df["width"]= df.width.apply(lambda x: str(round(float(x)/1256*100,4))+"%")#这里1256、640是你自己电脑屏幕的px值
df["height"] = df.height.apply(lambda x: str(round(float(x)/640*100*11/12,4))+"%")#如果出现右侧滑块,可适当进行缩放,这里按11/12的比例缩放
df["top"] = df.top.apply(lambda x: str(round(float(x)/640*100*11/12,4))+"%")
df["left"] = df.left.apply(lambda x: str(round(float(x)/1256*100,4))+"%")
df.to_json("chart_config2.json",orient = "records")
  • 修改好json数据之后,再运行这行代码
#第一个字段是前面拖拽的html文件名,cfg_file就是你刚save的json文件名,dest是你要生成的文件名
page.save_resize_html( 'temp.html', cfg_file= 'chart_config2.json',dest= '设置好位置后的BI看板.html')

加上标题,可自定义html

这样弄完有点问题就是背景页面可能和图片背景有违和感,你可以使用BeautifulSoup再修改下页面背景颜色啥的,相关网课链接https://www.bilibili.com/video/BV1KT4y1c7pb?p=22

from bs4 import BeautifulSoup
import os

#这里要先读取那个json文件

with open(os.path.join(os.path.abspath("."), "设置好位置后的BI看板.html"), 'r+', encoding="utf8") as html:
    html_bf = BeautifulSoup(html, "lxml")
    divs = html_bf.find_all("div")
    #读取config_json文件,循环写入到HTML
    for i in range(len(df_config)):
        width = df_config.loc[i,'width']
        height = df_config.loc[i,'height']
        top = df_config.loc[i,'top']
        left = df_config.loc[i,'left']
        divs[i+1]["style"] = f"width:{width};height:{height};position:absolute;top:{top};left:{left};"
        
    body = html_bf.find("body")
    body["style"]="background-color:#333333;"
#     div_title = "<div align=\"center\" style=\"width:100%;\">\n<span style=\"font-size:30px;font face=\'微软雅黑\';color :#FFFFFF\"><b>CD_NOW数据看板</b></div>"  # 修改页面背景色、追加标题
#     body.insert(0, BeautifulSoup(div_title, "lxml").div)
    html_new = str(html_bf)
    html.seek(0, 0)
    html.truncate()
    html.write(html_new)
    html.close()

当然也可以把那个json文件里面的值传到对应的样式代码中在HTML页面中进行布局,需要注意的是画图时指定好每个图表的chart_id,便于通过字符串格式进行指定对应图表位置。

注意点:(来自https://www.jianshu.com/p/47e8f056e5cf
1.由于图片的布局是根据chart_config.json中图片id的对应关系进行布局,因此每张图片均需要指定其id;
如:
Bar(init_opts=opts.InitOpts(chart_id=1))
2.在本次操作工程中,发现Table类中不包含init_opts参数,可以通过修改源码,在D:\Anaconda3\Lib\site-packages\pyecharts\component\table指定char_id,具体修改的代码如下:

class Table(ChartMixin):
    def __init__(self, page_title: str = CurrentConfig.PAGE_TITLE, js_host: str = "",chart_id=None):
        self.page_title = page_title
        self.js_host = js_host or CurrentConfig.ONLINE_HOST
        self.js_dependencies: OrderedSet = OrderedSet()
        self.js_functions: OrderedSet = OrderedSet()
        self.title_opts: ComponentTitleOpts = ComponentTitleOpts()
        self.html_content: str = ""
        self._component_type: str = "table"
        if chart_id:
            self.chart_id: str = chart_id
        else:
            self.chart_id: str = uuid.uuid4().hex

https://www.bilibili.com/video/BV1KT4y1c7pb 网课里有资源自己去取

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容