Related
I use matplotlib in python2.7 to get a figure with multiple trajectories. I am getting this figure as the output of my code:
However, as you see the title of each trajectory is not aligned with the corresponding color line. I played with the options of legend, but did not figure out the solution.
Do you have any idea?
Here is my code:
import numpy as np
import matplotlib.pyplot as plt
liste = range(1,13)
labels = ['3-3','3-4','3-5','3-6','4-3','4-4',
'4-5','4-6','5-3','5-4','5-5','5-6',
'6-3','6-4','6-5','6-6']
plt.figure(1)
plt.subplot(2,1,1)
for cnt, j in enumerate(labels):
a1 = range(100) # iter
a2 = np.random.randint(10,size=[100]) # dnn
a3 = np.ones((100)) # optimal
if cnt == 0:
plt.plot(a1,a3,label='BS', color='blue')
plt.plot(a1, np.divide(a3,a3), color='blue')
title_font = {'size':'11', 'color':'black', 'weight':'normal',
'verticalalignment':'bottom'}
plt.plot(a1,a2,label=j, color='red')
plt.xlabel('episode')
plt.ylabel('cost')
plt.grid(True)
lgd=plt.legend(bbox_to_anchor=(0., 1.0, 1.0, 0.102),
loc=3, ncol=len(liste), mode="expand", borderaxespad=0.)
plt.show()
The legend handles and labels do not fit into the legend box. You may see that when removing the mode="expand" label.
To get the legend entries take less space, use the handlelength, handletextpad and the columnspacing and play with the parameters until they fit. If that would still not be the case, using less columns is necessary. Set the ncols to a lower number.
E.g. with the following
lgd=plt.legend(bbox_to_anchor=(0., 1.02, 1.0, 0.102),
handlelength=0.9, handletextpad=0.3,columnspacing=0.7,
loc=3, ncol=9, mode="expand", borderaxespad=0.)
one would get an image with all legend entries fitting
I have a plot with a legend whose height is bigger than the axes height (like the result of the code below). Now, I would like to stretch the axes height in such a way that it ends at the legend's end.
import matplotlib.pyplot as plt
import numpy as np
t = np.arange(0., 10.5, 0.5)
fig, ax = plt.subplots()
for c in range(0, 20):
ax.plot(t, t*c/2, label='func {}'.format(c))
ax.legend(bbox_to_anchor=(1.01, 1), loc=2, borderaxespad=0.)
What I would like to have is something like that (it does not matter, if the bottom of the grid or the bottom of the ticks ends with the legend)
I tried to reach my goal with appending following code but without any effect (legend_h is always =1.0):
legend_h = ax.get_legend().get_window_extent().height
ax_height = ax.get_window_extent().height
if ax_height < legend_h:
fig.set_figheight(legend_h/ax_height * fig.get_figheight())
Further on, it would be nice if I could only change the properties of the axes itself and not of the whole figure.
Edit:
My main purpose is to run the figure generation from a script but I also tried it in the Ipython notebook. One try was also to temporarily store the figure before getting the heights and setting the new figure height. But that also did not produce correct results.
I think you can achieve what you want by simply adding plt.draw() to what you already have, e.g.
fig, ax = plt.subplots()
for c in range(0, 20):
ax.plot(t, t*c/2, label='func {}'.format(c))
ax.legend(bbox_to_anchor=(1.01, 1), loc=2, borderaxespad=0.)
plt.draw()
legend_h = ax.get_legend().get_window_extent().height
ax_height = ax.get_window_extent().height
if ax_height < legend_h:
fig.set_figheight(legend_h/ax_height * fig.get_figheight())
Update: Also, you can try (which should work from a script, and based on this answer):
import matplotlib.pyplot as plt
import numpy as np
t = np.arange(0., 10.5, 0.5)
fig, ax = plt.subplots()
for c in range(0, 20):
ax.plot(t, t*c/2, label='func {}'.format(c))
lgd = ax.legend(bbox_to_anchor=(1.01, 1), loc=2, borderaxespad=0.)
fig.tight_layout()
fig.savefig('script.png', bbox_extra_artists=(lgd,), bbox_inches='tight')
In principle, #Matt Pitkin's answer shows the right approach. However, rather than set_figheight one would use set_size_inches. The calculation also needs to include the figure margins, which can be obtained from the fig.subplotpars.
Additionally to the height, we can also set the width of the figure, such that the legend is included.
import matplotlib.pyplot as plt
import numpy as np
t = np.linspace(0,10); c=20
fig, ax = plt.subplots()
for c in range(0, 20):
ax.plot(t, t*c/2, label='func {}'.format(c))
bbox = (1.01,1)
ax.legend(bbox_to_anchor=bbox, loc=2, borderaxespad=0.)
fig.canvas.draw()
legend_h = ax.get_legend().get_window_extent().height
ax_height = ax.get_window_extent().height
if ax_height < legend_h:
w,h = fig.get_size_inches()
h =legend_h/fig.dpi/(fig.subplotpars.top-fig.subplotpars.bottom)
fig.set_size_inches(w,h)
# set width as well
w,h = fig.get_size_inches()
r = ax.get_legend().get_window_extent().width/fig.dpi/w
fig.subplots_adjust(right=1-1.1*r)
plt.show()
The picture below is when running this as a script.
In Ipython or jupyter, the figure will automatically be cropped or expanded, because the png shown is automatically saved using the bbox_inches='tight' option. Therefore, the width adjustment is not necessary for a jupyter notebook.
I have a correlation matrix hat I am trying to visualize with matplotlib. I can create a heatmap style figure just fine, but I am running into problems with how I want the labels. I'm not even sure if this is possible, but this is what I'm trying to do and can't seem to make it work:
My correlation matrix is 150 X 150. On either the x or y (or both...this doesn't matter) axis, I would like to group the labels and then simply label them with a color, or a white label on a color background.
To clarify, let's say I'd like to have 1-15 as "Group 1" and either simply be a Blue bar, or "Group 1" text on a blue bar. Then 16-20 as "Group 2" on a red bar, or simply a red bar. Etc, through all of the items in the matrix.
I have been failing at both grouping axis labels as well as getting any color on them. Any help would be greatly appreciated. My code is below, though it's quite basic and I don't know if it will help.
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import numpy as np
# COREELATION MATRIX TEST #
corr = np.genfromtxt(csv_path,delimiter=',')
fig = plt.figure()
ax1 = fig.add_subplot(111)
cmap = cm.get_cmap('jet', 30)
cax = ax1.imshow(corr, cmap=cmap)
ax1.grid(True)
plt.title('THIS IS MY TITLE')
fig.colorbar(cax, ticks=[-1,-0.8,-0.6,-0.4,-0.2,0.0,0.2,0.4,0.6,0.8,1.0])
plt.show()
You may create auxilary axes next to the plot and plot colored bar plots to them. Turning the axes spines off lets those bars look like labelboxes.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
# COREELATION MATRIX TEST #
corr = 2*np.random.rand(150,150)-1
# labels [start,end]
labels = np.array([[0,15],[16,36],[37,82],[83,111],[112,149]])
colors = ["crimson", "limegreen","gold","orchid","turquoise"]
fig, ax = plt.subplots()
im = ax.imshow(corr, cmap="Blues")
ax.set_title('THIS IS MY TITLE')
fig.colorbar(im, ticks=[-1,-0.8,-0.6,-0.4,-0.2,0.0,0.2,0.4,0.6,0.8,1.0])
# create axes next to plot
divider = make_axes_locatable(ax)
axb = divider.append_axes("bottom", "10%", pad=0.06, sharex=ax)
axl = divider.append_axes("left", "10%", pad=0.06, sharey=ax)
axb.invert_yaxis()
axl.invert_xaxis()
axb.axis("off")
axl.axis("off")
# plot colored bar plots to the axes
barkw = dict( color=colors, linewidth=0.72, ec="k", clip_on=False, align='edge',)
axb.bar(labels[:,0],np.ones(len(labels)),
width=np.diff(labels, axis=1).flatten(), **barkw)
axl.barh(labels[:,0],np.ones(len(labels)),
height=np.diff(labels, axis=1).flatten(), **barkw)
# set margins to zero again
ax.margins(0)
ax.tick_params(axis="both", bottom=0, left=0, labelbottom=0,labelleft=0)
# Label the boxes
textkw = dict(ha="center", va="center", fontsize="small")
for k,l in labels:
axb.text((k+l)/2.,0.5, "{}-{}".format(k,l), **textkw)
axl.text(0.5,(k+l)/2., "{}-{}".format(k,l), rotation=-90,**textkw)
plt.show()
In python matplotlib, there are two convention used to draw plots:
1.
plt.figure(1,figsize=(400,8))
2.
fig,ax = plt.subplots()
fig.set_size_inches(400,8)
Both have different ways of doing the same thing. eg defining axis label.
Which one is better to use? What is the advantage of one over the other?
Or what is the "Good Practice" for plotting a graph using matplotlib?
Although #tacaswell already gave a brief comment on the key difference. I will add more to this question only from my own experience with matplotlib.
plt.figure just creates a Figure (but with no Axes in it), this means you have to specify the ax to place your data (lines, scatter points, images). Minimum code should look like this:
import numpy as np
import matplotlib.pyplot as plt
# create a figure
fig = plt.figure(figsize=(7.2, 7.2))
# generate ax1
ax1 = fig.add_axes([0.1, 0.1, 0.5, 0.5])
# generate ax2, make it red to distinguish
ax2 = fig.add_axes([0.6, 0.6, 0.3, 0.3], fc='red')
# add data
x = np.linspace(0, 2*np.pi, 20)
y = np.sin(x)
ax1.plot(x, y)
ax2.scatter(x, y)
In the case of plt.subplots(nrows=, ncols=), you will get Figure and an array of Axes(AxesSubplot). It is mostly used for generating many subplots at the same time. Some example code:
def display_axes(axes):
for i, ax in enumerate(axes.ravel()):
ax.text(0.5, 0.5, s='ax{}'.format(i+1), transform=ax.transAxes)
# create figures and (2x2) axes array
fig, axes = plt.subplots(2, 2, figsize=(7.2, 7.2))
# four (2*2=4) axes
ax1, ax2, ax3, ax4 = axes.ravel()
# for illustration purpose
display_axes(axes)
Summary:
plt.figure() is usually used when you want more customization to you axes, such as positions, sizes, colors and etc. You can see artist tutorial for more details. (I personally prefer this for individual plot).
plt.subplots() is recommended for generating multiple subplots in grids. You can also achieve higher flexibility using 'gridspec' and 'subplots', see details here.
I am just plotting image in matplotlib but the labels in colorbar are not getting placed properly.I have placed 12 classes in colorbar but it is showing only some of them. I shall be thankful for the help.
from PIL import Image
from scipy import misc
import scipy
import pylab as pl
import numpy as np
from sklearn import svm
from sklearn import neighbors
import matplotlib.pyplot as plt
import matplotlib as mpl
def plotimage(labels,image,imname):#input - array of labels and image of 1..n classes
cmap = mpl.colors.ListedColormap(['r', 'g', 'b', 'c','k','y','m','w','#FFFF00','#FF8C00','#FF8EFC','#962404'])#only 12 classes at present
#need to create pallate and labels later out of this def
bounds = [0,1, 2, 3,4, 5,6,7,8,9,10,11,12,13]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
img = plt.imshow(image, cmap=cmap )
cbar = plt.colorbar(img, cmap=cmap) #norm=norm, boundaries=bounds, ticks=bounds
cbar.ax.set_yticklabels(labels)
plt.gcf().canvas.set_window_title("Classification")
#imsave("expr1_im", image.reshape(image.shape[0]/256,256), cmap=cmap)
#savefig(imname)
plt.title(imname)
plt.show()
b_imgarray = np.loadtxt("PlotData2001_Solar/SVM2001PixelArray.csv", delimiter=',')
labels = ['D','F','A','G','R','I','BS','ABG','W','BG','COAG','BSMIX']
a = np.reshape(b_imgarray, (1500,1500))
c = 'SVmimage'
plotimage(labels,a,c)
The image of misplaced bar legend. As you can see i have define 12 labels from 'D' to 'BSMIX' but it is showing upto ABG.
imshow usually restricts the range of your cmap to the values which appear in the image. In order to force imshow to use all values of your cmap use vmin and vmax:
img = plt.imshow(image, cmap=cmap, vmin=0, vmax=13)
In order to align the labels at the centers of the discrete colorbar fields, you have to define fixed number of ticks (to avoid sub-units) and use clim().
For example, if there are N discrete levels to be shown in the colorbar:
cbar = plt.colorbar(ticks=range(N))
plt.clim(-0.5, N - 0.5)
It's old question but it's still difficult to find the correct answer, and none of the above worked for me.
thanks a lot #plosner... I have little bit tweaked it it and it works...
def plotimage(labels,image,imname):#input - array of labels and image of 1..n classes
cmap = mpl.colors.ListedColormap(['r', 'g', 'b', 'c','y','k','#FFFF00','#00FF00','#ADBE34','#FF8C00','w','#FF8EFC','#00FFDD','#673E03','#ADEE12','m','#12ABDE','#22EEAB','#33DDEF'])#only 8 classes at present ,'#FF8EFC','#00FF00','#00FFDD''#962404' '#FF8EFC','#962404'
#need to create pallate and labels later out of this def
#cmap = mpl.colors.ListedColormap(['r', 'g', 'b', 'c','m','k','y','w','#7FFF00','#FF8C00','#FFFF00','#FE6D06','#6E6E6E','#04B4AE'])#only 8 classes at present ,'#FF8EFC','#00FF00','#00FFDD''#962404' '#FF8EFC','#962404'
bounds = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
print cmap.N
img = plt.imshow(image, cmap=cmap , vmin = 1, vmax =20)
cbar = plt.colorbar(img, cmap=cmap,norm=norm, boundaries=bounds, ticks=bounds) #norm=norm, boundaries=bounds, ticks=bounds
cbar.ax.set_yticklabels(labels)
plt.gcf().canvas.set_window_title("Classification")
#imsave("expr1_im", image.reshape(image.shape[0]/256,256), cmap=cmap)
#savefig(imname)
plt.title(imname)
plt.show()