使数据分析和数据可视化能够更好地集中于同一平台,博主决定用Python.pyecharts库部分替代Apache ECharts的使用(主要是数据量较大的场景,也因此本篇的结果都仅以png图片展示)。

由于PyECharts版本迭代过于激进,各版本差异较大,经研究,博主采用思路相对清晰pythonic的2.0.5版本进行学习。

简约线条

学习使用pyecharts绘制一个简单柱状图。

没想到这个问题中最难的部分是如何导出png图片。经过尝试,在pyecharts 2.0和Python 3.9环境中,可用性好的只有如下示例代码中的方法,即利用类似于pyecharts.render.make_snapshot(snapshot_selenium.snapshot, chart.render(), output_path)的语句。使用这一方法前确保snapshot_selenium库已安装。

Python
from pyecharts.charts import Bar
from pyecharts import options as opts
from snapshot_selenium import snapshot
from pyecharts.render import make_snapshot
from pyecharts.globals import ThemeType


def try_bar():
    x_data = ['jan', 'feb', 'mar', 'apr', 'may']
    y_data = [10, 20, 15, 25, 30]

    # 创建柱状图
    # bar_chart = Bar()  # 最简单创建
    bar_chart = Bar(init_opts=opts.InitOpts(theme=ThemeType.ROMA))  # 试试其他theme
    bar_chart.add_xaxis(x_data)
    bar_chart.add_yaxis('sales', y_data)

    # 配置图表
    bar_chart.set_global_opts(
        title_opts=opts.TitleOpts(title='bar chart of monthly sales'),
        xaxis_opts=opts.AxisOpts(name='month'),
        yaxis_opts=opts.AxisOpts(name='sales (10k)'),
        legend_opts=opts.LegendOpts(pos_left='center', pos_top='top'),
        toolbox_opts=opts.ToolboxOpts()  # 显示工具
    )

    # 渲染图表
    bar_chart.render('chart_1.html')  # 生成html文件
    # make_snapshot(snapshot, bar_chart.render('chart_1.html'), 'chart_1.png')  # 生成html和png文件,不过后者速度明显慢一截,而且偶有bug,建议测试时不使用该语句


if __name__ == '__main__':
    try_bar()

成图如下:

极致色彩

网上的教程基本到上一节的内容就都完结了,所以,这下博主肯定学会所有pyecharts技巧了我什么都没学会,HELP!。现在进入实战环节。

案例一

使用seaborn提供的diamonds数据集,绘制钻石关于其重量(carat)、成色(clarity)、全深(depth)和价格(price)的散点图:横坐标为carat;纵坐标为price;点的颜色以clarity的种类进行区分,对应关系为:{'I1': '#A63130', 'SI2': '#D54A38', 'SI1': '#EDB74D', 'VS2': '#000000', 'VS1': '#A0B87B', 'VVS2': '#46966E', 'VVS1': '#306884', 'IF': '#7D7D7D'};点的大小以depth的大小进行区分;关于clarity和depth的图例位于图的右下角;图的尺幅为4: 3。

seaborn结果范例如下图所示。

……

别吵,博主正在思考。

……

……

此时无声胜有声。

你熟悉pyecharts的2.0版本吗?
嗯~ Pyecharts的2.0版本吗?对一些基础功能比较熟悉,想了解什么特别的东西吗?

和Ichigo讨论的结果:

Python
import pandas as pd
from pyecharts.charts import Scatter
from pyecharts import options as opts
import seaborn as sns
from snapshot_selenium import snapshot
from pyecharts.render import make_snapshot

# 加载钻石数据集
# diamonds = sns.load_dataset('diamonds')
diamonds = sns.load_dataset('diamonds', data_home='seaborn-data', cache=True)  # 太慢了,直接用本地的

# 定义颜色映射
color_map = {
    'I1': '#A63130', 'SI2': '#D54A38', 'SI1': '#EDB74D',
    'VS2': '#000000', 'VS1': '#A0B87B', 'VVS2': '#46966E',
    'VVS1': '#306884', 'IF': '#7D7D7D'
}

# 将depth分成6个类别,并映射到点的大小
diamonds['depth_category'] = pd.qcut(diamonds['depth'], 6, labels=False)
# 将类别映射到一组具体的大小
size_map = {i: size for i, size in enumerate([3, 4, 5, 6, 7, 8])}
diamonds['size'] = diamonds['depth_category'].map(size_map)

# 创建散点图对象
scatter = Scatter(init_opts=opts.InitOpts(width="800px", height="600px"))

# 设置全局选项
scatter.set_global_opts(
    title_opts=opts.TitleOpts(title="Diamonds"),
    legend_opts=opts.LegendOpts(pos_right="3%", pos_bottom="3%"),  # 似乎难以在图例中表达depth,暂时搁置这一问题
    tooltip_opts=opts.TooltipOpts(formatter="{a} <br/>{b}: {c}"),
    xaxis_opts=opts.AxisOpts(name="carat", type_="value"),  # 这个type_很重要,保证坐标轴是依照数值有序增长的
    yaxis_opts=opts.AxisOpts(name="price", type_="value"),
)

# 分组添加数据点,这里面的方法相当巧妙
for clarity, group in diamonds.groupby('clarity'):
    scatter.add_xaxis(group['carat'].tolist())
    scatter.add_yaxis(
        series_name=clarity,
        y_axis=[opts.ScatterItem(value=[x[0], x[1]], symbol_size=x[2]) for x in zip(group['carat'], group['price'], group['size'])],
        label_opts=opts.LabelOpts(is_show=False),
        itemstyle_opts=opts.ItemStyleOpts(color=color_map[clarity])
    )

# 渲染图表
make_snapshot(snapshot, scatter.render('diamonds.html'), 'diamonds.png')

成图如下:

案例二

使用seaborn提供的dots数据集,绘制视觉感知实验中条件对齐方式(align)、选择(choice)、从对齐点开始的时间(time)、刺激的一致性(coherence)和神经元放电率(firing_rate)的关系折线图:折线图分为左右两个子图展示,每个子图中展示单个align类别(align种类分为'dots'和'sacc')的其他指标关系;每个子图中,横坐标为time,纵坐标为firing_rate;每个子图有2*6条折线,由2种choice以及6种coherence交叉分组而得,其中choice控制了折线的宽度(类别'T1'的宽度是'T2'的8倍),coherence控制了折线的颜色(对应关系为:{0: '#F6B48F', 3.2: '#F37651', 6.4: '#E13342', 12.8: '#AD1759', 25.6: '#701F57', 51.2: '#35193E'});在右子图的右侧展示coherence和choice的图例;在每个子图的上方标注其所属的align(“align = ...”);控制纵坐标一致(0~80,间隔10)。

seaborn结果范例如下图所示。

PyECharts尝试:

Python
from pyecharts import options as opts
from pyecharts.charts import Line, Grid
import seaborn as sns
import pandas as pd
from snapshot_selenium import snapshot
from pyecharts.render import make_snapshot

# 加载数据集
data_dots = sns.load_dataset('dots', data_home='seaborn-data', cache=True)

# 定义颜色和线宽的映射
palette_map = {0: '#F6B48F', 3.2: '#F37651', 6.4: '#E13342', 12.8: '#AD1759', 25.6: '#701F57', 51.2: '#35193E'}
choice_size_map = {'T1': 8, 'T2': 1}  # T1为粗线,T2为细线

