2017-10-02 2 views
1

Je voudrais tracer une barre de couleurs, avec une chaîne (séquence) alignée sur le bas de celle-ci. J'ai un exemple de travail posté ci-dessous, mais ce n'est pas vraiment une solution idéale car il y a quelques valeurs codées en dur. Y a-t-il d'autres façons/mieux/plus intelligentes de faire cela? L'adaptation dynamique de différentes tailles de police serait idéale.Alignement de l'annotation matplotlib sur la barre de couleurs

exigences:

  1. Le texte doit être sélectionné dans un navigateur Web comme une ligne continue (format svg webbrowsers permet de cliquer et faire glisser sélectionner le texte de matplotlib)
    • Je voudrais sélectionnez les sections du texte. à savoir, si je vois une région particulièrement intéressante basée sur mon scorelist, je voudrais que sélectionner les sections et le copier dans mon presse-papiers
  2. Le texte doit aligner parfaitement à la colorbar derrière elle.

Si vous deviez faire cocher chaque lettre, elles ne seraient plus sélectionnables comme une seule chaîne continue (en fait, une nouvelle ligne les sépare) que je ne veux pas.

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

Fondamentalement, je veux être en mesure de regarder la représentation colorbar du scorelist, et certaines régions manuellement que je trouve intéressant, et les obligeaient scorelist à aligner mon texte sur le graphique bien .

Répondre

0

Notez que ce qui suit ne produit pas réellement la bonne sortie svg. On peut toujours l'utiliser pour la sortie PNG.

Une option consiste à calculer la largeur de figure nécessaire en fonction du texte donné. L'idée est donc de créer d'abord le texte avec une police et une taille, puis de "mesurer" sa taille et, en fonction de ce nombre, de définir la largeur des axes. Maintenant, la largeur des axes elle-même n'est pas directement disponible, mais plutôt en fonction de la largeur de la figure et des marges définies, conformément à figwidth = axes width/(margin_right - margin_left). Nous définirions alors la largeur du texte comme la largeur des axes. Le texte lui-même est positionné au milieu des axes avec alignement du centre, de sorte qu'une fois que les axes ont la même largeur que le texte, ils s'alignent (presque) parfaitement.

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

Maintenant, il est relativement facile de jouer avec les paramètres d'entrée libres et observer les changements.
Notez qu'en principe on pourrait aussi déterminer automatiquement le remplissage entre les axes et le texte (ici codé en dur à 0.08) et entre les axes et les étiquettes (ici codées en dur à 10 points); mais comme je ne sais pas si cela est nécessaire, je l'ai laissé de côté.

+0

Impressionnant Je n'avais aucune idée que vous pouviez saisir la largeur de la figure! cependant ceci est légèrement sur de longues séquences (2000+ lettres) au moins sur mon mac. Une chose est que je dois faire est 'matplotlib.use ('webagg')' parce que je suis sur un mac. mais je ne pense pas que ce soit le problème. Où avez-vous obtenu les valeurs de 'subplotpars'? est un 'left of 0.1' et un' right of 0.9' juste par défaut dans matplotlib? – danpharrell

+0

Je ne sais pas dans quelle mesure il peut être légèrement désactivé. Est-ce la même chose lors de l'exportation vers svg ou png? Les 0.1 et 0.9 sont juste mon choix, mais proche des valeurs par défaut de 0.125 et 0.9.Pour des séquences plus grandes, des valeurs plus petites des marges auraient du sens. Vous pouvez bien sûr les définir en pouces et calculer les nombres relatifs à la volée, si vous le souhaitez. – ImportanceOfBeingErnest

+0

Le problème est certainement quelque chose de différent entre svg, et png. L'intrigue de png fonctionne parfaitement, mais l'intrigue de svg est éteinte, montrer ici: https://imgur.com/a/PhQ1m il est également court de l'autre côté. Dans le svg, la couleur de la barre de couleur semble également légèrement décalée aussi ... bizarre. – danpharrell