简体   繁体   English

将Matplotlib注释与颜色栏对齐

[英]Aligning matplotlib annotation to colorbar

I'd like to plot a colorbar, with a string(sequence) aligned to the bottom of it. 我想绘制一个彩条,并在其底部对齐一个字符串(序列)。 I have a working example posted below, but it's not really an ideal solution because there are a few hardcoded values. 我在下面发布了一个工作示例,但这并不是一个理想的解决方案,因为有一些硬编码值。 Are there any other/better/smarter ways that I could do this? 我还有其他/更好/更智能的方式可以做到这一点吗? Accommodating various font sizes dynamically would be ideal. 动态地适应各种字体大小将是理想的。

requirements: 要求:

  1. The text must be selectable in a web browser as one continuous line (svg format in webbrowsers allows us to click and drag select matplotlib text) 文本必须在Web浏览器中作为连续的一行可以选择(webbrowsers中的svg格式允许我们单击并拖动选择的matplotlib文本)
    • I'd like to select the sections of the text. 我想选择文本的各个部分。 ie, if i see a particularly interesting region based on my scorelist, i'd like to select that sections and copy it to my clipboard 即,如果我根据分数列表看到一个特别有趣的区域,我想选择该部分并将其复制到我的剪贴板中
  2. The text must align perfectly to the colorbar behind it. 文字必须与它后面的颜色条完全对齐。

If you were to make each letter an axes tick, they would no longer be selectable as a single continuous string (actually a newline separates them) which I don't want. 如果要使每个字母成为一个轴刻度,它们将不再是我不希望选择的单个连续字符串(实际上是换行符分隔它们)。

import numpy as np
import matplotlib.pyplot as plt
from numpy import array
from mpl_toolkits.axes_grid1 import make_axes_locatable


myseq = "AERGERGEREKLKLKLLLKKLWEWRKLEWRLWKERLKKLYKLTELWLKLRWELKHLW"
scorelist = [np.random.randint(0,5) for x in myseq ]

# SET FIGURE SIZE, Not ideal, but sort of works
fig = plt.figure(figsize=(len(myseq)/6.64175,5)) # MAGIC NUMBER?
ax = plt.subplot()
scorearray = array([scorelist,scorelist])

# Plotting the colorbar
cmap = plt.cm.rainbow
im = ax.imshow(scorearray, extent=[-0.5, len(scorelist)-0.5, 0, 5], cmap=cmap)
divider = make_axes_locatable(ax)
cax = divider.append_axes("top", size="5%", pad=0.04)
cbar = plt.colorbar(im, cax=cax, orientation='horizontal')
cbar.ax.xaxis.set_ticks_position('top')
ax.yaxis.set_visible(False)
ax.text(-0.5,-1.1,myseq, size=14, name='Courier new')

# Use 1 indexing for human counting
ax.set_xticks([0] + list(np.arange(9, len(myseq), 10)));
ax.set_xticklabels(['1'] + [str(i) for i in range(10, len(myseq), 10)]);
ax.set_xticks(np.arange(-0.5, len(myseq), 1), minor=True);

ax.grid(which='minor', color='w', linestyle='-', linewidth=2)
plt.rcParams['svg.fonttype'] = 'none'
# plt.show()
plt.savefig("alignment.svg")

在此处输入图片说明

Basically I want to be able to look at colorbar representation of the scorelist, and manually select regions that I find interesting, and require the scorelist to align to my on-graph text nicely. 基本上,我希望能够查看评分表的颜色条表示形式,并手动选择我觉得有趣的区域,并要求评分表与我的图上文字很好地对齐。

Note that the following does not actually produce the correct svg output. 请注意,以下内容实际上不会产生正确的svg输出。 One may still use it for png output though. 虽然有人可能仍将其用于png输出。

An option is to calculate the needed figure width according to the given text. 一种选择是根据给定的文本计算所需的图形宽度。 So the idea is to first create the text with some font and size, then "measure" how large it is and according to that number set the axes width. 因此,想法是先创建具有某种字体和大小的文本,然后“测量”它的大小,然后根据该数字设置轴的宽度。 Now the axes width itself is not directly available, but rather subject to the figure width and the margins being set, according to figwidth = axes width/(margin_right - margin_left) . 现在,轴宽度本身不是直接可用的,而是取决于figwidth = axes width/(margin_right - margin_left)设置的图形宽度和边距。 We would then set the text width as the axes width. 然后,我们将文本宽度设置为轴宽。 The text itself is positionned in the middle of the axes with center alignment, such that once the axes has the same width than the text they (almost) perfectly align. 文本本身以中心对齐的方式放置在轴的中间,这样,一旦轴的宽度与文本的宽度相同,它们(几乎)就会完全对齐。

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable

#### free input parameters ####
figheight = 4 # inch
subplotpars = dict(left=0.1, right=0.9)
fontsize = 16
fontname = 'Courier new' # need to choose a monospace font here

myseq = "AERGERGEREKLKLKLLLKKLWEWRKLEWRLWKERLKKLYKLTELWLKLRWELKHLW"
scorelist = [np.random.randint(0,5) for x in myseq ]

#### automatic plotting ####
fig = plt.figure(figsize=(6,figheight)) # at this point only height will matter
fig.subplots_adjust(**subplotpars)
ax = plt.subplot()
scorearray = np.array([scorelist,scorelist])

# Plotting the colorbar
cmap = plt.cm.rainbow
im = ax.imshow(scorearray, extent=[-0.5, len(scorelist)-0.5, 0, 5], cmap=cmap)
divider = make_axes_locatable(ax)
cax = divider.append_axes("top", size="5%", pad=0.04)
cbar = plt.colorbar(im, cax=cax, orientation='horizontal')
cbar.ax.xaxis.set_ticks_position('top')
ax.yaxis.set_visible(False)

# position text centered at middle of axes
text = ax.text(0.5,-0.08, myseq, size=fontsize, name=fontname, 
               transform=ax.transAxes, va="top", ha="center")
# draw canvas to fix positions of elements
fig.canvas.draw()
bbox = text.get_window_extent()
# calculate figure width according to width of text
figwidth=bbox.width/float(fig.dpi)/(subplotpars["right"]-subplotpars["left"])
fig.set_size_inches((figwidth, fig.get_size_inches()[1]))


ax.set_xticks([0] + list(np.arange(9, len(myseq), 10)));
ax.set_xticklabels(['1'] + [str(i) for i in range(10, len(myseq), 10)]);
ax.set_xticks(np.arange(-0.5, len(myseq), 1), minor=True);
ax.xaxis.set_tick_params(pad=10)

ax.grid(which='minor', color='w', linestyle='-', linewidth=2)
plt.rcParams['svg.fonttype'] = 'none'
plt.show()

在此处输入图片说明

Now it's relatively easy to play with the free input parameters and observe the changes. 现在,使用免费的输入参数并观察更改相对容易。
Note that in principle one could also automatically determine the padding between axes and text (here hardcoded to 0.08) and between axes and ticklabels (here hardcoded to 10 points); 请注意,原则上也可以自动确定轴与文本之间的填充(此处硬编码为0.08)以及轴与刻度标签之间的填充(此处硬编码为10点); but as I don't know if that is needed I left that out. 但由于我不知道是否需要这样做,我将其遗漏了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM