将matplotlib注释与colorbar对齐

时间:2017-10-02 22:06:46

标签: python matplotlib svg plot

我想绘制一个颜色条,其中一个字符串(序列)与其底部对齐。我在下面发布了一个工作示例,但它并不是一个理想的解决方案,因为有一些硬编码值。 有没有其他/更好/更聪明的方法可以做到这一点?动态调整各种字体大小是理想的。

要求:

  1. 文本必须可以在Web浏览器中选择为一个连续的行(webbrowsers中的svg格式允许我们单击并拖动选择matplotlib文本)
    • 我想选择文字的各个部分。即,如果我根据我的分数列表看到一个特别有趣的区域,我想选择那些部分并将其复制到我的剪贴板
  2. 文本必须与其背后的颜色条完全对齐。
  3. 如果你要使每个字母成为一个轴,那么它们就不再可以选择作为一个连续的字符串(实际上是换行符将它们分开),这是我不想要的。

    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")
    

    enter image description here

    基本上我希望能够查看得分列表的颜色栏表示,并手动选择我感兴趣的区域,并要求得分列表很好地与我的图表文本对齐。

1 个答案:

答案 0 :(得分:0)

请注意,以下内容实际上并不会产生正确的svg输出。但是仍然可以将它用于png输出。

一个选项是根据给定的文本计算所需的图形宽度。因此,我们的想法是首先使用某种字体和大小创建文本,然后“测量”它的大小,并根据该数字设置轴宽度。现在轴宽度本身不是直接可用的,而是根据figwidth = axes width/(margin_right - margin_left)设置的图宽度和边距。然后我们将文本宽度设置为轴宽度。   文本本身位于轴的中间,中心对齐,这样一旦轴的宽度与文本相同(几乎)完全对齐。

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()

enter image description here

现在使用自由输入参数并观察变化相对容易 请注意,原则上还可以自动确定轴和文本之间的填充(此处硬编码为0.08)以及轴和刻度标签之间(此处硬编码为10个点);但由于我不知道是否需要,我把它留了出来。