Tricontourf plot with a hole in the middle. - python

I have some data defined on a regular Cartesian grids. I'd like to show only some of them with a condition based on the radius from the center. This will effectively create a ring-like structure with a hole in the center. As a result, I cannot use imshow. tricontourf or tripcolor are what I found to deal with it. My code looks something like this:
R = np.sqrt(x**2+y**2)
flag = (R<150)*(R>10)
plt.tricontourf(x[flag], y[flag], data[flag], 100)
where x and y are mesh grids where data defines. The problem here is that both tricontourf and tripcolor try to fill the middle of the ring, where I hope can be left blank.
To be more specific, the one in the left is similar to what I want but I can only get the one in the right with this piece of code shown above.

The following shows how to mask some parts of the plot based on a condition. Using imshow is perfectly possible, and that's what the script below is doing.
The idea is to set all the unwanted parts of the plots to nan. To make the nan values disappear, we can set their alpha to 0, basically making the plot transparent at those points.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-150, 150, 300)
y = np.linspace(-150, 150, 300)
X,Y = np.meshgrid(x,y)
data = np.exp(-(X/80.)**2-(Y/80.)**2)
R = np.sqrt(X**2+Y**2)
flag =np.logical_not( (R<110) * (R>10) )
data[flag] = np.nan
palette = plt.cm.jet
palette.set_bad(alpha = 0.0)
im = plt.imshow(data)
plt.colorbar(im)
plt.savefig(__file__+".png")
plt.show()
Just to add that also tricontourf can do what you're asking about. This example from the matplotlib gallery shows exactly what you're looking for, while this question on SO deals with a similar issue in a more comprehensive way.

Try creating fake data points in the inner hole and set them to np.nan or np.inf. Alternately, you could set them to a high value (in your case, say simply 1) and then pass limits to the colour scale so that these high regions are not plotted.

Related

twinx non-linear mapping between shared y axes on a plot in matplotlib

I have some 3d data that I am plotting with pcolormesh.
On the x-axis is time, on the y-axis is height.
The height has a potential (E) associated with it, but the mapping from height (y) to potential (E) is non-linear.
I want to add an axis on the right hand side of my figure showing the potential that is correct based on the values on the left hand side. I do not particularly care about the left and right ticks lining up (as is the case in this solution). If anything 'nice number' ticks on the right axis would be useful.
I have tried setting the ylim of the top and bottom points as per the celsius-farenheit example in the matplotlib docs, but this assumes a linear scale between the start and end point which is not the case here.
Finally I tried using a funcformatter, but the scaling for the potential requires two external constants to be given, and I can't find a way that constants can be passed to a funcformatter.
So far my code looks like:
import numpy as np
import matplotlib.pyplot as plt
time = np.arange(0.0, 11.0, 1.0)
y = np.arange(0.0, 11.0, 1.0)
data = np.random.randint(1,100, size=(11,11))
fig,ax=plt.subplots(1,1)
im=ax.pcolormesh(time,y,data,shading='nearest')
ax.set_xlabel('time')
ax.set_ylabel('height')
ax.set_ylim(y.min(),y.max())
ax_E = ax.twinx()
ax_E.set_ylabel('Potential E')
plt.savefig('test.png')
Currently the right hand y axis has a linear scale from 0 to 1.0.
I would like this replacing with a scale showing the potential correct according to the values of y on the left hand y-axis.
The function I want to use for the potential is something like:
def get_E(mu, ymax, y):
p2 = 2.0*mu/ymax**3
Jmin = 2.0*np.sqrt(p2)*ymax
pmin = Jmin/(2.0*y)
E = np.sqrt(mu**2*pmin**2 + mu**2) - mu
return E
i.e. highly nonlinear, with 2 constants (mu and ymax) passed to it.
Any help you can give would be greatly appreciated.
I have done my best to search for a solution to this specific problem already, but my apologies if I have missed anything.
Please do ask any questions to clarify.

pcolormesh ticks center for each data point/tile

I have some z=f(x,y) data that I would like to display in a heat map. So I am using np.meshgrid to create a (x,y)-grid and then call pcolormesh. However the ticks are not centered for each "tile" that correspond to a data point -- in the docs, I did not find any instructions on how to center the ticks for each tile so that I can immediately read off the corresponding value. Any ideas?
In the image attached for instance, it is not clear to which x-value the tick corresponds.
In a pcolormesh the grid is defined by the edge values. In the following example the value of 6 in the lower left corner is the value between 0 and 1 in each dimension. I think this is perfectly understandable to everyone.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)
x = np.arange(5)
y = np.arange(3)
X,Y = np.meshgrid(x,y)
Z = np.random.randint(1,9, size=(2,4))
plt.pcolormesh(X,Y,Z)
plt.show()
Now if you want to (only) have the ticks in the middle of the cell, you can set them as follows
plt.xticks(x[:-1]+0.5)
plt.yticks(y[:-1]+0.5)
If the lower left pixel actually does not correspond to the data between 0 and 1, but to the data at 0, the grid is 'wrong'; a solution would be to fix it by translating it by half the pixel width.
plt.pcolormesh(X-0.5,Y-0.5,Z)
As above, the ticks could be adapted to show only certain numbers, using plt.xticks.

imagesc like feature with non-rectangular grids [MATLAB]

If i want to color a square grid with different color in each grid cells, then it is possible in MATLAB with a simple call to imagesc command like here.
What if i want to color different cells in a grid like this:
Is this functionality available by default in either python or Matlab? I tried discretizing this grid with very small square cells. And then color each cell. That works. But it seems ordinary. Is there a smarter way to get his done?
In python, there is the builtin polar projection for the axes. This projection allows you to automatically use almost every plotting method in polar coordinates. In particular, you need to you pcolor or pcolormesh as follows
import numpy as np
from matplotlib import pyplot as plt
r = np.linspace(0,4,5)
theta = np.linspace(0,2*np.pi,10)
theta,r = np.meshgrid(theta,r)
values = np.random.rand(*(theta.shape))
ax = plt.subplot(111,polar=True)
ax.pcolor(theta,r,values)
plt.show()
Note that this will produce a plot like this
which is almost what you want. The obvious problem is that the patch vertices are joined by straight lines and not lines that follow the circle arc. You can solve this by making the angles array denser. Here is a posible way to do it.
import numpy as np
from matplotlib import pyplot as plt
r = np.linspace(0,4,5)
theta = np.linspace(0,2*np.pi,10)
values = np.random.rand(r.size,theta.size)
dense_theta = np.linspace(0,2*np.pi,100)
v_indeces = np.zeros_like(dense_theta,dtype=np.int)
i = -1
for j,dt in enumerate(dense_theta):
if dt>=theta[i+1]:
i+=1
v_indeces[j] = i
T,R = np.meshgrid(dense_theta,r)
dense_values = np.zeros_like(T)
for i,v in enumerate(values):
for j,ind in enumerate(v_indeces):
dense_values[i,j] = v[ind]
ax = plt.subplot(111,polar=True)
ax.pcolor(T,R,dense_values)
plt.show()
Which would produce
I am not aware of a way to do this in matlab but I googled around and found this that says it can produce pcolor plots in polar coordinates. You should check it out.

Changing size of scattered points in matplotlib

I am doing some plotting using cartopy and matplotlib, and I am producing a few images using the same set of points with a different domain size shown in each image. As my domain size gets bigger, the size of each plotted point remains fixed, so eventually as I zoom out, things get scrunched up, overlapped, and generally messy. I want to change the size of the points, and I know that I could do so by plotting them again, but I am wondering if there is a way to change their size without going through that process another time.
this is the line that I am using to plot the points:
plt.scatter(longs, lats, color = str(platformColors[platform]), zorder = 2, s = 8, marker = 'o')
and this is the line that I am using to change the domain size:
ax.set_extent([lon-offset, lon+offset, lat-offset, lat+offset])
Any advice would be greatly appreciated!
scatter has the option set_sizes, which you can use to set a new size. For example:
import matplotlib.pylab as pl
import numpy as np
x = np.random.random(10)
y = np.random.random(10)
s = np.random.random(10)*100
pl.figure()
l=pl.scatter(x,y,s=s)
s = np.random.random(10)*100
l.set_sizes(s)
It seems that set_sizes only accepts arrays, so for a constant marker size you could do something like:
l.set_sizes(np.ones(x.size)*100)
Or for a relative change, something like:
l.set_sizes(l.get_sizes()*2)
http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.scatter
These are the parameters that plt.scatter take and the s parameter is the size of the scattered point so change s to whatever you like, something like so
plt.scatter(longs, lats, color = str(platformColors[platform]), zorder = 2, s = 20, marker = 'o')

Can python/matplotlib's confourf be made to plot a limited region of data?

Am trying to make a contour plot with matplotlib's contourf. Is there a way to zoom in on a particular region of my data and leave the rest unplotted? For instance, maybe my horizontal extent goes from -1 to 101, but I just want to plot the data that's in between 0 and 100 inclusive, and I want the boundary of the plot to be drawn at 0 on the left and 100 on the right. I thought the "extent" keyword would do the job, but it is inactive when X and Y data are given. I know that I can mask the extraneous data in various ways, but that leaves the boundaries of the plot drawn beyond my region of interest, which is not what I want. I guess I could also filter or interpolate my data to my region of interest and then give that filtered data to contourf, but if I can just make contourf focus on a particular region, it would be alot easier. Thanks.
Perhaps you are looking for plt.xlim:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-1,101,100)
y = np.linspace(-1,101,100)
x, y = np.meshgrid(x, y)
z = x*x+y*y
plt.figure()
plt.xlim(0, 50)
plt.contourf(x, y, z)
plt.show()
Above, plt.xlim(0, 50) was used instead of plt.xlim(0,100) just to emphasize the change. Without plt.xlim(0, 50) the plot looks like this:

Categories