matplotlib polar plot scientific notation - matplotlib

I am trying to draw a polar plot with matplotlib and would like to do the following:
a) show tick labels with scientific notation
b) show radius circles at specified intervals.
Can anyone give me advice on how to do a) and b) with the code below
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111, polar=True)
theta =[np.pi/3, np.pi/3]
theta2 =[np.pi/6, np.pi/6]
r = [0.0, 8.0e-04]
r2 = [0.0, 7.0e-04]
ax.plot(theta, r, 'r-', label ='Observed')
ax.plot(theta2, r2, 'b-', label ='Simulated')
ax.grid(True)
ax.legend(loc='upper center', bbox_to_anchor=(0.5, 1.25),
ncol=3, fancybox=True, shadow=True)
plt.show()

For specifying location of radial ticks, it is very simple - you can just set rticks:
ax.set_rticks([0.0002, 0.0004, 0.0006, 0.0008])
There are a couple of options for the formatting, it depends on how exactly you want the ticks to be displayed. The default formatter will automatically switch to scientific for large and small numbers. If you want to change its threshold for what it considers "small", you can do that by modifying the yaxis formatter (the y axis is the radial axis):
ax.yaxis.get_major_formatter().set_powerlimits((-3,4)) # Things smaller than 1e-3
# will be in scientific
# notation
However, that looks a bit funny to me, it puts the little "1e-4" in the upper left of the plot.
So, if you want to force the current radial ticks to be scientific notation where they are, one way is to use your own format for them. The following uses FormatStrFormatter:
import matplotlib.ticker as ticker
# plotting code here
frmtr = ticker.FormatStrFormatter('%4.1e')
ax.yaxis.set_major_formatter(frmtr)
There are plenty of options available via matplotlib.ticker if this doesn't do exactly what you wanted. The second formatting option give me this:

Related

Pyplot axis limits within boundaries

Is there an easy way to avoid pyplot zooming far into noisy data?
Something like a lower boundary for the axis limits.
I am not trying to set a fix boundary to my axis, as this will fully disable automatic scaling.
Maybe a "minimum tick distance" would also work.
Right now I am using an additional 'invisible' plot in my graph that will define the maximum zoom.
Some example that illustrates what I want to achieve:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 100, 1)
noise = np.random.randn(len(x))*0.1
y = 10+noise
y_dummy_low = [0]*len(x)
y_dummy_high = [20]*len(x)
plt.figure()
plt.plot(x, y) # noise data i actually want to plot
plt.plot(x, y_dummy_low, y_dummy_high, marker="None", linestyle="None") # this will avoid zooming too much
plt.show()
Zooming too far
Zooming OK

show origin axis (x,y) in matplotlib plot

I have following simple plot, and I would like to display the origin axis (x, y). I already have grid, but I need the x, y axis to be emphasized.
this is my code:
x = linspace(0.2,10,100)
plot(x, 1/x)
plot(x, log(x))
axis('equal')
grid()
I have seen this question. The accepted answer suggests to use "Axis spine" and just links to some example. The example is however too complicated, using subplots. I am unable to figure out, how to use "Axis spine" in my simple example.
Using subplots is not too complicated, the spines might be.
Dumb, simple way:
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0.2,10,100)
fig, ax = plt.subplots()
ax.plot(x, 1/x)
ax.plot(x, np.log(x))
ax.set_aspect('equal')
ax.grid(True, which='both')
ax.axhline(y=0, color='k')
ax.axvline(x=0, color='k')
And I get:
(you can't see the vertical axis since the lower x-limit is zero.)
Alternative using simple spines
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0.2,10,100)
fig, ax = plt.subplots()
ax.plot(x, 1/x)
ax.plot(x, np.log(x))
ax.set_aspect('equal')
ax.grid(True, which='both')
# set the x-spine (see below for more info on `set_position`)
ax.spines['left'].set_position('zero')
# turn off the right spine/ticks
ax.spines['right'].set_color('none')
ax.yaxis.tick_left()
# set the y-spine
ax.spines['bottom'].set_position('zero')
# turn off the top spine/ticks
ax.spines['top'].set_color('none')
ax.xaxis.tick_bottom()
Alternative using seaborn (my favorite)
import numpy as np
import matplotlib.pyplot as plt
import seaborn
seaborn.set(style='ticks')
x = np.linspace(0.2,10,100)
fig, ax = plt.subplots()
ax.plot(x, 1/x)
ax.plot(x, np.log(x))
ax.set_aspect('equal')
ax.grid(True, which='both')
seaborn.despine(ax=ax, offset=0) # the important part here
Using the set_position method of a spine
Here are the docs for a the set_position method of spines:
Spine position is specified by a 2 tuple of (position type, amount).
The position types are:
'outward' : place the spine out from the data area by the specified number of points. (Negative values specify placing the
spine inward.)
'axes' : place the spine at the specified Axes coordinate (from
0.0-1.0).
'data' : place the spine at the specified data coordinate.
Additionally, shorthand notations define a special positions:
'center' -> ('axes',0.5)
'zero' -> ('data', 0.0)
So you can place, say the left spine anywhere with:
ax.spines['left'].set_position((system, poisition))
where system is 'outward', 'axes', or 'data' and position in the place in that coordinate system.
Some time has passed since this question was asked. With Matplotlib 3.6.2 it looks like this works:
plt.axhline(0, color='black', linewidth=.5)
plt.axvline(0, color='black', linewidth=.5)
and there are other options.
Let me answer to this (rather old) question for those who will search for it as I just did. Although it suggested working solutions, I consider the (only) provided answer as way too complex, when it comes to such a simple situation like that described in the question (note: this method requires you to specify all axes endpoints).
I found a simple working solution in one of the first tutorials on matplotlib's pyplot. It is sufficient to add the following line after the creation of the plot
plt.axis([xmin, xmax, ymin, ymax])
as in the following example:
from matplotlib import pyplot as plt
xs = [1,2,3,4,5]
ys = [3,5,1,2,4]
plt.scatter(xs, ys)
plt.axis([0,6,0,6]) #this line does the job
plt.show()
which produces the following result:

Python matplotlib: fractional logscale

I would like to plot some data with a fractional logscale, such that the y axis has the ticks at 10^(-0.1), 10^(-0.2), 10^(-0.3), etc.
The problem is that when I plot my data, there are only ticks at 10^0 and 10^-1, which leaves the slope of the line too slight to see.
Is is possible to set a fractional logscale this way?
Thanks
It sounds like you want tick labels, not the tick marks in particular. In most figures, the minor tick marks are already there where you want them.
The following may then work, though I would think there's an easier way. Note that I'm applying labels to the minor tick marks only: the (two) major tick marks already have a label. Unfortunately, the fonts of the two types of tick marks are not the same; I think that's a result of the LaTeX equation usage.
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
X = np.logspace(0, 3)
Y = X**-0.2
plt.plot(X,Y)
plt.yscale('log')
yticks = np.linspace(-0.1, -0.9, 9)
ax.set_yticks(10**yticks, minor=True)
ax.set_ylim(0.1, 1)
ax.set_yticklabels(['$10^{{{:.1f}}}$'.format(ytick) for ytick in yticks], minor=True)
plt.show()
which results in:
For the issue of the different label fonts, you can manually change the major tick labels:
ax.set_yticks([1, 0.1])
ax.set_yticklabels(['$10^0$', '$10^{-1}$'])
(and probably the same for the x-axis).

heatmap for positive and negative values [duplicate]

I am trying to make a filled contour for a dataset. It should be fairly straightforward:
plt.contourf(x, y, z, label = 'blah', cm = matplotlib.cm.RdBu)
However, what do I do if my dataset is not symmetric about 0? Let's say I want to go from blue (negative values) to 0 (white), to red (positive values). If my dataset goes from -8 to 3, then the white part of the color bar, which should be at 0, is in fact slightly negative. Is there some way to shift the color bar?
First off, there's more than one way to do this.
Pass an instance of DivergingNorm as the norm kwarg.
Use the colors kwarg to contourf and manually specify the colors
Use a discrete colormap constructed with matplotlib.colors.from_levels_and_colors.
The simplest way is the first option. It is also the only option that allows you to use a continuous colormap.
The reason to use the first or third options is that they will work for any type of matplotlib plot that uses a colormap (e.g. imshow, scatter, etc).
The third option constructs a discrete colormap and normalization object from specific colors. It's basically identical to the second option, but it will a) work with other types of plots than contour plots, and b) avoids having to manually specify the number of contours.
As an example of the first option (I'll use imshow here because it makes more sense than contourf for random data, but contourf would have identical usage other than the interpolation option.):
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import DivergingNorm
data = np.random.random((10,10))
data = 10 * (data - 0.8)
fig, ax = plt.subplots()
im = ax.imshow(data, norm=DivergingNorm(0), cmap=plt.cm.seismic, interpolation='none')
fig.colorbar(im)
plt.show()
As an example of the third option (notice that this gives a discrete colormap instead of a continuous colormap):
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import from_levels_and_colors
data = np.random.random((10,10))
data = 10 * (data - 0.8)
num_levels = 20
vmin, vmax = data.min(), data.max()
midpoint = 0
levels = np.linspace(vmin, vmax, num_levels)
midp = np.mean(np.c_[levels[:-1], levels[1:]], axis=1)
vals = np.interp(midp, [vmin, midpoint, vmax], [0, 0.5, 1])
colors = plt.cm.seismic(vals)
cmap, norm = from_levels_and_colors(levels, colors)
fig, ax = plt.subplots()
im = ax.imshow(data, cmap=cmap, norm=norm, interpolation='none')
fig.colorbar(im)
plt.show()

matplotlib: adding padding/offset to polar plots tick labels

Is there a way to increase the padding/offset of the polar plot tick labels (theta)?
import matplotlib
import numpy as np
from matplotlib.pyplot import figure, show, grid
# make a square figure
fig = figure(figsize=(2, 2))
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True, axisbg='#d5de9c')
ax.set_yticklabels([])
r = np.arange(0, 3.0, 0.01)
theta = 2*np.pi*r
ax.plot(theta, r, color='#ee8d18', lw=3)
ax.set_rmax(2.0)
show()
I'd like to have theta tick labels further away from the polar plot so they don't overlap.
First of all; seeing as how you have specified the figsize to be (2,2) and having the ax occupy 80 % of both the width and height, you have very little space left over to pad the ticklabels. This could cause the ticklabels to be "cut off" at the figure's egdes. This can easily be "fixed" by either
Specifying bigger figsize
Make the ax occupy less space on the (2,2) sized figure
Use smaller fontsize for the ticklabels
or any combination of these. Another, in my opinion better, solution to this "problem" is to use a subplot rather than specifying the Axes's bounds;
ax = fig.add_subplot(111, polar=True, axisbg='#d5de9c')
as this makes it possible to use the method tight_layout() which automatically configures the figure layout to nicely include all elements.
Then over to the real problem at hand; the padding. On a PolarAxes you can set, among other things, the radial placement of the theta-ticks. This is done by specifying the fraction of the polar axes radius where you want the ticklabels to be placed as an argument to the frac parameter of the PolarAxes's set_thetagrids() method. The argument should be a fraction of the axes' radius where you want the ticklabels placed. I.e. for frac < 1 the ticklabels will be placed inside the axes, while for frac > 1 they will be placed outside the axes.
Your code could then be something like this:
import numpy as np
from matplotlib.pyplot import figure, show, grid, tight_layout
# make a square figure
fig = figure(figsize=(2, 2))
ax = fig.add_subplot(111, polar=True, axisbg='#d5de9c')
ax.set_yticklabels([])
r = np.arange(0, 3.0, 0.01)
theta = 2*np.pi*r
ax.plot(theta, r, color='#ee8d18', lw=3)
ax.set_rmax(2.0)
# tick locations
thetaticks = np.arange(0,360,45)
# set ticklabels location at 1.3 times the axes' radius
ax.set_thetagrids(thetaticks, frac=1.3)
tight_layout()
show()
You should try different values for frac to find a value that is best suited for your needs.
If you don't specify a value to the parameter frac as above, i.e. frac has default value None, the code outputs a plot as below. Notice how the radius of the plot is bigger, as the ticklabels don't "occupy as much space" as in the example above.
As of matplotlib 2.1.0, the functionality of the original answer is now deprecated - polar axes now obey to the parameters of ax.tick_params:
ax.tick_params(pad=123)
should do the trick.