I have a matplotlib code for creating a set of three subplots which share the same axes. My problem is that I am trying to plot an ellipse on each of these subplots, but the ellipse only shows up on one of the subplots. Can anyone tell me what I'm doing wrong?
Sorry if this kind of thing has already been answered elsewhere, I've been looking for a while but can't find an answer!
from pylab import *
import numpy as np
import sys
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import scipy.interpolate
import matplotlib
from matplotlib.patches import Ellipse
from numpy import linspace
from scipy import pi,sin,cos
data = np.genfromtxt(sys.argv[1]);
name1 = (sys.argv[1]);
name = ( sys.argv[2] );
print(sys.argv)
font = {'family' : 'normal',
'size' : 16}
matplotlib.rc('font', **font)
def ellipse(ra,rb,ang,x0,y0,Nb=50):
xpos,ypos=x0,y0
radm,radn=ra,rb
an=ang
co,si=cos(an),sin(an)
the=linspace(0,2*pi,Nb)
X=radm*cos(the)*co-si*radn*sin(the)+xpos
Y=radm*cos(the)*si+co*radn*sin(the)+ypos
return X,Y
def plot(x, y, z, name1, name):
# I2 = scipy.interpolate.NearestNDInterpolator((x, y), z)
I2 = scipy.interpolate.Rbf(x, y, z, function='linear')
xi, yi = np.linspace(x.min(), x.max(), 100), np.linspace(y.min(), y.max(), 100)
xig, yig = np.meshgrid(xi, yi)
zi = I2(xig, yig)
plt.clf()
#creating plot
f, axarr = plt.subplots(1,3,sharey=True)
plt.setp(axarr.flat, aspect=1.0, adjustable='box-forced')
#first plot
im1=axarr[2].imshow(zi, vmin=0, vmax=1,cmap='gist_heat_r', origin='lower', extent=[x.min(), x.max(), y.min(), y.max()])
# axarr[2].set_title('Sharing both axes')
X,Y=ellipse(7.36,2.29,0,0,0,Nb=70)
plt.plot(X,Y,"g.-",ms=1) # green ellipse
#second plot
im2=axarr[1].imshow(zi, vmin=0, vmax=1,cmap='gist_heat_r', origin='lower', extent=[x.min(), x.max(), y.min(), y.max()])
X,Y=ellipse(7.36,2.29,0,0,0,Nb=70)
plt.plot(X,Y,"g.-",ms=1) # green ellipse
#third plot
im3=axarr[0].imshow(zi, vmin=0, vmax=1,cmap='gist_heat_r', origin='lower', extent=[x.min(), x.max(), y.min(), y.max()])
# axis labels
plt.xlabel('X AXIS (kpc)')
plt.ylabel('Y AXIS (kpc)')
f.subplots_adjust(hspace=0);
f.subplots_adjust(wspace=0);
X,Y=ellipse(7.36,2.29,0,0,0,Nb=70)
plt.plot(X,Y,"g.-",ms=1) # green ellipse
# Colorbar
from mpl_toolkits.axes_grid1 import make_axes_locatable
divider = make_axes_locatable(plt.gca())
cax = divider.append_axes("right", "5%", pad="3%")
plt.colorbar(im1,cax=cax)
# Save figure
plt.savefig(name1 + "_norm.eps", bbox_inches='tight');
plot(data[:,0], data[:,1], data[:,2], name1, name);
When you call plt.plot() on what axes do you expect it to be plotted on? It will plot on the current active axes, which in your case is probably the first.
You need to either change the current active axes with plt.sca(axarr[n]) before calling plt.plot(), or even better, stop mixing both the OO- and state machine interface and use axarr[n].plot().
You are using the OO-interface for .imshow(), so why not for .plot() as well?
Related
Is it possible to put the color diagram (which is now on the right side of the original figure) on the top of the figure?
My code:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D, get_test_data
from matplotlib import cm
import numpy as np
# set up a figure twice as wide as it is tall
fig = plt.figure(figsize=plt.figaspect(0.5))
#===============
# First subplot
#===============
# set up the axes for the first plot
ax = fig.add_subplot(1, 2, 1, projection='3d')
# plot a 3D surface like in the example mplot3d/surface3d_demo
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
ax.set_zlim(-1.01, 1.01)
fig.colorbar(surf, shrink=0.5, aspect=10)
fig.savefig('64bit.png')
You have to add additional axes (add_axes) to put your colorbar at the desired position:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D, get_test_data
from matplotlib import cm
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np
# set up a figure twice as wide as it is tall
fig = plt.figure(figsize=plt.figaspect(0.5))
#===============
# First subplot
#===============
# set up the axes for the first plot
ax = fig.add_subplot(1, 2, 1, projection='3d')
# plot a 3D surface like in the example mplot3d/surface3d_demo
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
ax.set_zlim(-1.01, 1.01)
# position of colorbar
# where arg is [left, bottom, width, height]
cax = fig.add_axes([0.15, .87, 0.35, 0.03])
fig.colorbar(surf, orientation='horizontal', cax=cax)
plt.show()
Yes it is, there are multiple answers here in the site showing you how to move the colorbar around like this one: positioning the colorbar
In your case, you want to combine that with the orientation argument. As far as I know, there is no easy way of just placing the colorbar to the top of your figure automatically, you will have to place it manually. Here is my code that replaces your fig.colorbar(surf, shrink=0.5, aspect=10):
cbax = fig.add_axes([0.1, 0.89, 0.5, 0.05])
fig.colorbar(surf, orientation="horizontal", cax=cbax)
The numbers in the list describe some characteristics of the colorbar which are [left, bottom, width, height] as mentioned in the other answer that I have attached.
These numbers came out nicely for your plot, feel free to change them to your liking.
In order to get the colorbar on top of the plot you need to create some axes, designated to host the colorbar.
This can either be done manually by placing a new axes at some given position in figure coordinates,
cax = fig.add_axes([0.2,0.8,0.3,.05])
fig.colorbar(surf, cax=cax, orientation="horizontal")
or, by using a subplot grid (gridspec), which is shown in the following:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
import matplotlib.gridspec as gridspec
import numpy as np
x = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(x,x)
Z = np.sin(np.sqrt(X**2 + Y**2))
gs = gridspec.GridSpec(2, 2, height_ratios=[0.05,1])
fig = plt.figure()
ax = fig.add_subplot(gs[1,0], projection='3d')
cax = fig.add_subplot(gs[0,0])
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap="coolwarm",
linewidth=0, antialiased=False, vmin=-1, vmax=1)
fig.colorbar(surf, cax=cax, orientation="horizontal", ticks=[-1,0,1])
plt.show()
For a method which avoids having to manually create new axes and instead allows us to keep the colorbar linked to an existing plot axis, we can use the location keyword (method adapted initially from here).
The location argument is meant to be used on colorbars which reference multiple axes in a list (and will throw an error if colorbar is given only one axis), but if you simply put your one axis in a list, it will allow you to use the argument. You can use the following code as an example:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
axp = ax.imshow(np.random.randint(0, 100, (100, 100)))
cb = plt.colorbar(axp,ax=[ax],location='top')
plt.show()
which yields this plot. From here, you can edit the colorbar using the typical methods (pad, shrink, etc.) to further tune the appearance of your plot.
Fair warning, I haven't seen this method used many other places and it could be less robust than going through the extra steps of creating a new axis for your colorbar.
I am trying to create a density plot with a given data and using log scales in the two axes x,y, using the version of Matplotlib 2.0.0. I have made the following code, the problem is that for the log plot case don't give the correct functional behaviour.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
init = 0.0
points = 500
final_value = 100
steep = (final_value-init)/points
list_values_x = np.arange(init,final_value,steep)
list_values_y = np.arange(init,final_value,steep)
#WE CREATE OUT DATA FILE
f1 = open("data.txt", "w")
for i in list_values_x:
for j in list_values_y:
f1.write( str(i) +" "+str(j)+" "+str(0.0001*(i**2+j**2)) +"\n")
f1.close()
#NOW WE OPEN THE FILE WITH THE DATA AND MAKE THE PLOT
x,y,temp = np.loadtxt('data.txt').T #Transposed for easier unpacking
nrows, ncols = points, points
grid = temp.reshape((nrows, ncols))
# LINEAR PLOT
fig1 = plt.imshow(grid, extent=(x.min(), x.max(), y.max(), y.min()),
interpolation='nearest', cmap=cm.gist_rainbow)
plt.axis([x.min(), x.max(),y.min(), y.max()])
plt.colorbar()
plt.suptitle('Example', fontsize=15)
plt.xlabel('x', fontsize=16)
plt.ylabel('y', fontsize=16)
plt.show()
# LOG-LOG PLOT
fig, (ax1) = plt.subplots(ncols=1, figsize=(8, 4))
ax1.imshow(grid, aspect="auto", extent=(1, 1e2, 1, 1e2), interpolation='nearest')
ax1.set_yscale('log')
ax1.set_xscale('log')
ax1.set_title('Example with log scale')
plt.show()
The data that I am using in order to make the plot is irrelevant, it's just an example. So that, the first plot is given with a linear scale. The second plot is given with a log-log scale, but is clear that it's incorrect, the behaviour beetwen the two plots is absolutely different and I am using the same data. Moreover, I don't know how put a colorbar in the log-log plot
Any idea why this happens? Thanks for your attention.
PD: In order to build the log-log plot, I have used part of the code that apears in "Non-linear scales on image plots" given in (http://matplotlib.org/devdocs/users/whats_new.html#non-linear-scales-on-image-plots)
Using the extent keyword and it with extent=(xmin, xmax, ymin, ymax) makes more sense when additionally using origin="lower" in imshow. You might also want to set the limits for the axes, since the automatic feature does not work too well for log scales.
Here is the complete example:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from mpl_toolkits.axes_grid1 import make_axes_locatable
points = 500
init = 0.0
final_value = 100
steep = (final_value-init)/points
x = np.arange(init,final_value,steep)
y = np.arange(init,final_value,steep)
X,Y = np.meshgrid(x,y)
Z = 0.0001*(X**2+Y**2)
fig, (ax, ax1) = plt.subplots(ncols=2, figsize=(8, 4))
# LINEAR PLOT
im = ax.imshow(Z, extent=(x.min(), x.max(), y.min(), y.max() ),
interpolation='nearest', cmap=cm.gist_rainbow, origin="lower")
ax.set_title('lin scale')
#make colorbar
divider = make_axes_locatable(ax)
ax_cb = divider.new_horizontal(size="5%", pad=0.05)
fig.add_axes(ax_cb)
fig.colorbar(im, cax = ax_cb, ax=ax)
# LOG-LOG PLOT
im1 = ax1.imshow(Z, extent=(1, 1e2, 1, 1e2),
interpolation='nearest',cmap=cm.gist_rainbow, origin="lower")
ax1.set_yscale('log')
ax1.set_xscale('log')
ax1.set_xlim([1, x.max()])
ax1.set_ylim([1, y.max()])
ax1.set_title('log scale')
#make colorbar
divider1 = make_axes_locatable(ax1)
ax_cb1 = divider1.new_horizontal(size="5%", pad=0.05)
fig.add_axes(ax_cb1)
fig.colorbar(im1, cax = ax_cb1, ax=ax1)
plt.tight_layout()
plt.show()
I have some code to plot 3D surfaces in Python using matplotlib:
import math
import numpy as np
import matplotlib.pyplot as plt
from pylab import meshgrid,cm,imshow,contour,clabel,colorbar,axis
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import seaborn as sns
sns.set(style="white")
def surface_map(func, xmin=0, xmax=1, ymin=0, ymax=1, step_size=0.05, maxz=25000):
X, Y = meshgrid(
np.arange(xmin, xmax, step_size),
np.arange(ymin, ymax, step_size))
Z = np.zeros(X.shape)
for i in range(X.shape[0]):
for j in range(X.shape[1]):
Z[i, j] = min(func(X[i, j], Y[i, j]), maxz)
return X, Y, Z
def plot_surface(X, Y, Z, xlabel, ylabel, zlabel, title, point=None, size=25):
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, Z,
rstride=1, cstride=1, vmin=0, vmax=20*1000,
cmap=cm.RdBu, linewidth=0, antialiased=True)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
ax.set_xlabel(xlabel)
ax.set_ylabel(ylabel)
ax.set_zlabel(zlabel)
ax.set_title(title)
fig.colorbar(surf, shrink=0.5, aspect=5)
if point:
ax.hold(True)
func, fpr, recall = point
ax.scatter([fpr], [recall], [
func(fpr, recall)
], s=size, c='b', marker='.', zorder=10)
plt.show()
And then I call it like so:
# create mesh
R, FPR, FuncValue = surface_map(my_function, xmin=0, xmax=1, ymin=0, ymax=1, step_size=0.05, maxz=20*1000)
# plot it
plot_surface(R, FPR, FuncValue,
xlabel="Recall",
ylabel="FPR",
zlabel="Function Value",
title="Recall Settings Payout Function",
point=(my_function, 0.5, 0.5))
I'm setting ax.scatter to use large marker sizes and a high zorder, but no point gets drawn on the surface when the plot gets rendered.
What am I missing?
The point you are looking for is there, but hidden "inside" the surface. This is a common problem in matplotlib.
I see two options here:
Make the surface plot semitransparent, i.e. use alpha=.8 or similar.
Use plot instead of scatter.
I am trying to make a three joint plot. The frame of one of the plots is rotated by 90 degrees with respect to the other and perpendicular to the axis of the other. So I can make a histogram plot in this frame but when I use kde and generate data and use fill to overlay to the hist it won't rotate.
import pylab as plt
import seaborn as sns
from scipy.stats import gaussian_kde
import numpy as np
from astroML.plotting import hist
from mpl_toolkits.axes_grid1 import make_axes_locatable
sns.set_style("ticks")
axScatter = plt.subplot(111)
xmin, xmax = x.min(), x.max()
ymin, ymax = y.min(), y.max()
# Peform the kernel density estimate
xx, yy = np.mgrid[xmin:xmax:100j, ymin:ymax:100j]
positions = np.vstack([xx.ravel(), yy.ravel()])
values = np.vstack([x, y])
kernel = gaussian_kde(values)
f = np.reshape(kernel(positions).T, xx.shape)
axScatter.set_xlim(xmin, xmax)
axScatter.set_ylim(ymin, ymax)
# Contourf plot
cfset = axScatter.contourf(xx, yy, f, cmap='Blues')
## Or kernel density estimate plot instead of the contourf plot
#ax.imshow(np.rot90(f), cmap='Blues', extent=[xmin, xmax, ymin, ymax])
# Contour plot
cset = axScatter.contour(xx, yy, f, colors='k')
# Label plot
axScatter.scatter(x, y, marker='o', s=1, alpha=0.2, color='k')
axScatter.set_aspect('auto')
axScatter.set_xlabel(r'$X$')
axScatter.set_ylabel(r'$Y$')
# create new axes on the right and on the top of the current axes.
divider = make_axes_locatable(axScatter)
axHistx = divider.append_axes("top", size=1.2, pad=0.1, sharex=axScatter)
axHisty = divider.append_axes("right", size=1.2, pad=0.1, sharey=axScatter)
# the scatter plot:
# histograms
kde = gaussian_kde(x)
X_plot = np.linspace(xmin, xmax, 1000)
X_dens = kde.evaluate(X_plot)
axHistx.fill(X_plot, X_dens, fc='#AAAAFF',alpha=0.2)
hist(x, bins='knuth', ax=axHistx, color='black', histtype='step', normed=True)
kde = gaussian_kde(y)
Y_plot = np.linspace(ymin,ymax, 1000)
Y_dens = kde.evaluate(Y_plot)
axHisty.fill(Y_plot, Y_dens, fc='#AAAAFF' ,alpha=0.2)
hist(y, bins='knuth', ax=axHisty, color='black', histtype='step', normed=True, orientation='horizontal')
How can I rotate the fill function in right panel?
You can use the fill_betweenx function of the axHisty axes to do this:
axHisty.fill_betweenx(Y_plot, Y_dens, color='#AAAAFF' ,alpha=0.2)
Note the fill_betweenx doesn't take fc as a kwarg, but does take color.
I modified the scatter_hist.py example from the matplotlib gallery to have histograms and fills in the same style as your plot, and used the fill_betweenx line above, to create this plot:
I'm new to python and after installing it I've accomplished to plot my 3d data using matplotlib. Sadly the only thing I don't know how to get done is the color part. My image just shows the surface but doesn't use the color bar at all. Here is my code.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
from matplotlib.mlab import griddata
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
data = np.genfromtxt('Uizq.txt')
x = data[:,0]
y = data[:,1]
z = data[:,2]
xi = np.linspace(min(x), max(x))
yi = np.linspace(min(y), max(y))
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('U')
X, Y = np.meshgrid(xi, yi)
Z = griddata(x, y, z, xi, yi)
ax.set_zlim3d(np.min(Z), np.max(Z))
surf = ax.plot_surface(X, Y, Z, rstride=2, cstride=2, cmap=cm.jet,
linewidth=0.5, antialiased=False)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
you can obviously see that it is all blue, and I want to relate the color with "U" using the full cm.jet spectrum. This might be a very noob question, so sorry if you rolled your eyes.
Add the line
surf.set_clim([np.min(Z),np.max(Z)])
before you add the color bar.
It seems that the 3D plotting does not take into account the masking, so you are including NaN in the data, which confuses the automatic color limits.