为使数据分析和数据可视化能够更好地集中于同一平台,博主决定用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
库已安装。
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
结果范例如下图所示。
……
别吵,博主正在思考。
……
……
此时无声胜有声。
和Ichigo讨论的结果:
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尝试:
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尝试:
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']
修改后的成图如下: