I have written a class to plot some data points. I used seaborn to make kernel density plot and it caused that (1) the frame gets disappeared and I would like a rigid frame and (2) there are grids in the plot with (3)a background colour which I would like to get rid of them. How should it be done? In addition, how could I get star-shape and polygon-shape markers for the scatter plot ?
import seaborn
import pandas
import pylab as P
import numpy as np
class PlotLocus(object):
def __init__(self, colorX, colorY, colorpX, colorpY ,excluded_points,lcolorx1,lcolorx2,lcolory1,lcolory2,correspondence_Matrix):
self.exarr=excluded_points #scatter points excluded by kde
self.colorx=colorX
self.colory=colorY
self.colorpx=colorpX
self.colorpy=colorpY
r=np.arange(self.colorx.shape[0])
self.arr=np.setxor1d(r,self.exarr)
self.lx1=lcolorx1
self.lx2=lcolorx2
self.ly1=lcolory1
self.ly2=lcolory2
correspondence_indicies = np.where(M > 0.99)
self.colorx_corr=self.colorx[correspondence_indicies[0]]
self.colory_corr=self.colory[correspondence_indicies[0]]
self.colorpx_corr=self.colorpx[correspondence_indicies[1]]
self.colorpy_corr=self.colorpy[correspondence_indicies[1]]
def plot_before_colors(self):
fig=P.figure(1, figsize=(8,8), dpi=100)
ax = fig.add_subplot(111)
X=np.vstack((self.colorx, self.colory)).T
data = pandas.DataFrame(X, columns=["X", "Y"])
seaborn.kdeplot(data.X,data.Y,bw='scott',shade=False, cmap="Purples")
ax.tick_params(axis='both', which='major', direction='in', length=6, width=2)
ax.scatter(self.colorx[self.exarr], self.colory[self.exarr], s=30, c='g', marker='o', edgecolors='k',facecolors='none')
ax.scatter(self.colorx, self.colory ,marker='.',s=15,color='b')
ax.scatter(self.colorpx, self.colorpy, s=15, c='r', marker='d', edgecolor='r')
for i in range(len(self.colorx_corr)):
ax.annotate("",
xy=(self.colorpx_corr[i], self.colorpy_corr[i]), xycoords='data',
xytext=(self.colorx_corr[i], self.colory_corr[i]), textcoords='data',
arrowprops=dict(arrowstyle="->",
connectionstyle="arc3"),
color='0.3'
)
ax.set_xlabel("%s - %s"%(self.lx1,self.lx2), size='medium')
ax.set_ylabel("%s - %s"%(self.ly1,self.ly2), size='medium')
ax.set_aspect('auto')
if __name__ == "__main__":
colorx=np.array([0.4,0.5,-0.3,1.5,0.91,0.66,0.59,-0.11,-0.08,0.12])
colory=np.array([0.22,-1.15,0.44,0.7,-0.65,-0.21,0.8,-1.1,1.01,0.8])
colorpx=np.array([0.48,0.45,-0.38,0.5,0.98,0.62,0.77,-0.15,-0.12,0.8])
colorpx=np.array([0.48,0.45,-0.38,0.5,0.98,0.62,0.77,-0.15,-0.12,0.8,1.8])
colorpx=np.array([0.48,0.45,-0.38,0.5,0.98,0.62,0.77,-0.15,-0.12,0.8,1.8,2.4])
colorpy=np.array([0.26,-0.98,-0.1,0.66,-0.7,-0.5,0.84,-0.88,-1.2,0.9,-2.1,1.3])
lcolorx1='u'
lcolorx2='i'
lcolory1='i'
lcolory2='g'
M=np.zeros((10,12),float)
M[1,4]=1
M[3,5]=1
M[9,7]=1
M[0,2]=1
M[4,10]=1
p=PlotLocus(colorx,colory,colorpx,colorpy,np.array([2,6,8]),lcolorx1,lcolorx2,lcolory1,lcolory2,M)
p.plot_before_colors()
P.show()
You can remove the grey background and white grid by using seaborn.set_style(style='white') at the beginning of your code. This will also add a rigid black border to your plot.
For the markers you can get star shapes using marker='*' in the scatter call or by using the matplotlib.markers api.
Below is the plot I get if I add the seaborn.set_style call to your code, I've not changed the markers as I don't know which markers you wish to change.
The accepted answer doesn't work with recent seaborn versions.
Use ax.set_facecolor('white') instead of seaborn.set_style(style='white').
Related
I want to make a plot with a grid of thumbnails on the left and a line plot on the right. Here is a minimal example
import numpy as np
from matplotlib import pyplot as plt
### This can change at runtime
n_grid = 4
### Grid of thumbnails
fig = plt.figure(figsize=(20,10.2))
for i in range(n_grid):
for j in range(n_grid):
ax = plt.subplot2grid(shape=(n_grid, 2*n_grid), loc=(i,j))
plt.imshow(np.random.random((16,16)))
ax.set_axis_off()
### Line plot
ax = plt.subplot2grid(shape=(n_grid, 2*n_grid), loc=(0,n_grid), rowspan=n_grid-1, colspan=n_grid)
plt.plot(np.cumsum(np.random.random(100)), label='Random Sum')
plt.xlim([0, 100])
plt.ylim(0,50)
plt.xlabel('Number', fontsize=12)
plt.ylabel('Sum', fontsize=12)
plt.figtext(0.5, 0.01, f'Unique identifier', ha='center', va='baseline')
#plt.tight_layout()
plt.subplots_adjust(left=0.01, bottom=0.03, right=0.99, top=0.99, wspace = 0.06, hspace=0.06)
plt.savefig('plot_1.png', dpi=96)
The problem is that the yticklabels and ylabel stick over the center into the area of the thumbnails. The lineplot on the right is too wide.
One common solution found on the internet is using automatic resizing with tight_layout(), so I change the last three lines to
plt.tight_layout()
#plt.subplots_adjust(left=0.01, bottom=0.03, right=0.99, top=0.99, wspace = 0.06, hspace=0.06)
plt.savefig('plot_2.png', dpi=96)
This does not rescale the lineplot, but instead makes the wspace and hspace attributes so big I get way too much whitespace between the thumbnails.
I am looking for a solution to either
Set wspace and hspace of only the right subplot, not all of them together, or
resize the lineplot to fit into the designated area, without the labels sticking out
It would seem that this is an easy problem, but despite searching for about 2 hours and digging around in the object properties with iPython I found nothing suitable. All solutions seem to change the size and padding of the subplots, not fitting a plot into the area defined with subplot2grid. The only other solution I can think of is a hack that calculates a modified aspect from the value ranges to make the lineplot always a given percentage thinner.
You can play around with subfigures. For example, if you do:
import numpy as np
from matplotlib import pyplot as plt
### This can change at runtime
n_grid = 4
### Grid of thumbnails
fig = plt.figure(figsize=(20,10.2))
# add 2 subfigures
subfigs = fig.subfigures(1, 2, wspace=0)
# add thumbnail grid into left subfig
gsLeft = subfigs[0].add_gridspec(n_grid, n_grid)
axLeft = []
for i in range(n_grid):
for j in range(n_grid):
axLeft.append(subfigs[0].add_subplot(gsLeft[i, j]))
axLeft[-1].imshow(np.random.random((16,16)))
axLeft[-1].set_axis_off()
### Line plot
gsRight = subfigs[1].add_gridspec(3, 1)
axRight = subfigs[1].add_subplot(gsRight[:2, 0])
axRight.plot(np.cumsum(np.random.random(100)), label='Random Sum')
axRight.set_xlim([0, 100])
axRight.set_ylim(0,50)
axRight.set_xlabel('Number', fontsize=12)
axRight.set_ylabel('Sum', fontsize=12)
# adjust subfigures here (play around with these to get the desired effect)
subfigs[0].subplots_adjust(wspace=0.03, hspace=0.03, bottom=0.05, top=0.95, left=0.05, right=0.95)
subfigs[1].subplots_adjust(left=0.01)
# add title (here I've had to add it to the left figure, so it's not centred,
# in my test adding it to the figure itself meant it was not visible, although
# the example in the Matplotlib docs suggests it should work!)
# fig.suptitle(f'Unique identifier', x=0.5, y=0.025, ha='center', va='baseline')
subfigs[0].suptitle(f'Unique identifier', x=0.5, y=0.025, ha='center', va='baseline')
fig.savefig("plot_1.png", dpi=150)
This gives:
but you can play around with the values to adjust it as you like.
I have a plot in which some data is represented by a scatter plot with error bars and I want to fit a curve to it. However, no matter where in the code I plot the curve, the error bars float on top of it. I want the fitted curves to display in front of the error bars because otherwise I can't see it.
Here is a simple example of the issue:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
x = np.arange(1,10)
r = np.random.random(x.size)
fig1, ax = plt.subplots()
ln1 = ax.plot(2*x,x,'g')
ax3 = ax.twinx()
ln2 = ax3.errorbar(x,r,yerr=x,color='red',fmt='o')
ln2fit = ax3.plot(x,r-0.3,'b')
and the plot it produces:
There are two axes because I'm comparing two datasets.
As you can see, even though I plotted the curve above the error bars, the error bars and points still obscure the curve. What can I do to disable this?
You can specify the zorder:
ln2 = ax3.errorbar(x,r,yerr=x,color='red',fmt='o',zorder=1)
If you also want to have the green line in the foreground you need to move it's axes ax to a higher zorder (default is 0) and hide the axes patch of ax so that the then underlying ax3 stays visible:
ax.set_zorder(1)
ax.patch.set_visible(False)
I have a heatplot in matplotlib for which I want to remove the whitespace to the north and east of the plot, as shown in the image below.
here is the code I'm using to generate the plots:
# plotting
figsize=(50,20)
y,x = 1,2
fig, axarry = plt.subplots(y,x, figsize=figsize)
p = axarry[1].pcolormesh(copy_matrix.values)
# put the major ticks at the middle of each cell
axarry[1].set_xticks(np.arange(copy_matrix.shape[1])+0.5, minor=False)
axarry[1].set_yticks(np.arange(copy_matrix.shape[0])+0.5, minor=False)
axarry[1].set_title(file_name, fontweight='bold')
axarry[1].set_xticklabels(copy_matrix.columns, rotation=90)
axarry[1].set_yticklabels(copy_matrix.index)
fig.colorbar(p, ax=axarry[1])
Phylo.draw(tree, axes=axarry[0])
The easiest way to do this is to use ax.axis('tight').
By default, matplotlib tries to choose "even" numbers for the axes limits. If you want the plot to be scaled to the strict limits of your data, use ax.axis('tight'). ax.axis('image') is similar, but will also make the cells of your "heatmap" square.
For example:
import numpy as np
import matplotlib.pyplot as plt
# Note the non-"even" size... (not a multiple of 2, 5, or 10)
data = np.random.random((73, 78))
fig, axes = plt.subplots(ncols=3)
for ax, title in zip(axes, ['Default', 'axis("tight")', 'axis("image")']):
ax.pcolormesh(data)
ax.set(title=title)
axes[1].axis('tight')
axes[2].axis('image')
plt.show()
Original Post
I need to make several subplots with different sizes.
I have simulation areas of the size (x y) 35x6µm to 39x2µm and I want to plot them in one figure. All subplots have the same x-ticklabels (there is a grid line every 5µm on the x-axis).
When I plot the subplots into one figure, then the graphs with the small x-area are streched, so that the x-figuresize is completely used. Therefore, the x-gridlines do not match together anymore.
How can I achieve that the subplots aren't streched anymore and are aligned on the left?
Edit: Here is some code:
size=array([[3983,229],[3933,350],[3854,454],[3750,533],[3500,600]], dtype=np.float)
resolution=array([[1024,256],[1024,320],[1024,448],[1024,512],[1024,640]], dtype=np.float)
aspect_ratios=(resolution[:,0]/resolution[:,1])*(size[:,1]/size[:,0])
number_of_graphs=len(data)
fig, ax=plt.subplots(nrows=number_of_graphs, sharex=xshare)
fig.set_size_inches(12,figheight)
for i in range(number_of_graphs):
temp=np.rot90(np.loadtxt(path+'/'+data[i]))
img=ax[i].imshow(temp,
interpolation="none",
cmap=mapping,
norm=specific_norm,
aspect=aspect_ratios[i]
)
ax[i].set_adjustable('box-forced')
#Here I have to set some ticks and labels....
ax[i].xaxis.set_ticks(np.arange(0,int(size[i,0]),stepwidth_width)*resolution[i,0]/size[i,0])
ax[i].set_xticklabels((np.arange(0, int(size[i,0]), stepwidth_width)))
ax[i].yaxis.set_ticks(np.arange(0,int(size[i,1]),stepwidth_height)*resolution[i,1]/size[i,1])
ax[i].set_yticklabels((np.arange(0, int(size[i,1]), stepwidth_height)))
ax[i].set_title(str(mag[i]))
grid(True)
savefig(path+'/'+name+'all.pdf', bbox_inches='tight', pad_inches=0.05) #saves graph
Here are some examples:
If I plot different matrices in a for loop, the iPhython generates an output which is pretty much what I want. The y-distande between each subplot is constant, and the size of each figure is correct. You can see, that the x-labels match to each other:
When I plot the matrices in one figure using subplots, then this is not the case: The x-ticks do not fit together, and every subplot has the same size on the canvas (which means, that for thin subplots there is more white space reservated on the canvas...).
I simply want the first result from iPython in one output file using subplots.
Using GridSpec
After the community told me to use GridSpec to determine the size of my subplots directly I wrote a code for automatic plotting:
size=array([[3983,229],[3933,350],[3854,454],[3750,533],[3500,600]], dtype=np.float)
#total size of the figure
total_height=int(sum(size[:,1]))
total_width=int(size.max())
#determines steps of ticks
stepwidth_width=500
stepwidth_height=200
fig, ax=plt.subplots(nrows=len(size))
fig.set_size_inches(size.max()/300., total_height/200)
gs = GridSpec(total_height, total_width)
gs.update(left=0, right=0.91, hspace=0.2)
height=0
for i in range (len(size)):
ax[i] = plt.subplot(gs[int(height):int(height+size[i,1]), 0:int(size[i,0])])
temp=np.rot90(np.loadtxt(path+'/'+FFTs[i]))
img=ax[i].imshow(temp,
interpolation="none",
vmin=-100,
vmax=+100,
aspect=aspect_ratios[i],
)
#Some rescaling
ax[i].xaxis.set_ticks(np.arange(0,int(size[i,0]),stepwidth_width)*resolution[i,0]/size[i,0])
ax[i].set_xticklabels((np.arange(0, int(size[i,0]), stepwidth_width)))
ax[i].yaxis.set_ticks(np.arange(0,int(size[i,1]),stepwidth_height)*resolution[i,1]/size[i,1])
ax[i].set_yticklabels((np.arange(0, int(size[i,1]), stepwidth_height)))
ax[i].axvline(antenna[i]) #at the antenna position a vertical line is plotted
grid(True)
#colorbar
cbaxes = fig.add_axes([0.93, 0.2, 0.01, 0.6]) #[left, bottom, width, height]
cbar = plt.colorbar(img, cax = cbaxes, orientation='vertical')
tick_locator = ticker.MaxNLocator(nbins=3)
cbar.locator = tick_locator
cbar.ax.yaxis.set_major_locator(matplotlib.ticker.AutoLocator())
cbar.set_label('Intensity',
#fontsize=12
)
cbar.update_ticks()
height=height+size[i,1]
plt.show()
And here is the result....
Do you have any ideas?
What about using matplotlib.gridspec.GridSpec? Docs.
You could try something like
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
gs = GridSpec(8, 39)
ax1 = plt.subplot(gs[:6, :35])
ax2 = plt.subplot(gs[6:, :])
data1 = np.random.rand(6, 35)
data2 = np.random.rand(2, 39)
ax1.imshow(data1)
ax2.imshow(data2)
plt.show()
Suppose we have a figure with three plots in it for three different parameters. But for the all three plots We have same temperature T=4K . Then how can I add this information in the figure?
I am not interested to write it in the Caption. I want it on the figure itself.
figtext would work well.
The advantage of figtext over text and annotate is that figtext defaults to using the figure coordinates, whereas the others default to using the coordinates of the axes (and therefore "T=4K" would move around if your axes are different between the different plots).
import matplotlib.pyplot as plt
plt.figure()
plt.xlim(-10, 10)
plt.ylim(0, .01)
plt.figtext(.8, .8, "T = 4K")
plt.show()
Here's a demonstration of using annotate. Check out this example for different styles of annotation.
import matplotlib.pyplot as plt
import numpy as np
plt.ion()
fig, ax = plt.subplots()
x = np.linspace(0,4,100)
plt.plot(x,2*x)
plt.plot(x,x**2)
plt.plot(x,np.sqrt(8*x))
ax.annotate('T = 4K', xy=(2,4), xycoords='data',
xytext=(-100,60), textcoords='offset points',
arrowprops=dict(arrowstyle='fancy',fc='0.6',
connectionstyle="angle3,angleA=0,angleB=-90"))
plt.show()
raw_input()
figtext can make annotations at the bootom of multiple subplots figure like a comment independent of figures so you can make additional comments or remarks all in one picture. I was looking for this too. Thank you guys! :-)
import matplotlib.pyplot as plt
plt.figure(1)
plt.suptitle("SOME TITLE HERE")
#FIRST SUBPLOT
plt.subplot(311)
plt.ylabel(r"$a [m/s^2]$") # YOU CAN USE LaTeX TYPESETTING IN PYPLOT STRINGS!
plt.xlabel("time [s]")
plt.grid(True)
plt.plot(some_data)
# SECOND SUBPLOT
plt.subplot(312)
...
# THIRD SUBPLOT
plt.subplot(313)
...
# BOTTOM LABEL
plt.figtext(0.5, 0, "SOME LABEL BELOW ALL SUBPLOTS", ha="center", fontsize=7, bbox={"facecolor":"orange", "alpha":0.5, "pad":5})
# DRAW THE PLOT
plt.show()
Notre ha=center will center the string if x=0.5. You can also use fontsize and bbox parameters to change appearance of the string and its area.
Well, I'm not sure what you mean, but you can add text to the plot with the text() method.
Plot text in matplotlib pyplot
I suggest a grey horizontal zone around the T=4K zone
If you look at axhspan(ymin, ymax, xmin=0, xmax=1, **kwargs) in the matplotlib documentation for axes, you can make things like that: