2015-08-14 4 views
10

Может ли кто-нибудь узнать, можно ли повернуть легенду на участке в matplotlib? Я сделал простой сюжет с приведенным ниже кодом и отредактировал график в краске, чтобы показать, что я хочу.Легенда легенды Matplotlib

plt.plot([4,5,6], label = 'test') 
ax = plt.gca() 
ax.legend() 
plt.show() 

http://www.abraham.dreamhosters.com/Capture.PNG

+4

Был вопрос о списке Matplotlib рассылки, и ответ был, что это не представляется возможным с текущей (2009) реализации легенды (https://www.mail-archive.com/[email protected]/msg10474.html). Я никогда не сталкивался с таким вариантом при использовании более поздних версий matplotlib, возможно, это еще не возможно. –

+0

Спасибо Крис - я нашел ту же тему и должен был включить это в мой оригинальный пост. –

ответ

1

я провел несколько часов, сколы прочь на этом вчера, и сделал немного прогресса, так что я поделюсь ниже вместе с некоторыми предложениями двигаться вперед.

Во-первых, кажется, что мы можем вращать и переводить ограничительную рамку (bbox) или рамку вокруг легенды. В первом примере ниже вы можете увидеть, что может применяться transform, хотя и требует некоторых странно больших чисел перевода после применения поворота на 90 градусов. Но на самом деле проблемы с сохранением переведенного кадра легенды в файл изображения, поэтому мне пришлось сделать снимок экрана из ноутбука IPython. Я также добавил некоторые комментарии.

import matplotlib.pyplot as plt 
%matplotlib inline 
import numpy as np 
import matplotlib.transforms 

fig = plt.figure() 
ax = fig.add_subplot('121') #make room for second subplot, where we are actually placing the legend 
ax2 = fig.add_subplot('122') #blank subplot to make space for legend 
ax2.axis('off') 
ax.plot([4,5,6], label = 'test') 

transform = matplotlib.transforms.Affine2D(matrix=np.eye(3)) #start with the identity transform, which does nothing 
transform.rotate_deg(90) #add the desired 90 degree rotation 
transform.translate(410,11) #for some reason we need to play with some pretty extreme translation values to position the rotated legend 

legend = ax.legend(bbox_to_anchor=[1.5,1.0]) 
legend.set_title('test title') 
legend.get_frame().set_transform(transform) #This actually works! But, only for the frame of the legend (see below) 
frame = legend.get_frame() 
fig.subplots_adjust(wspace = 0.4, right = 0.9) 
fig.savefig('rotate_legend_1.png',bbox_extra_artists=(legend,frame),bbox_inches='tight', dpi = 300) #even with the extra bbox parameters the legend frame is still getting clipped 

enter image description here

Далее, я думал, что было бы разумно, чтобы исследовать get_methods() других компонентов легенды. Вы можете сортировать эти вещи с помощью dir(legend) и legend.__dict__ и так далее. В частности, я заметил, что вы можете сделать это: legend.get_title().set_transform(transform), что, по-видимому, означает, что мы можем перевести текст легенды (а не только рамку, как указано выше). Давайте посмотрим, что происходит, когда я пытался что:

название
fig2 = plt.figure() 
ax = fig2.add_subplot('121') 
ax2 = fig2.add_subplot('122') 
ax2.axis('off') 
ax.plot([4,5,6], label = 'test') 

transform = matplotlib.transforms.Affine2D(matrix=np.eye(3)) 
transform.rotate_deg(90) 
transform.translate(410,11) 

legend = ax.legend(bbox_to_anchor=[1.5,1.0]) 
legend.set_title('test title') 
legend.get_frame().set_transform(transform) 
legend.get_title().set_transform(transform) #one would expect this to apply the same transformation to the title text in the legend, rotating it 90 degrees and translating it 

frame = legend.get_frame() 
fig2.subplots_adjust(wspace = 0.4, right = 0.9) 
fig2.savefig('rotate_legend_1.png',bbox_extra_artists=(legend,frame),bbox_inches='tight', dpi = 300) 

enter image description here

Легенда кажется, исчезли в скриншоте из записной IPython. Но, если мы посмотрим на сохраненный файл название легенды теперь в нижнем левом углу, и, кажется, игнорирует компонент вращения преобразования (почему?):

enter image description here

Я имел аналогичные технические трудности с такой подход:

bbox = matplotlib.transforms.Bbox([[0.,1],[1,1]]) 
trans_bbox = matplotlib.transforms.TransformedBbox(bbox, transform) 
legend.set_bbox_to_anchor(trans_bbox) 

Другие замечания и предложения:

  1. это может быть разумная идея копаться в различиях в поведении будет между заголовком легенды и объектами кадра - почему они оба принимают преобразования, но только кадр принимает поворот? Возможно, можно было бы подклассировать объект легенды в исходном коде и внести некоторые корректировки.
  2. Нам также необходимо найти решение для того, чтобы рамка с повернутой/переведенной легендой не сохранялась на выходе, даже после следующих различных предложений по SO (т. Е. Matplotlib savefig with a legend outside the plot).
+0

Спасибо за изучение этого! –

1

Я подошел к аналогичной проблеме и решил ее, написав функцию legendAsLatex, которая генерирует латексный код, который будет использоваться как метка оси y. Функция собирает цвет, маркер, стиль линии и метку, предоставленную функции графика. Это требует включения латекса и загрузки необходимых пакетов. Вот код для генерации вашего сюжета с дополнительными кривыми, которые используют как вертикальную ось.

from matplotlib import pyplot as plt 
import matplotlib.colors as cor 

plt.rc('text', usetex=True) 
plt.rc('text.latex', preamble=r'\usepackage{amsmath} \usepackage{wasysym}'+ 
    r'\usepackage[dvipsnames]{xcolor} \usepackage{MnSymbol} \usepackage{txfonts}') 

def legendAsLatex(axes, rotation=90) : 
    '''Generate a latex code to be used instead of the legend. 
     Uses the label, color, marker and linestyle provided to the pyplot.plot. 
     The marker and the linestyle must be defined using the one or two character 
      abreviations shown in the help of pyplot.plot. 
     Rotation of the markers must be multiple of 90. 
    ''' 
    latexLine = {'-':'\\textbf{\Large ---}', 
     '-.':'\\textbf{\Large --\:\!$\\boldsymbol{\cdot}$\:\!--}', 
     '--':'\\textbf{\Large --\,--}',':':'\\textbf{\Large -\:\!-}'} 
    latexSymbol = {'o':'medbullet', 'd':'diamond', 's':'filledmedsquare', 
     'D':'Diamondblack', '*':'bigstar', '+':'boldsymbol{\plus}', 
     'x':'boldsymbol{\\times}', 'p':'pentagon', 'h':'hexagon', 
     ',':'boldsymbol{\cdot}', '_':'boldsymbol{\minus}','<':'LHD', 
     '>':'RHD','v':'blacktriangledown', '^':'blacktriangle'} 
    rot90=['^','<','v','>'] 
    di = [0,-1,2,1][rotation%360//90] 
    latexSymbol.update({rot90[i]:latexSymbol[rot90[(i+di)%4]] for i in range(4)}) 
    return ', '.join(['\\textcolor[rgb]{'\ 
      + ','.join([str(x) for x in cor.to_rgb(handle.get_color())]) +'}{' 
      + '$\\'+latexSymbol.get(handle.get_marker(),';')+'$' 
      + latexLine.get(handle.get_linestyle(),'') + '} ' + label 
       for handle,label in zip(*axes.get_legend_handles_labels())]) 

ax = plt.axes() 
ax.plot(range(0,10), 'b-', label = 'Blue line') 
ax.plot(range(10,0,-1), 'sm', label = 'Magenta squares') 
ax.set_ylabel(legendAsLatex(ax)) 

ax2 = plt.twinx() 
ax2.plot([x**0.5 for x in range(0,10)], 'ro', label = 'Red circles') 
ax2.plot([x**0.5 for x in range(10,0,-1)],'g--', label = 'Green dashed line') 
ax2.set_ylabel(legendAsLatex(ax2)) 

plt.savefig('legend.eps') 

plt.close() 

Рисунок генерируется код:

enter image description here

+0

Это отлично. В итоге я использовал подобный, но гораздо менее общий код для моих цифр. Я включу некоторые из ваших идей в свою функцию. –

+0

Спасибо, @MadPhysicist. Вы можете проверить версию, которую я только что загрузил. Это проще, правильно относится к цветам и принимает больше углов поворота. – bmello

+0

Не уверен, если это имеет значение, но вы можете 'rot90' в dict, введенный' rotation% 360' вместо того, чтобы проходить через этого монстра 'if'. –

Смежные вопросы