文章目录

  • 背景
  • 方案一(题外话)
  • 方案二 自定义scale
    • 理论部分
    • 核心代码
    • 数据标注
  • 多组数据使用heatmap:自定义color bar的scale

背景

现在我对比了14个模型在某个数据集上的预测性能,得到了14个 R 2 R^2 R2值,但因为它取值范围是 ( − ∞ , 1 ] (-\infty,1] (−∞,1] ,所以有不少很负的值。

这是数据

data = [  0.9733,   0.    ,   0.0566,  -9.654 ,   0.1291,  -0.0926,  -0.0661,  -2.3085,   0.    , -10.63  ,   0.,  -3.797 ,-7.592 ,   0.    ]

做可视化的时候,其实是有点困难的。比如说用柱状图可视化成下面这种样子

Emmm 很难看啊,其实你负的再多,对我来说也没啥意义,我关注的主要是正半轴的部分,现在因为负数的太负,几个正数的 R 2 R^2 R2 ,反倒没啥区别了。这个时候我希望的就是,能不能把负半轴压缩压缩,把正半轴拉伸拉伸?

方案一(题外话)

我首先想到的方案是断裂坐标轴,这个可以用brokenaxes这个package实现(pip install)。这个我不展开讲,不是重点。

from brokenaxes import brokenaxesx = np.arange(14)
ylims = ((-10.8, -10.4), (-9.8, -9.6), (-7.8, -7.4), (-3.9, -3.6), (-2.4, -2.2), (-0.18, 1.14))
bax = brokenaxes(ylims=ylims, # 连续的区间hspace=0.05, # y轴裂口宽度wspace=0.05, # x轴裂口宽度despine=False, # 是否只显示单侧裂口(没有上坐标轴和右坐标轴)d=0.007, # 裂口斜线长度diag_color='red', # 裂口斜线颜色tilt=45 # 裂口斜线倾角)
# 使用bax绘图使用和matplotlib.axes._subplots.AxesSubplot绘图的方法基本一致
bax.bar(x, data[:, 2], facecolor='#ff9999', width=0.4)

方案二 自定义scale

理论部分

以上是matplotlib自带的scale,最常用的、也是默认设置,就是Linear Scale。Log scale适合可视化数量级很大或者很小(接近0)的数据,它实际上做的事情是把真实世界的 x x x,映射到图上的 lg ⁡ x \lg x lgx 的位置,但是刻度标注的还是 x x x

但是对于很大(小)的负数,因为定义域的问题, lg ⁡ x \lg x lgx 就无能为力了,Symmetric Log Scale做的是把正半轴的对数标度对称到负半轴上,让这些负数也能用对数标度可视化。

再来看看我们的需求,需要**压缩负数区间,拉伸 [ 0 , 1 ] [0,1] [0,1] 区间!**什么样的函数可以做到这一点呢?先大致画一下函数图像吧

我想大致应该这样,x轴在哪儿不重要,唯一的目标就是压缩负的,拉伸正的!像这样的函数,我们可能会想到 y = a x y=a^x y=ax ,或者是 y = 1 / ( b − x ) y=1/(b-x) y=1/(b−x) 之类的。就这俩而言,哪个更好呢?我想可能是分式函数好一点,因为 b b b 这个参数,可以帮助我们规定:越靠近 b b b 的地方,得增长越快。结合我们的需求, R 2 ⩽ 1 R^2\leqslant1 R2⩽1 恒成立,而 R 2 R^2 R2 越接近1,预测得越好, R 2 R^2 R2 从0.99提升到0.999的难度,比从0.9提升到0.99得难度大得多。所以我们可以把这个 b b b 设置成大于且接近1的一个值。实操中,我取了 1.6。

有了理论,现在看看怎么变现。FuncScale 这个类挺好,给了我们自定义scale的接口,这样我就不用自己重写一个Scale 类了。实际上呢,在代码中,我们也不用import这个FuncScale,因为它已经 register 了。我们要做的事情,就是像之前使用对数坐标那样(ax.set_yscale('log'))来设置自定义函数的标度,即ax.set_yscale('function', (forward, inverse))

这边多了forwardinverse,分别为映射函数和其反函数,结合我们的例子,就是
f o r w a r d ( x ) = 1 b − x \mathrm{forward}(x)=\frac{1}{b-x} forward(x)=b−x1​

i n v e r s e ( x ) = b − 1 y \mathrm{inverse}(x)=b-\frac{1}{y} inverse(x)=b−y1​

核心代码

核心部分讲完了,下面给出完整代码!

from matplotlib import pyplot as plt, font_manager as fm
import numpy as np
from matplotlib.ticker import NullFormatter, FixedLocatordef forward(x):x = 1 / (frac_b - x)return xdef inverse(x):x = frac_b - 1 / xreturn xdata = [  0.9733,   0.    ,   0.0566,  -9.654 ,   0.1291,  -0.0926,  -0.0661,  -2.3085,   0.    , -10.63  ,   0.,  -3.797 ,-7.592 ,   0.    ]
x = np.arange(14)
plt.rc('font', family='Times New Roman', size=15)
font_formula = fm.FontProperties(math_fontfamily='cm', size=20
)
font_text = {'size': 20}
yticks = [-11, -2.0, -0.5, 0, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0],colors = '#ff9999'
ylims = [-2000, 1.01]
bar_width = 0.4
frac_b = 1.6
text_skip = 0.03 # 标注的数据与柱状图顶(底)端间距
fig, ax = plt.subplots(tight_layout=True)ax.bar(x, data[:, ind + 1], facecolor=colors[ind], width=bar_width)
ax.set_xticks(x)
ax.set_xlabel('Model No.', labelpad=18, fontdict=font_text)
ax.set_ylabel(r'$R^2$', fontproperties=font_formula)
ax.set_yscale('function', functions=(forward, inverse))
ax.yaxis.set_minor_formatter(NullFormatter())
ax.yaxis.set_major_locator(FixedLocator(yticks[ind]))
ax.set_ylim(ylims[ind])
# 标注数据
for i in range(14):cur_r2 = data[i, ind + 1]cur_skip = frac_b - cur_r2 - 1 / (text_skip + 1 / (frac_b - cur_r2)) # 实际间距与图上间距转换if cur_r2 > 0:ax.text(x[i], cur_r2 + cur_skip, f'{cur_r2:.4}', ha='center')elif cur_r2 == 0:ax.text(x[i], cur_r2 + cur_skip, 'Divergence' if i == 8 else 'Unfitted', ha='center')else:ax.text(x[i], cur_r2 - cur_skip, f'{cur_r2:.4}', ha='center', va='top')
fig.set_size_inches([15.36, 7.57])

数据标注

上方代码的数据标注部分需要额外讲解一下。根据理论部分,实际中的 x x x (也即刻度值),在图上表现为 1 / ( b − x ) 1/(b-x) 1/(b−x),我现在想让每个数据都在图上距离bar的顶(底)端0.03个距离。但是我python中代码给的应该是实际距离,怎么办呢?这便是第44行代码(cur_skip=...)的作用。

假设我的bar高度是0.2,现在它顶部的刻度值就是0.2了,假如说我想在 0.2 + Δ x 0.2+\Delta x 0.2+Δx 的高度(刻度)标注我的数据,那么它图上的间距是多少呢?大约是
f o r w a r d ( 0.2 + Δ x ) − f o r w a r d ( 0.2 ) = 1 1.6 − ( 0.2 + Δ x ) − 1 1.6 − 0.2 \mathrm{forward}(0.2+\Delta x) - \mathrm{forward}(0.2)=\frac{1}{1.6-(0.2+\Delta x)}-\frac{1}{1.6-0.2} forward(0.2+Δx)−forward(0.2)=1.6−(0.2+Δx)1​−1.6−0.21​
从这个式子可以很明显看出来,如果bar不是高0.2了,但 Δ x \Delta x Δx 不变,那图上距离就会变了!

可以看到0.9733距离bar太远了,负值则距离bar太近了,因此必须根据bar的高度动态调整实际间距( Δ x \Delta x Δx ,代码中为cur_skip,使得每个bar对应的图上间距( Δ y \Delta y Δy,代码中为text_skip)相同。将式(3)一般化,有
f o r w a r d ( x + Δ x ) − f o r w a r d ( x ) = 1 b − ( x + Δ x ) − 1 b − x = Δ y \mathrm{forward}(x+\Delta x) - \mathrm{forward}(x)=\frac{1}{b-(x+\Delta x)}-\frac{1}{b-x}=\Delta y forward(x+Δx)−forward(x)=b−(x+Δx)1​−b−x1​=Δy
用给定的 Δ y \Delta y Δy 表示未知的 Δ x \Delta x Δx,有
Δ x = ( b − x ) − 1 Δ y + 1 b − x \Delta x=(b-x)-\frac{1}{\Delta y + \dfrac{1}{b-x}} Δx=(b−x)−Δy+b−x1​1​
这就是第44行代码的出处。

多组数据使用heatmap:自定义color bar的scale

现在我有不止一组数据集,而是四组。当然了可以画四个bar plot,但是我们也可以集成四张bar plot于一张heatmap中:

显然,这个也涉及了负数太负使得小的正数无法分辨的问题,需要自定义一下color bar。

原理跟之前一样;代码上,可以使用colors.FuncNorm 这个类(19-22行),vminvmax 分别指定color bar的下限和上限。

import seaborn as sns
import numpy as np
from matplotlib import pyplot as plt, font_manager as fm, colorsdef forward(x):x = 1 / (frac_b - x)return xdef inverse(x):x = frac_b - 1 / xreturn xdef comp_heatmap(ax):plt.rc('font', family='Times New Roman', size=15)plt.subplots_adjust(left=0.05, right=1)norm = colors.FuncNorm((forward, inverse),vmin=-11, vmax=1)mask = np.zeros_like(data)mask[:, [1, 8, 10, 13]] = 1mask = mask.astype(np.bool)with sns.axes_style('white'):ax = sns.heatmap(data, ax=ax, vmax=.3,mask=mask,annot=True, fmt='.4',annot_kws=font_annot,norm=norm,xticklabels=np.arange(14),yticklabels=np.arange(4),cbar=False,cmap='RdYlGn')cbar = ax.figure.colorbar(ax.collections[0])cbar.set_ticks([-11, -1.0, 0, 0.3, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0])# set tick labelsxticks = ax.get_xticks()ax.set_xticks(xticks)ax.set_xticklabels(xticks.astype(int), **font_tick)yticks = ax.get_yticks()ax.set_yticks(yticks)ax.set_yticklabels(['', '', '', ''])return axfont_formula = fm.FontProperties(math_fontfamily='cm', size=22
)
font_text = {'size': 22, 'fontfamily': 'Times New Roman'}
font_annot = {'size': 17, 'fontfamily': 'Times New Roman'}
font_tick = {'size': 18, 'fontfamily': 'Times New Roman'}
fig, axes = plt.subplots()
data = np.array([[  0.9848,   0.    ,   0.9504,  -0.8198,   0.9501,   0.9071,0.8598,   0.9348,   0.    ,   0.713 ,   0.    ,   0.669 ,0.6184,   0.    ],[  0.9733,   0.    ,   0.0566,  -9.654 ,   0.1291,  -0.0926,-0.0661,  -2.3085,   0.    , -10.63  ,   0.    ,  -3.797 ,-7.592 ,   0.    ],[  0.9676,   0.    ,   0.9331,   0.9177,   0.9401,   0.9352,0.9251,   0.7987,   0.    ,   0.5635,   0.    ,   0.5924,0.2456,   0.    ],[  0.9759,   0.    ,  -0.114 ,   0.1566,   0.0412,   0.3588,0.2605,  -0.5471,   0.    ,   0.2534,   0.    ,   0.5216,0.3784,   0.    ]])
frac_b = 1.5
ax = comp_heatmap(axes)
fig.set_size_inches([15.36, 7.57])

matplotlib中自定义scale——针对普通标度与colorbar相关推荐

  1. matplotlib中的色条colormap 及色带colorbar

    colormap 关键只有三个语句, 初始化 jet = cm = plt.get_cmap('Reds') cNorm = colors.Normalize(vmin=0, vmax=5) scal ...

  2. python使用matplotlib中的errorbar函数可视化误差条、并自定义数据点标记、数据点大小、数据点颜色、数据点边缘颜色、误差棒颜色、误差棒线条宽度、误差棒边界线长度、误差棒边界线厚度等

    python使用matplotlib中的errorbar函数可视化误差条.并自定义数据点标记.数据点大小.数据点颜色.数据点边缘颜色.误差棒颜色.误差棒线条宽度.误差棒边界线长度.误差棒边界线厚度等 ...

  3. seaborn箱图(box plot)可视化、并且使用matplotlib的meanprops函数在箱图中自定义均值标签、标签形状、标签大小、标签填充色彩、标签边缘颜色

    seaborn箱图(box plot)可视化.并且使用matplotlib的meanprops函数在箱图中自定义均值标签.标签形状.标签大小.标签填充色彩.标签边缘颜色 目录

  4. Python matplotlib可视化:自定义轴标签格式化函数(在轴刻度上添加自定义的数值以及符号形式)、使用自定义函数在Matplotlib中为坐标轴刻度添加自定义符号(例如,货币符号¥$等)

    Python matplotlib可视化:自定义轴标签格式化函数(在轴刻度上添加自定义的数值以及符号形式).使用自定义函数在Matplotlib中为坐标轴刻度添加自定义符号(例如,货币符号¥$等) 目 ...

  5. python自定义函数画图_python matplotlib自定义colorbar颜色条-以及matplotlib中的内置色条...

    自定义colorbar(draw colorbar without any mapple/plot) 参考:Customized Colorbars Tutorial api example code ...

  6. python matplotlib自定义colorbar颜色条-以及matplotlib中的内置色条

    文章目录 自定义colorbar(draw colorbar without any mapple/plot) contourf函数 为所有子图添加一个色带 推荐色带与自定义色带 PS: 传送门--自 ...

  7. matplotlib中文文档_python绘图库——Matplotlib及Seaborn使用(入门篇1)

    在数据分析过程中,数据及模型可视化是无可避免的,同时这也是展示我们数据分析成果的最佳方式.因此,熟悉掌握绘图库的使用,对精进我们的数据分析技能起着不可替代的作用. 今天,我们就来了解一下python强 ...

  8. Matplotlib中annotate详解

    原文:https://blog.csdn.net/wizardforcel/article/details/54782628 标注 基本标注 使用text()会将文本放置在轴域的任意位置. 文本的一个 ...

  9. Matplotlib中中文不显示问题

    我们在使用jupter进行数据分析的时候,会接触到Matplotlib这个库,它是用来进行可视化数据分析的,在一个图中,我们常常会加入一些中文来进行说明.当我们加入中文的时候会出现下图所示的样子: 可 ...

最新文章

  1. php 远程下载图片到本地
  2. c3p0 数据库连接池
  3. 71 Zabbix自定义触发器
  4. Image and video pencil sketch with OpenCV 3.X and CUDA
  5. LPC1768的SPI通讯
  6. Mysql Explain的简单使用
  7. 【Android进阶】Junit单元測试环境搭建以及简单有用
  8. 如何用Sql更新默认值
  9. css半透明渐变过渡效果
  10. 计算机怎么退出远程桌面连接,怎么退出远程桌面控制?远程桌面软件哪个好?
  11. 兰州理工大学计算机考研好考吗,兰州理工大学考研难吗
  12. 微信撤回消息在服务器可以看到吗,微信撤回消息可以查看了,对方撤回了什么一目了然...
  13. 若依对接企业微信JS-DK
  14. 独家强大情侣头像网名个性签名多功能工具微信小程序源码下载
  15. C++实现单例模式 —— 打印机
  16. 什么是瑞士加密谷Crypto Valley、CV Labs
  17. Processing 案例 | 郭锐文先生的 worms
  18. html弹出式登录窗口(DIV悬浮窗口)实现
  19. Rancher 和 KubeSphere 的对比
  20. Apache 服务器 SSL 证书到期-续期处理步骤

热门文章

  1. 9.如何抗住双11预约抢购活动
  2. 赛门铁克SEP架构部署十大常见问题解答
  3. 2460: [BeiJing2011]元素 有关线性基的理解
  4. 建国大业 《红》mp3下载
  5. 材料设计Materials Studio教程(简易课程)
  6. 关于md5的加密和解密的一些好的文章以及软件下载地址
  7. MyBatis-- 分页插件
  8. Ubuntu 20.04 安装CppAD 与 Ipopt 3.13.2避坑指南
  9. 中英广播电视新媒体战略比较研究
  10. 与电台同步的网上广播系统