Python contour plotting wrong values with plot_surface - python

I want to plot a surface in Matplotlib consisting of zeros everywhere, except for a rectangular region centered in (0, 0), with sides (Dx, Dy), consisting of ones - kind of like a table, if you wil; I can do that using the plot_surface command, no worries there. I also want to plot its projections in the "x" and "y" directions (as in this demo) and that's when the results become weird: Python seems to be interpolating my amplitude values (which, again, should be either zero or one) for the contour plots and showing some lines with values that do not correspond to my data points.
This is what I'm doing:
import numpy
from matplotlib import pylab
from mpl_toolkits.mplot3d import axes3d
Dx = 1. # Define the sides of the rectangle
Dy = 2.
x_2D = numpy.linspace(-Dx, Dx, 100) # Create the mesh points
y_2D = numpy.linspace(-Dy, Dy, 100)
x_mesh, y_mesh = numpy.meshgrid(x_2D, y_2D)
rect_2D = numpy.zeros(x_mesh.shape) # All values of "rect_2D" are zero...
for i in range(x_2D.size):
for j in range(y_2D.size):
if numpy.abs(x_mesh[i, j]) <= Dx/2 and numpy.abs(y_mesh[i, j]) <= Dy/2:
rect_2D[i, j] = 1. # ... except these ones
fig = pylab.figure(figsize=(9, 7))
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x_mesh, y_mesh, rect_2D, alpha=0.3)
ax.contour(x_mesh, y_mesh, rect_2D, zdir='x', offset=-1.5, cmap=pylab.cm.brg)
ax.contour(x_mesh, y_mesh, rect_2D, zdir='y', offset=3, cmap=pylab.cm.brg)
ax.set_xlim(-1.5, 1.5)
ax.set_ylim(-3, 3)
ax.set_zlim(0., 1.5)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
The resulting figure has a dark green line with amplitude a bit below 0.8 on both the "x" and "y" projections, which does not exist in my "rect_2D" variable. Does anyone knows if this is a bug or if there is a mistake in my code? Any suggestions on how to get rid of it? Thanks in advance!

Add a levels = [0], kwarg to your ax.contour call. This specifies where along the zdir axis your contours are computed. See mpl.axes.Axes.contour docstring for more info.
The problem is that without specifying levels, contour automatically computes the locations to plot contours and one of these contours is selected just past the 'edge of your table', but before your Zdata is 0. At these points contour interpolates between 0 and 1.

Related

Data points falling outside the meshgrid being interpolated over, while the meshgrid certainly covers those points

I am trying to interpolate sparse data over a meshgrid, but am observing some rather odd behavior. The white dots are precisely where I have values, and I am relying on the linear interpolation algorithm to fill in the other grids where possible. I recognize that this type of interpolation is not perfect due to the obvious lack of data, but how come some of the points where I have data fall outside the meshgrid that I am interpolating over? Is this a common phenomenon? This doesn't change even if I make the grid coarser.
I would appreciate some insight into why this happens, (perhaps how the linear interpolation works), or if there are any ways to fix this. See the red circles in the picture below for example:
Data points provided for interpolation falling outside the meshgrid that is interpolated over
The following is some code on the interpolation that generated the gridded data.
#mesh grid
xg = np.linspace(-130, -60, num=70)
yg = np.linspace(20,50,num=30)
Xg,Yg = np.meshgrid(xg,yg)
zg1 = griddata(points1, df2['tempratio'], (Xg, Yg), method = 'linear')
from mpl_toolkits.basemap import Basemap
lon_0 = xg.mean()
lat_0 = yg.mean()
m = Basemap(width=5000000, height=3500000,
resolution='l', projection='stere',\
lat_ts=40, lat_0=lat_0, lon_0=lon_0)
xm, ym = m(Xg, Yg)
cs = m.pcolormesh(xm,ym,zg1,shading='flat',cmap=plt.cm.Reds)
griddata assigns values to the vertices of a grid, so 70x30 points. pcolormesh doesn't color vertices, but the rectangles in-between. There are only 69x29 rectangles formed by the given vertices. So, one row and one column of zg1 will be dropped. To counter that, an extra row and extra column can be added to the coordinates and shifting everything half a rectangle in each direction.
It still doesn't force griddata to include all given points, but goes a step towards the desired outcome. A denser grid can also help. (Choosing 'nearest' instead of 'linear' interpolation would fill the complete grid.)
Here is some code to illustrate what's happening:
import numpy as np
from scipy.interpolate import griddata
from matplotlib import pyplot as plt
def extend_range(x):
dx = (x[1] - x[0]) / 2
return np.append( x - dx, x[-1] + dx)
N = 10
points1 = np.vstack([np.random.randint(-130, -60, N), np.random.randint(20, 50, N)]).T
tempratio = np.random.randint(0, 20, N)
xg = np.linspace(-130, -60, num=15)
yg = np.linspace(20, 50, num=10)
Xg, Yg = np.meshgrid(xg, yg)
zg1 = griddata(points1, tempratio, (Xg, Yg), method='linear')
fig, axs = plt.subplots(ncols=2, figsize=(12, 4))
for ax in axs:
ax.scatter(Xg, Yg, c=zg1, cmap='coolwarm', ec='g', s=80, zorder=2, label='griddata')
ax.scatter(points1[:,0], points1[:,1], c=tempratio, cmap='coolwarm', ec='black', s=150, zorder=3, label='given data')
if ax == axs[0]:
ax.pcolormesh(xg, yg, zg1, shading='flat', cmap='coolwarm')
ax.set_title('given x and y ranges')
else:
#todo: convert xg and yg to map coordinates
ax.pcolormesh(extend_range(xg), extend_range(yg), zg1, shading='flat', cmap='coolwarm')
ax.set_title('extended x and y ranges')
ax.legend()
plt.show()

Matplotlib: Color bar on contour without striping

In matplotlib, I'm looking to create an inset color bar to show the scale of my contour plot, but when I create the contour using contour, the color bar has white stripes running through it, whereas when I use contourf, the colorbar has the proper "smooth" appearance:
How can I get that nice smooth colorbar from the filled contour on my normal contour plot? I'd also be OK with a filled contour where the zero-level can be set to white, I imagine.
Here is code to generate this example:
from numpy import linspace, outer, exp
from matplotlib.pyplot import figure, gca, clf, subplots_adjust, subplot
from matplotlib.pyplot import contour, contourf, colorbar, xlim, ylim, title
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
# Make some data to plot - 2D gaussians
x = linspace(0, 5, 100)
y = linspace(0, 5, 100)
g1 = exp(-((x-0.75)/0.2)**2)
g2 = exp(-((y-4.25)/0.1)**2)
g3 = exp(-((x-3.5)/0.15)**2)
g4 = exp(-((y-1.75)/0.05)**2)
z = outer(g1, g2) + outer(g3, g4)
figure(1, figsize=(13,6.5))
clf()
# Create a contour and a contourf
for ii in range(0, 2):
subplot(1, 2, ii+1)
if ii == 0:
ca = contour(x, y, z, 125)
title('Contour')
else:
ca = contourf(x, y, z, 125)
title('Filled Contour')
xlim(0, 5)
ylim(0, 5)
# Make the axis labels
yt = text(-0.35, 2.55, 'y (units)', rotation='vertical', size=14);
xt = text(2.45, -0.4, 'x (units)', rotation='horizontal', size=14)
# Add color bar
ains = inset_axes(gca(), width='5%', height='60%', loc=2)
colorbar(ca, cax=ains, orientation='vertical', ticks=[round(xx*10.0)/10.0 for xx in linspace(0, 1)])
if ii ==1:
ains.tick_params(axis='y', colors='#CCCCCC')
subplots_adjust(left=0.05, bottom=0.09, right=0.98, top=0.94, wspace=0.12, hspace=0.2)
show()
Edit: I realize now that at the lower resolution, the white striping behavior is hard to distinguish from some light transparency. Here's an example with only 30 contour lines which makes the problem more obvious:
Edit 2: While I am still interested in figuring out how to do this in the general general case (like if there's negative values), in my specific case, I have determined that I can effectively create something that looks like what I want by simply setting the levels of a filled contour to start above the zero-level:
ca = contourf(x, y, z, levels=linspace(0.05, 1, 125))
Which basically looks like what I want:
A simple hack is to set the thickness of the lines in the colorbar to some higher value.
E.g. storing the colorbar object as cb and adding the following lines to your example
for line in cb.lines:
line.set_linewidth(3)
gives

Set a colormap under a graph

I know this is well documented, but I'm struggling to implement this in my code.
I would like to shade the area under my graph with a colormap. Is it possible to have a colour, i.e. red from any points over 30, and a gradient up until that point?
I am using the method fill_between, but I'm happy to change this if there is a better way to do it.
def plot(sd_values):
plt.figure()
sd_values=np.array(sd_values)
x=np.arange(len(sd_values))
plt.plot(x,sd_values, linewidth=1)
plt.fill_between(x,sd_values, cmap=plt.cm.jet)
plt.show()
This is the result at the moment. I have tried axvspan, but this doesnt have cmap as an option. Why does the below graph not show a colormap?
I'm not sure if the cmap argument should be part of the fill_between plotting command. In your case probably want to use the fill() command btw.
These fill commands create polygons or polygon collections. A polygon collection can take a cmap but with fill there is no way of providing the data on which it should be colored.
What's (for as far as i know) certainly not possible is to fill a single polygon with a gradient as you wish.
The next best thing is to fake it. You can plot a shaded image and clip it based on the created polygon.
# create some sample data
x = np.linspace(0, 1)
y = np.sin(4 * np.pi * x) * np.exp(-5 * x) * 120
fig, ax = plt.subplots()
# plot only the outline of the polygon, and capture the result
poly, = ax.fill(x, y, facecolor='none')
# get the extent of the axes
xmin, xmax = ax.get_xlim()
ymin, ymax = ax.get_ylim()
# create a dummy image
img_data = np.arange(ymin,ymax,(ymax-ymin)/100.)
img_data = img_data.reshape(img_data.size,1)
# plot and clip the image
im = ax.imshow(img_data, aspect='auto', origin='lower', cmap=plt.cm.Reds_r, extent=[xmin,xmax,ymin,ymax], vmin=y.min(), vmax=30.)
im.set_clip_path(poly)
The image is given an extent which basically stretches it over the entire axes. Then the clip_path makes it only showup where the fill polygon is drawn.
I think all you need is to do the plot of the data one at a time, like:
import numpy
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import matplotlib.colors as colors
# Create fake data
x = numpy.linspace(0,4)
y = numpy.exp(x)
# Now plot one by one
bar_width = x[1] - x[0] # assuming x is linealy spaced
for pointx, pointy in zip(x,y):
current_color = cm.jet( min(pointy/30, 30)) # maximum of 30
plt.bar(pointx, pointy, bar_width, color = current_color)
plt.show()
Resulting in:

Grid Lines below the contour in Matplotlib

I have one question about the grid lines matplotlib.
I am not sure if this is possible to do or not.
I am plotting the following graph as shown in the image.
I won't give the entire code, since it is involving reading of files.
However the important part of code is here -
X, Y = np.meshgrid(smallX, smallY)
Z = np.zeros((len(X),len(X[0])))
plt.contourf(X, Y, Z, levels, cmap=cm.gray_r, zorder = 1)
plt.colorbar()
...
# Set Border width zero
[i.set_linewidth(0) for i in ax.spines.itervalues()]
gridLineWidth=0.1
ax.set_axisbelow(False)
gridlines = ax.get_xgridlines()+ax.get_ygridlines()
#ax.set_axisbelow(True)
plt.setp(gridlines, 'zorder', 5)
ax.yaxis.grid(True, linewidth=gridLineWidth, linestyle='-', color='0.6')
ax.xaxis.grid(False)
ax.xaxis.set_ticks_position('none')
ax.yaxis.set_ticks_position('none')
Now, my questions is like this -
If I put the grid lines below the contour, they disappear since they are below it.
If I put the grid line above the contour, they looks like what they are looking now.
However, what I would like to have is the grid lines should be visible, but should be below the black portion of the contour. I am not sure if that is possible.
Thank You !
In addition to specifying the z-order of the contours and the gridlines, you could also try masking the zero values of your contoured data.
Here's a small example:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-2*np.pi, 2*np.pi, 0.1)
y = np.arange(-2*np.pi, 2*np.pi, 0.1)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) - np.cos(Y)
Z = np.ma.masked_less(Z, 0) # you use mask_equal(yourData, yourMagicValue)
fig, ax = plt.subplots()
ax.contourf(Z, zorder=5, cmap=plt.cm.coolwarm)
ax.xaxis.grid(True, zorder=0)
ax.yaxis.grid(True, zorder=0)
And the output:

matplotlib: how can I convert a XYZ scatter to a pixel image?

I'm looking for some way in to convert a scatter plot (X vs Y, color normalized by Z) into a 2D "pixel" image. I.e. how can I plot a pixelized image where the pixels are colored according to a third variable?
In my case, I have a list of galaxies, each a with sky coordinate (X,Y) and a distance (Z). I want to make a pixelized image of X vs Y, with the pixels color normalized according to Z (e.g. the median Z value for the galaxies in that pixel).
I know I could do something like this with hexbin, but I would like to have square pixels, not hexagons. (Something more like what imshow produces).
I'm still learning python, so if there is a simple/quick way to do this (or clear instructions on how to do it the complicated way!) that'd be great.
Any help would be much appreciated!
Okay - there are two ways that you can do this. One would be for you to have a discreet number of bins for the distances (like d < 10pc, 10pc < d < 20pc, d> 20pc). This is relatively easy, all you need to do are a few loops - here is an example with 3:
raclose = []
ramid = []
rafar = []
decdlose = []
decmid = []
decfar = []
for ii in range(len(dist)):
if dist[ii] < 10.:
raclose.append(ra[ii])
decclose.append(dec[ii])
elif dist[ii] > 20.:
rafar.append(ra[ii])
decfar.append(dec[ii])
else:
ramid.append(ra[ii])
decmid.append(dec[ii])
plt.clf
ax1 = scatter(raclose, decclose, marker='o', s=20, color="darkgreen", alpha=0.6)
ax2 = scatter(ramid, decmid, marker='o', s=20, color="goldenrod", alpha=0.6)
ax3 = scatter(rafar, decfar, marker='o', s=20, color="firebrick", alpha=0.6)
line1 = Line2D(range(10), range(10), marker='o', color="darkgreen")
line2 = Line2D(range(10), range(10), marker='o',color="goldenrod")
line3 = Line2D(range(10), range(10), marker='o',color="firebrick")
plt.legend((line1,line2,line3),('d < 10pc','20pc > d > 10pc', 'd > 20pc'),numpoints=1, loc=3)
show()
Or you can do a contour plot, such that you stipulate RA on the x-axis and Dec on the y-axis and fill in the plot with the distances. Both RA and Dec are 1D arrays with the respective coordinates. Then you make a 2D array with the distance. Determine what the median/mean value of the distances are and then divide the 2D array by that value to normalize it. Finally, plot using a contour plot (using contourf or imshow), like:
import matplotlib.pyplot as plt
from matplotlib import cm
ax = pylab.contourf(RA,Dec,dists, levels=[1, 5, 10, 15], cmap=plt.cm.spectral)
cbar=pylab.colorbar()

Categories

Resources