# 准备两个align类别的图表
charts = []
for align in ['dots', 'sacc']:
    line = Line()
    align_data = data_dots[data_dots['align'] == align]
    for choice in ['T1', 'T2']:
        for coherence in sorted(data_dots['coherence'].unique()):
            # 筛选对应choice和coherence的数据
            filter_data = align_data[(align_data['choice'] == choice) & (align_data['coherence'] == coherence)]
            # 设置折线图的属性
            line.add_xaxis(filter_data['time'].tolist())
            line.add_yaxis(
                series_name=f'{coherence}',
                y_axis=filter_data['firing_rate'].tolist(),
                is_smooth=True,
                linestyle_opts=opts.LineStyleOpts(width=choice_size_map[choice]),
                color=palette_map[coherence],  # 根据coherence选择颜色
                label_opts=opts.LabelOpts(is_show=False),
            )
    line.set_global_opts(
        legend_opts=opts.LegendOpts(pos_right="3%", pos_bottom="3%"),  # 还是调整一下图例的位置,因地制宜
        xaxis_opts=opts.AxisOpts(name='time', type_='value'),
        yaxis_opts=opts.AxisOpts(name='firing_rate', type_='value')
    )
    charts.append(line)

# 将两个align的图表放在并列布局
grid = Grid(init_opts=opts.InitOpts(width="1000px", height="600px"))  # 可以调整初始化选项来设置整体布局大小

# 偏移控制,防止元素重叠
align_map = {0: 'dots', 1: 'sacc'}
pos_map = {0: (5, 55), 1: (55, 5)}

for idx, chart in enumerate(charts):
    chart.set_global_opts(
        title_opts=opts.TitleOpts(title=f"Align={align_map[idx]}", pos_left=f"{pos_map[idx][0]}%"),  # 根据子图位置调整标题位置
        yaxis_opts=opts.AxisOpts(min_=0, max_=80, interval=10)  # 控制y轴一致
    )
    grid.add(
        chart,
        grid_opts=opts.GridOpts(
            pos_left=f'{pos_map[idx][0]}%',  # 左偏移
            pos_right=f'{pos_map[idx][1]}%',  # 右偏移
        ),
    )

# 渲染图表
make_snapshot(snapshot, grid.render('dots.html'), 'dots.png')

成图如下:

案例三

像素级复刻Apache ECharts官网的笛卡尔坐标系热力图:

https://echarts.apache.org/examples/zh/editor.html?c=heatmap-cartesian

Apache ECharts结果范例如下图所示。

PyECharts尝试:

Python
from pyecharts import options as opts
from pyecharts.charts import HeatMap, Grid
from snapshot_selenium import snapshot
from pyecharts.render import make_snapshot

# 准备数据
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'
]
days = [
    'Saturday', 'Friday', 'Thursday',
    'Wednesday', 'Tuesday', 'Monday', 'Sunday'
]
data = []  # 一大堆,就不附在这里了
data = [[item[1], item[0], item[2] or '-'] for item in data]

# 创建热力图
heatmap = HeatMap()
heatmap.set_global_opts(
    tooltip_opts=opts.TooltipOpts(position='top'),
    legend_opts=opts.LegendOpts(is_show=False),
    visualmap_opts=opts.VisualMapOpts(
        min_=0,
        max_=10,
        orient='horizontal',
        is_calculable=True,
        pos_left='center',
        pos_bottom='3%',
    ),
    xaxis_opts=opts.AxisOpts(type_='category', splitarea_opts=opts.SplitAreaOpts(is_show=True)),
    yaxis_opts=opts.AxisOpts(type_='category', splitarea_opts=opts.SplitAreaOpts(is_show=True)),
)
heatmap.add_xaxis(hours)
heatmap.add_yaxis(
    series_name='Punch Card',
    yaxis_data=days,
    value=data,
    label_opts=opts.LabelOpts(is_show=True),
)

grid = Grid(init_opts=opts.InitOpts(width='1000px', height='600px'))
grid.add(
    heatmap,
    grid_opts=opts.GridOpts(
        pos_bottom='25%',
    )
)

# 渲染图表
make_snapshot(snapshot, grid.render('heatmap_example.html'), 'heatmap_example.png')

成图如下:

可恶,标准配色盘不一样!宣布实验失败直接截图取色。

在上述代码的line 31后面增加一个VisualMapOpts的参数:range_color=['#F6EFA6', '#BF444C']

修改后的成图如下: