Related
I am making a figure with subplots with different y-axes. The y-axis of the last plot needs to be represented in scientific notation, and I would like the numbers on the axis to only display 2 decimals, i.e., 2.15 instead 2.1565. I have been trying to do format the numbers for hours without any success. Any suggestion on how to do it?
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.ticker as mtick
from matplotlib.ticker import FormatStrFormatter
fig, axs = plt.subplots(2, 3)
y_pos = [0.2,0.4,0.6,0.8]
plt.setp(axs, xticks=[0.2,0.4,0.6,0.8], xticklabels=['A','B','C','D'],xlim=[0.1,0.9],facecolor='#F4F4F4')
#P-2
Pval = [3.3705E+00,3.4262E+00,3.5093E+00,3.4882E+00]
axs[0,0].set_ylim(3.3,3.55)
axs[0,0].set_yticks(np.arange(3.3,3.56, 0.05))
axs[0,0].grid(zorder=0)
axs[0,0].bar(y_pos,Pval,width=0.1,color='#ADDDCE')
axs[0,0].set_title(r'$Initial$')
axs[0,0].grid(True,linestyle='--',color='#D6D8CD')
#P-1
Pval = [1.6667E+00,1.6079E+00,1.6087E+00,1.6132E+00]
axs[0,1].set_ylim(1.5,1.7)
axs[0,1].set_yticks(np.arange(1.5,1.71, 0.05))
axs[0,1].grid(zorder=0)
axs[0,1].bar(y_pos,Pval,width=0.1,color='#FFC347')
axs[0,1].set_title(r'$Test 1$')
axs[0,1].grid(True,linestyle='--',color='#D6D8CD')
#P1
Pval = [9.9458E-01,9.4241E-01,9.4569E-01,9.4014E-01]
axs[0,2].set_ylim(0.8,1)
axs[0,2].set_yticks(np.arange(0.8,1.01, 0.05))
#
axs[0,2].grid(zorder=0)
axs[0,2].bar(y_pos,Pval,width=0.1,color='#FC746C')
#
axs[0,2].set_title(r'$Test 2$')
axs[0,2].grid(True,linestyle='--',color='#D6D8CD')
#P2
Pval = [2.4196E+00,2.3292E+00,2.3499E+00,2.3213E+00]
axs[1,0].set_ylim(1.8,2.5)
axs[1,0].set_yticks(np.arange(1.8,2.7, 0.2))
#
axs[1,0].grid(zorder=0)
axs[1,0].bar(y_pos,Pval,width=0.1,color='#70AE98')
#
axs[1,0].set_title(r'$ \omega $')
axs[1,0].grid(True,linestyle='--',color='#D6D8CD')
#P3
Pval = [1.5646E+01,1.5346E+01,1.5538E+01,1.5298E+01]
axs[1,1].set_ylim(15,15.7)
axs[1,1].set_yticks(np.arange(15.,15.9, 0.2))
#X
axs[1,1].grid(zorder=0)
axs[1,1].bar(y_pos,Pval,width=0.1,color='#F0A35E')
#
axs[1,1].set_title(r'$ \Pi $')
axs[1,1].grid(True,linestyle='--',color='#D6D8CD')
#P4
Pval = [2.1391E+02,2.1148E+02,2.1434E+02,2.1085E+02]
axs[1,2].set_ylim(2.09E+02,2.15E+02)
axs[1,2].set_yticks(np.arange(2.09E+02,2.16E+02,0.95))
#axs[1,2].ticklabel_format(style='sci',scilimits=(200,300),axis='y')
#
#axs[1,2].yaxis.set_major_formatter(MathTextSciFormatter('%.2e'))
#m = grabMaxExponent(2)
#axs[1,2].yaxis.set_major_formatter(OOMFormatter(2, "%1.1f"))
#axs[1,2].yaxis.set_major_formatter(mtick.FormatStrFormatter('%.2f'))
axs[1,2].ticklabel_format(axis='y', style='sci', scilimits=(2,2))
axs[1,2].yaxis.major.formatter._useMathText = True
#axs[1,2].yaxis.set_major_formatter(FormatStrFormatter('%.2f'))
#axs[1,2].yaxis.set_major_formatter(MathTextSciFormatter('%.2e'))
#axs[1,2].ticklabel_format(useOffset=False)
#X
axs[1,2].grid(zorder=0)
axs[1,2].bar(y_pos,Pval,width=0.1,color='#CA7E8D')
#
axs[1,2].set_title(r'$\gamma$')
axs[1,2].grid(True,linestyle='--',color='#D6D8CD')
fig.tight_layout()
plt.show()
ScalarFormatter doesn't support custom formatting. What you can try is overriding the method responsible for the formatting:
(NOTE: This works for Matplotlib 3.0.x - Python 3.x, NOT Matplotlib 2.0.x.)
from matplotlib.ticker import ScalarFormatter
...
# P4
# Extend ScalarFormatter
class MyScalarFormatter(ScalarFormatter):
# Override '_set_format' with your own
def _set_format(self):
self.format = '%.2f' # Show 2 decimals
# Your data as before
Pval = [2.1391E+02,2.1148E+02,2.1434E+02,2.1085E+02]
axs[1,2].set_ylim(2.09E+02,2.15E+02)
axs[1,2].set_yticks(np.arange(2.09E+02,2.16E+02,0.95))
# New code
custom_formatter = MyScalarFormatter(useMathText=True)
axs[1,2].yaxis.set_major_formatter(custom_formatter)
axs[1,2].yaxis.major.formatter.set_powerlimits((2,2))
#axs[1,2].yaxis.major.formatter.set_scientific(True) # Redundant: True is default
# Rest code as before
axs[1,2].grid(zorder=0)
axs[1,2].bar(y_pos,Pval,width=0.1,color='#CA7E8D')
axs[1,2].set_title(r'$\gamma$')
axs[1,2].grid(True,linestyle='--',color='#D6D8CD')
fig.tight_layout()
plt.show()
Output
It rounds your y-axis values, though. But you could work it out a bit this way to achieve the result you want. Take a look at ScalarFormatter source.
I am trying to customise a colorbar on my matpllotlib contourf plots. Whilst I am able to use scientific notation I am trying to change the base of the notation - essentially so that my ticks would be in the range of (-100,100) rather than (-10,10).
For example, this produces a simple plot...
import numpy as np
import matplotlib.pyplot as plt
z = (np.random.random((10,10)) - 0.5) * 0.2
fig, ax = plt.subplots()
plot = ax.contourf(z)
cbar = fig.colorbar(plot)
cbar.formatter.set_powerlimits((0, 0))
cbar.update_ticks()
plt.show()
like so:
However, I would like the label above the colorbar to be 1e-2 and the numbers to range from -10 to 10.
How would I go about this?
A possible solution can be to subclass the ScalarFormatter and fix the order of magnitude as in this question: Set scientific notation with fixed exponent and significant digits for multiple subplots
You would then call this formatter with the order of magnitude as the argument order, OOMFormatter(-2, mathText=False). mathText is set to false to obtain the notation from the question, i.e.
while setting it to True, would give .
You can then set the formatter to the colorbar via the colorbar's format argument.
import numpy as np; np.random.seed(0)
import matplotlib.pyplot as plt
import matplotlib.ticker
class OOMFormatter(matplotlib.ticker.ScalarFormatter):
def __init__(self, order=0, fformat="%1.1f", offset=True, mathText=True):
self.oom = order
self.fformat = fformat
matplotlib.ticker.ScalarFormatter.__init__(self,useOffset=offset,useMathText=mathText)
def _set_order_of_magnitude(self):
self.orderOfMagnitude = self.oom
def _set_format(self, vmin=None, vmax=None):
self.format = self.fformat
if self._useMathText:
self.format = r'$\mathdefault{%s}$' % self.format
z = (np.random.random((10,10)) - 0.5) * 0.2
fig, ax = plt.subplots()
plot = ax.contourf(z)
cbar = fig.colorbar(plot, format=OOMFormatter(-2, mathText=False))
plt.show()
For matplotlib versions < 3.1 the class needs to look like this:
class OOMFormatter(matplotlib.ticker.ScalarFormatter):
def __init__(self, order=0, fformat="%1.1f", offset=True, mathText=True):
self.oom = order
self.fformat = fformat
matplotlib.ticker.ScalarFormatter.__init__(self,useOffset=offset,useMathText=mathText)
def _set_orderOfMagnitude(self, nothing):
self.orderOfMagnitude = self.oom
def _set_format(self, vmin, vmax):
self.format = self.fformat
if self._useMathText:
self.format = '$%s$' % matplotlib.ticker._mathdefault(self.format)
Similar to what #ImportanceOfBeingErnes described, you could use a FuncFormatter (docs) to which you just pass a function to determine the tick labels. This removes the auto generation of the 1e-2 header for your colorbar, but I imagine you can manually add that back in (I had trouble doing it, though was able to add it on the side). Using a FuncFormatter, you can just generate string tick values which has the advantage of not having to accept the way python thinks a number should be displayed.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as tk
z = (np.random.random((10,10)) - 0.5) * 0.2
levels = list(np.linspace(-.1,.1,9))
fig, ax = plt.subplots()
plot = ax.contourf(z, levels=levels)
def my_func(x, pos):
label = levels[pos]
return str(label*100)
fmt1 = tk.FuncFormatter(my_func)
cbar = fig.colorbar(plot, format=fmt1)
cbar.set_label("1e-2")
plt.show()
This will generate a plot which looks like this.
I am trying to set the format to two decimal numbers in a matplotlib subplot environment. Unfortunately, I do not have any idea how to solve this task.
To prevent using scientific notation on the y-axis I used ScalarFormatter(useOffset=False) as you can see in my snippet below. I think my task should be solved by passing further options/arguments to the used formatter. However, I could not find any hint in matplotlib's documentation.
How can I set two decimal digits or none (both cases are needed)? I am not able to provide sample data, unfortunately.
-- SNIPPET --
f, axarr = plt.subplots(3, sharex=True)
data = conv_air
x = range(0, len(data))
axarr[0].scatter(x, data)
axarr[0].set_ylabel('$T_\mathrm{air,2,2}$', size=FONT_SIZE)
axarr[0].yaxis.set_major_locator(MaxNLocator(5))
axarr[0].yaxis.set_major_formatter(ScalarFormatter(useOffset=False))
axarr[0].tick_params(direction='out', labelsize=FONT_SIZE)
axarr[0].grid(which='major', alpha=0.5)
axarr[0].grid(which='minor', alpha=0.2)
data = conv_dryer
x = range(0, len(data))
axarr[1].scatter(x, data)
axarr[1].set_ylabel('$T_\mathrm{dryer,2,2}$', size=FONT_SIZE)
axarr[1].yaxis.set_major_locator(MaxNLocator(5))
axarr[1].yaxis.set_major_formatter(ScalarFormatter(useOffset=False))
axarr[1].tick_params(direction='out', labelsize=FONT_SIZE)
axarr[1].grid(which='major', alpha=0.5)
axarr[1].grid(which='minor', alpha=0.2)
data = conv_lambda
x = range(0, len(data))
axarr[2].scatter(x, data)
axarr[2].set_xlabel('Iterationsschritte', size=FONT_SIZE)
axarr[2].xaxis.set_major_locator(MaxNLocator(integer=True))
axarr[2].set_ylabel('$\lambda$', size=FONT_SIZE)
axarr[2].yaxis.set_major_formatter(ScalarFormatter(useOffset=False))
axarr[2].yaxis.set_major_locator(MaxNLocator(5))
axarr[2].tick_params(direction='out', labelsize=FONT_SIZE)
axarr[2].grid(which='major', alpha=0.5)
axarr[2].grid(which='minor', alpha=0.2)
See the relevant documentation in general and specifically
from matplotlib.ticker import FormatStrFormatter
fig, ax = plt.subplots()
ax.yaxis.set_major_formatter(FormatStrFormatter('%.2f'))
If you are directly working with matplotlib's pyplot (plt) and if you are more familiar with the new-style format string, you can try this:
from matplotlib.ticker import StrMethodFormatter
plt.gca().yaxis.set_major_formatter(StrMethodFormatter('{x:,.0f}')) # No decimal places
plt.gca().yaxis.set_major_formatter(StrMethodFormatter('{x:,.2f}')) # 2 decimal places
From the documentation:
class matplotlib.ticker.StrMethodFormatter(fmt)
Use a new-style format string (as used by str.format()) to format the
tick.
The field used for the value must be labeled x and the field used for
the position must be labeled pos.
The answer above is probably the correct way to do it, but didn't work for me.
The hacky way that solved it for me was the following:
ax = <whatever your plot is>
# get the current labels
labels = [item.get_text() for item in ax.get_xticklabels()]
# Beat them into submission and set them back again
ax.set_xticklabels([str(round(float(label), 2)) for label in labels])
# Show the plot, and go home to family
plt.show()
format labels using lambda function
3x the same plot with differnt y-labeling
Minimal example
import numpy as np
import matplotlib as mpl
import matplotlib.pylab as plt
from matplotlib.ticker import FormatStrFormatter
fig, axs = mpl.pylab.subplots(1, 3)
xs = np.arange(10)
ys = 1 + xs ** 2 * 1e-3
axs[0].set_title('default y-labeling')
axs[0].scatter(xs, ys)
axs[1].set_title('custom y-labeling')
axs[1].scatter(xs, ys)
axs[2].set_title('x, pos arguments')
axs[2].scatter(xs, ys)
fmt = lambda x, pos: '1+ {:.0f}e-3'.format((x-1)*1e3, pos)
axs[1].yaxis.set_major_formatter(mpl.ticker.FuncFormatter(fmt))
fmt = lambda x, pos: 'x={:f}\npos={:f}'.format(x, pos)
axs[2].yaxis.set_major_formatter(mpl.ticker.FuncFormatter(fmt))
You can also use 'real'-functions instead of lambdas, of course.
https://matplotlib.org/3.1.1/gallery/ticks_and_spines/tick-formatters.html
In matplotlib 3.1, you can also use ticklabel_format. To prevents scientific notation without offsets:
plt.gca().ticklabel_format(axis='both', style='plain', useOffset=False)
I am trying to plot some data, using a for loop to plot distributions. Now I want to label those distributions according to the loop counter as the subscript in math notation. This is where I am with this at the moment.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.mlab as mlab
mean = [10,12,16,22,25]
variance = [3,6,8,10,12]
x = np.linspace(0,40,1000)
for i in range(4):
sigma = np.sqrt(variance[i])
y = mlab.normpdf(x,mean[i],sigma)
plt.plot(x,y,label=$v_i$) # where i is the variable i want to use to label. I should also be able to use elements from an array, say array[i] for the same.
plt.xlabel("X")
plt.ylabel("P(X)")
plt.legend()
plt.axvline(x=15, ymin=0, ymax=1,ls='--',c='black')
plt.show()
This doesn't work, and I can't keep the variable between the $ $ signs of the math notation, as it is interpreted as text. Is there a way to put the variable in the $ $ notation?
The original question has been edited, this answer has been updated to reflect this.
When trying to work with LaTeX formatting in matplotlib you must use raw strings, denoted by r"".
The code given below will iterate over range(4) and plot using i'th mean and variance (as you originally have done). It will also set the label for each plot using label=r'$v_{}$'.format(i+1). This string formatting simply replaces the {} with whatever is called inside format, in this case i+1. In this way you can automate the labels for your plots.
I have removed the plt.axvline(...), plt.xlabel(...) and plt.ylabel(...) out of the for loop as you only need to call it once. I've also removed the plt.legend() from the for loop for the same reason and have removed its arguments. If you supply the keyword argument label to plt.plot() then you can label your plots individually as you plot them.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.mlab as mlab
mean = [10,12,16,22,25]
variance = [3,6,8,10,12]
x = np.linspace(0,40,1000)
for i in range(4):
sigma = np.sqrt(variance[i])
y = mlab.normpdf(x,mean[i],sigma)
plt.plot(x,y, label=r'$v_{}$'.format(i+1))
plt.xlabel("X")
plt.ylabel("P(X)")
plt.axvline(x=15, ymin=0, ymax=1,ls='--',c='black')
plt.legend()
plt.show()
So it turns out that you edited your question based on my answer. However, you;re still not quite there. If you want to do it the way I think you want to code it, it should be like this:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.mlab as mlab
mean = [10, 12, 16, 22, 25]
variance = [3, 6, 8, 10, 12]
x = np.linspace(0, 40, 1000)
for i in range(4):
sigma = np.sqrt(variance[i])
y = mlab.normpdf(x, mean[i], sigma)
plt.plot(x, y, label = "$v_{" + str(i) + "}$")
plt.xlabel("X")
plt.ylabel("P(X)")
plt.legend()
plt.axvline(x = 15, ymin = 0, ymax = 1, ls = '--', c = 'black')
plt.show()
This code generates the following figure:
In case you want the first plot start with v_1 instead of v_0 all you need to change is str(i+1). This way the subscripts are 1, 2, 3, and 4 instead of 0, 1, 2 and 3.
Hope this helps!
Right, so I've got a list of x values, y values and z values (which I think I converted into arrays?) which I want to make a surface plot, but it's not working.
Here's what I'm trying to do, you can ignore most of the code as it is pretty irrelevant - just look at the end where I have xdis, ydis and dist and where I'm trying to plot atm I'm getting ValueError: need more than 1 value to unpack :(. Help much appreciated.
from math import *
from numpy import *
import pylab
def sweep (v,p,q,r,s):
a=.98
for i in range (1, len(v)-1):
for j in range (1, len(v)-1):
c =0.0
if i==p and j==q: c =1.0
if i==r and j==s: c= -1.0
v[i,j]=(v[i -1,j]+v[i+1,j]+v[i,j -1]+v[i,j+1]+c-a*v[i,j])/(4-a)
def main():
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
ydis=[]
xdis=[]
resis=[]
for j in range(2,18):
for i in range(2,18):
v= zeros ((20,20),float )
p=q=9
r=i
s=j
dv =1.0e10
lastdv =0
count =0
while (fabs(dv - lastdv)>1.0e-7*fabs(dv)):
lastdv =dv
sweep(v,p,q,r,s)
dv=v[p,q]-v[r,s]
resis.append(dv)
xdis.append(r-p)
ydis.append(s-q)
X=array(xdis)
Y=array(ydis)
Z=array(resis)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_wireframe(X,Y,Z)
plt.show()
main()
plot_wireframe expects three 2D-arrays (X,Y,Z) as input. So,
after:
X=np.array(xdis)
Y=np.array(ydis)
Z=np.array(resis)
add:
X=X.reshape((-1,16))
Y=Y.reshape((-1,16))
Z=Z.reshape((-1,16))
It doesn't seem like the "sweep" function is modifying 'v' so you're getting an empty list.