I'm trying to fill the area under a curve with matplotlib. The script below works fine.
import matplotlib.pyplot as plt
from math import sqrt
x = range(100)
y = [sqrt(i) for i in x]
plt.plot(x,y,color='k',lw=2)
plt.fill_between(x,y,0,color='0.8')
plt.show()
However if I set the y-scale to logarithmic (see below). It sometimes fills the area above the curve ! Can anyone help me? I would like to fill the area between the curve and y = 0.
x = range(100)
y = [sqrt(i) for i in x]
plt.plot(x,y,color='k',lw=2)
plt.fill_between(x,y,0,color='0.8')
plt.yscale('log')
plt.show()
Thanks in advance!
With a logarithmic y-scale, fill_between(x, y, 0) tells matplotlib to fill the region between log(0) = -infinity and log(y). Naturally, it balks. You can avoid the problem by changing 0 to some small number like 1e-6.
As mentioned, 0 -> -inf in a log scale. Thus, any plotted value that was less than or equal to zero would be problematic (requiring an infinite ylim in log space). This problem exists independently of whether you are using fill_between() or not.
Fortunately, matplotlib provides a way to handle this nicely. In the default behavior, matplotlib masks the values of every value less than or equal to zero. In your example, this means that your entire y=0 line is masked and excluded from the polygon defining the filled-between area. The result is that the polygon is simply closed by drawing a line from (100,10) down and leftward to (0,0). Another option is to clip the values. In this case, they are set to 1e-300 and are not consulted when determining the ylim of the plot. So to get your desired result, do the following:
plt.yscale('log', nonposy='clip')
Related
I have x values, y values, and z values. The z values are either 0 or 1, essentially indicating whether an (x,y) pair is a threat (1) or not a threat (0).
I have been trying to plot a 2D contour plot using the matplotlib contourf. This seems to have been interpolating between my z values, which I don't want. So, I did a bit of searching and found that I could use pcolormesh to better plot binary data. However, I am still having some issues.
First, the colorbar of my pcolormesh plot doesn't show two distinct colors (white or red). Instead, it shows a full spectrum from white to red. See the attached plot for what I mean. How do I change this so that the colorbar only shows two colors, for 0 and 1? Second, is there a way to draw a grid of squares into the contour plot so that it is more clear for which x and y intervals the 0s and 1s are occurring. Third, my code calls for minorticks. However, these do not show up in the plot. Why?
The code which I use is shown here. The vels and ms for x and y can really be anything, and the threat_bin is just the corresponding 0 or 1 values for all the (vets,ms) pairs:
fig=plt.figure(figsize=(6,5))
ax2=fig.add_subplot(111)
from matplotlib import cm
XX,YY=np.meshgrid(vels, ms)
cp=ax2.pcolormesh(XX/1000.0,YY,threat_bin, cmap=cm.Reds)
ax2.minorticks_on()
ax2.set_ylabel('Initial Meteoroid Mass (kg)')
ax2.set_xlabel('Initial Meteoroid Velocity (km/s)')
ax2.set_yscale('log')
fig.colorbar(cp, ticks=[0,1], label='Threat Binary')
plt.show()
Please be simple with your recommendations, and let me know the code I should include or change with respect to what I have at the moment.
I have the equation: z(x,y)=1+x^(2/3)y^(-3/4)
I would like to calculate values of z for x=[0,100] and y=[10^1,10^4]. I will do this for 100 points in each axis direction. My grid, then, will be 100x100 points. In the x-direction I want the points spaced linearly. In the y-direction I want the points space logarithmically.
Were I to need these values I could easily go through the following:
x=np.linspace(0,100,100)
y=np.logspace(1,4,100)
z=np.zeros( (len(x), len(y)) )
for i in range(len(x)):
for j in range(len(y)):
z[i,j]=1+x[i]**(2/3)*y[j]**(-3/4)
The problem for me comes with visualizing these results. I know that I would need to create a grid of points. I feel my options are to create a meshgrid with the values and then use pcolor.
My issue here is that the values at the center of the block do not coincide with the calculated values. In the x-direction I could fix this by shifting the x-vector by half of dx (the step between successive values). I'm not so sure how I would do this for the y-axis. Furthermore, If I wanted to compute values for each of the y-direction values, including the end points, they would not all show up.
In the final visualization I would like to have the y-axis as a log scale and the x axis as a linear scale. I would also like the tick marks to fall in the center of the cells, correlating with the correct value. Can someone point me to the correct plotting functions for this. I have to resolve the issue using pcolor or pcolormesh.
Should you require more details, please let me know.
In current matplotlib, you can use pcolormesh with shading='nearest', and it will center the blocks with the values:
import matplotlib.pyplot as plt
y_plot = np.log10(y)
z[5, 5] = 0 # to make it more evident
plt.pcolormesh(x, y_plot, z, shading="nearest")
plt.colorbar()
ax = plt.gca()
ax.set_xticks(x)
ax.set_yticks(y_plot)
plt.axvline(x[5])
plt.axhline(y_plot[5])
Output:
I'm plotting some 2D fields using matplotlib and the fields have to be seen with equal aspect ratio. But when I set the aspect ratio I find that there are unnecessary blank spaces. Please consider the following example:
from matplotlib import pyplot as plt
import numpy as np
x=np.arange(100)
y=np.arange(100)
Y, X = np.meshgrid(y,x)
Z = X + Y
plt.contourf(X, Y, Z)
#plt.axes().set_aspect('equal', 'datalim')
plt.tight_layout()
plt.colorbar()
plt.grid()
plt.show()
If I run that command I get this figure:
However, let's say I uncomment the line that sets the equal ratio . So let's say I include this:
plt.axes().set_aspect('equal', 'datalim')
I get the following output:
Which is a very poor use of space. I can't make the actual plot take better advantage of the figure space no matter how hard I try (I don't have that much knowledge of pyplot).
I there a way to expand the actual data part of the equal-ratio plot so that I have less white space?
Thank you.
The issue you're having is caused by "datalim", which asks the axes to apply the usual limits you would expect from a normal line or scatter plot, e.g. the use of 5% margin on each side of the shown data.
I do not see any reason to use "datalim" here. So you may just leave it out,
plt.axes().set_aspect('equal')
and get a plot with equal aspect and no white space around.
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.
I am having difficulty understanding how matplotlib.pyplot.xlim() works.
I am plotting a simple plot of x values vs y values. The y values are numerical points in the range of 100-600. The x values are of magnitude e-09 to e-13. So, I plot x against y. This is my plot, with generic pseudocode
import matplotlib.pyplot as plt
x = np.array
y = np.array
plt.plot(x,y)
plt.ylim(0,400)
plt.show()
As you can tell, there's plenty of structure between 0 and 0.5. I would like to look at that.
So, I try
plt.plot(x,y)
plt.xlim(0,0.5)
plt.ylim(0,400)
plt.show()
The output plot is completely blank. I see nothing.
So, I try, xlim= -1 to +1
plt.plot(x,y)
plt.xlim(-1,1)
plt.ylim(0,400)
plt.show()
This is the output plot.
Using the origninal plot, how can I set the x-axis to see the actual data?
As you clearly mentioned
The x values are of magnitude e-09 to e-13.
So if you want to see the values that lie within 1e-8 and 0.5e-9 you should do:
plt.xlim(1e-8,0.5e-9)
instead of
plt.xlim(0,0.5)
where you have no values to show as the values of x are within e-09 to e-13.
If your x-values are of magnitude of 1e-9 till 1e-13 you have completely different length scales. In this case a logarithmic axis may be appropriate. Note that this works only if all x-values are strictly positive.
plt.xscale('log')