I want to plot a graph with one logarithmic axis using matplotlib.
I've been reading the docs, but can't figure out the syntax. I know that it's probably something simple like 'scale=linear' in the plot arguments, but I can't seem to get it right
Sample program:
import pylab
import matplotlib.pyplot as plt
a = [pow(10, i) for i in range(10)]
fig = plt.figure()
ax = fig.add_subplot(2, 1, 1)
line, = ax.plot(a, color='blue', lw=2)
pylab.show()
You can use the Axes.set_yscale method. That allows you to change the scale after the Axes object is created. That would also allow you to build a control to let the user pick the scale if you needed to.
The relevant line to add is:
ax.set_yscale('log')
You can use 'linear' to switch back to a linear scale. Here's what your code would look like:
import pylab
import matplotlib.pyplot as plt
a = [pow(10, i) for i in range(10)]
fig = plt.figure()
ax = fig.add_subplot(2, 1, 1)
line, = ax.plot(a, color='blue', lw=2)
ax.set_yscale('log')
pylab.show()
First of all, it's not very tidy to mix pylab and pyplot code. What's more, pyplot style is preferred over using pylab.
Here is a slightly cleaned up code, using only pyplot functions:
from matplotlib import pyplot
a = [ pow(10,i) for i in range(10) ]
pyplot.subplot(2,1,1)
pyplot.plot(a, color='blue', lw=2)
pyplot.yscale('log')
pyplot.show()
The relevant function is pyplot.yscale(). If you use the object-oriented version, replace it by the method Axes.set_yscale(). Remember that you can also change the scale of X axis, using pyplot.xscale() (or Axes.set_xscale()).
Check my question What is the difference between ‘log’ and ‘symlog’? to see a few examples of the graph scales that matplotlib offers.
if you want to change the base of logarithm, just add:
plt.yscale('log',base=2)
Before Matplotlib 3.3, you would have to use basex/basey as the bases of log
You simply need to use semilogy instead of plot:
from pylab import *
import matplotlib.pyplot as pyplot
a = [ pow(10,i) for i in range(10) ]
fig = pyplot.figure()
ax = fig.add_subplot(2,1,1)
line, = ax.semilogy(a, color='blue', lw=2)
show()
I know this is slightly off-topic, since some comments mentioned the ax.set_yscale('log') to be "nicest" solution I thought a rebuttal could be due. I would not recommend using ax.set_yscale('log') for histograms and bar plots. In my version (0.99.1.1) i run into some rendering problems - not sure how general this issue is. However both bar and hist has optional arguments to set the y-scale to log, which work fine.
references:
http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.bar
http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.hist
So if you are simply using the unsophisticated API, like I often am (I use it in ipython a lot), then this is simply
yscale('log')
plot(...)
Hope this helps someone looking for a simple answer! :).
Related
I've got an issue with matplotlib and the way it displays graphs.
In my Python Crash Course coursebook, one of early graphs is meant to display up to 1000 on the x axis, and up to 1,000,000 on the y axis. Instead it displays a float of up to 2.0, and 1e6 at the top.
I use VSCode. I worry I haven't properly configured it. When displaying the course materials made by the developer, I have the same problem.
Here's the graph I want.
Here's the graph I've got.
And here's my code.
import matplotlib.pyplot as plt
x_values = range(1, 1001)
y_values = [x**2 for x in x_values]
plt.style.use('seaborn')
fig, ax = plt.subplots()
ax.scatter(x_values, y_values, c=y_values, cmap=plt.cm.Blues, s=10)
# Set chart title and label axes.
ax.set_title("Square Numbers", fontsize=24)
ax.set_xlabel("Value", fontsize=14)
ax.set_ylabel("Square of Value", fontsize=14)
# Set size of tick labels.
ax.tick_params(axis='both', which='major', labelsize=14)
# Set the range for each axis.
ax.axis([0, 1100, 0, 1100000])
plt.show()
If anyone has any experience with this, please let me know. I'm happy to change to another IDE that displays this properly, any recommendations would be welcome.
This is default matplotlib behaviour. You can turn this off by creating a custom ScalarFormatter object and turning scientific notation off. For more details, see the matplotlib documentation pages on tick formatters and on ScalarFormatter.
# additional import statement at the top
import matplotlib.pyplot as plt
from matplotlib import ticker
# additional code before plt.show()
formatter = ticker.ScalarFormatter()
formatter.set_scientific(False)
ax.yaxis.set_major_formatter(formatter)
Note that, most likely, the axis label will be slightly cut off. One way to fix this is by adding fig.tight_layout() before plt.show().
Responding to an old question in case it helps someone, but place the following before "plt.show()"
ax.ticklabel_format(style='plain')
I am working with matplotlib to generate some graphs but I do not know the difference between these two ways of showing an image. I already read some documentation about it but I do not understand yet.
First way:
import matplotlib.pyplot as plt
plt.figure()
plt.plot(x, y)
plt.show()
Second way:
import matplotlib.pyplot as plt
graph = plt.figure()
plt.plot(x, y)
graph.show()
I think this two ways do not do the same thing but it is not clear to me.
Could someone explain it step by step for the two ways?
Simplified, plt.show() will start an event loop and create a graphical representation for each figure that is active inside the pyplot state.
In contrast, fig.show(), where fig is a figure instance, would show only this figure. Since it would also not block, it is (only) useful in interactive sessions; else the figure would be closed directly after showing it due to the script exiting.
In the usual case you would hence prefer plt.show(). This does not prevent you from using the object-oriented interface. A recommended way of creating and showing a figure is hence,
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(x, y)
plt.show()
For two windows you can just repeat the plotting,
import matplotlib.pyplot as plt
fig1, ax1 = plt.subplots()
ax1.plot(x1, y1)
fig2, ax2 = plt.subplots()
ax2.plot(x2, y2)
plt.show()
Matplotlib has two styles of API implemented. One is object based (graph.show()) and the other is procedural (plt.show()) and looks a lot like the Matlab plotting API.
The procedural API works on the current figure and/or set of axes. You can always getting the current figure with plt.gcf() and the current axes with plt.gca().
There are occasionally some slight differences in syntax here and there. For example, if you want to set the x axis limits:
plt.xlim([0, 10])
or
ax = plt.gca()
ax.set_xlim([0, 10])
plt.figure returns an object that is assigned with graph = plt.figure() to graph . this is used when specific characteristics of this object ( the plot ) are intended to be changed, now the object can be refered to by its instance graph ( object-based plotting )
you use this i.e. if you want to access the axes of the graph or labels, subplots, ...
see https://python4mpia.github.io/plotting/advanced.html for object-based plotting
to manipulate the plot object you have to get a reference to it ( handle ) and this is done by graph = plt.figure() ( cf Object-Oriented Programming )
I'm trying to get the functionality of fill_betweenx() without having to use the function itself, because it doesn't accept the interpolate parameter. I need the interpolate functionality that is supported by fill_between(), but for the filling to happen relative to the x axis. It sounds like the interpolate parameter will be supported for fill_betweenx() in matplotlib 2.1, but it would be great to have access to the functionality via a workaround in the meantime.
This is the line of code in question:
ax4.fill_betweenx(x,300,p, where=p>=150, interpolate=True, facecolor='White', lw=1, zorder=2)
Unfortunately this gives me AttributeError: Unknown property interpolate.
One lazy way to do it is to use the fill_between() function with inverted coordinates on a figure that you don't show (i.e. close the figure before using plt.show()), and then re-use the vertices of the PolyCollection that fill_between() returns on your actual plot. It's not perfect, but it works as a quick fix. Here an example of what I'm talking about:
from matplotlib import pyplot as plt
from matplotlib.collections import PolyCollection
import numpy as np
fig, axes = plt.subplots(nrows = 2, ncols =2, figsize=(8,8))
#the data
x = np.linspace(0,np.pi/2,3)
y = np.sin(x)
#fill_between without interpolation
ax = axes[0,0]
ax.plot(x,y,'k')
ax.fill_between(x,0.5,y,where=y>0.25)
#fill_between with interpolation, keep the PolyCollection
ax = axes[0,1]
ax.plot(x,y,'k')
poly_col = ax.fill_between(x,0.5,y,where=y>0.25,interpolate=True)
#fill_betweenx -- no interpolation possible
ax = axes[1,0]
ax.plot(y,x,'k')
ax.fill_betweenx(x,0.5,y,where=y>0.25)
#faked fill_betweenx:
ax = axes[1,1]
ax.plot(y,x,'k')
#get the vertices from the saved PolyCollection, swap x- and y-values
v=poly_col.get_paths()[0].vertices
#convert to correct format
v2=list(zip(v[:,1],v[:,0]))
#and add to axes
ax.add_collection(PolyCollection([v2]))
#voila
plt.show()
The result of the code looks like this:
I have some test data:
import numpy as np
x_data = np.arange(10)
y = np.random.rand(len(x_data))
With different properties
ix1 = x_data < 5
ix2 = x_data >= 5
I want to investigate the differences visually, but am messing the plot up:
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_context('poster')
fig, ax = plt.subplots(figsize=(4, 4))
for i, x in enumerate(x_data):
if ix1[i]:
sns.set_palette('rainbow', sum(ix1))
if ix2[i]:
sns.set_palette('coolwarm', sum(ix2))
plt.plot(x, y[i], 'o', label='{}'.format(x))
plt.legend(loc='best', prop={'size': 6})
plt.show()
The result should be points 0-4 are rainbow (red-violet) and points 5-9 are coolwarm (blue-white-red), but instead:
So, two questions:
Is it ok to call sns.set_palette() after calling plt.subplots?
Is there a way to set the palette more than once?
No, because of the way matplotlib works, the color palette is a property of the Axes object and so whatever the currently set palette is at the time an Axes is created is what it's going to use. This is possible to get around if you want to hack on private attributes (see here), but I wouldn't really recommend that.
Here's what I could come up with in your case, using a somewhat different approach that might not be broadly applicable:
pal1 = sns.color_palette('rainbow', sum(ix1))
pal2 = sns.color_palette('coolwarm', sum(ix2))
fig, ax = plt.subplots(figsize=(4, 4))
ax.scatter(x_data[ix1], y[ix1], c=pal1, s=60, label="smaller")
ax.scatter(x_data[ix2], y[ix2], c=pal2, s=60, label="larger")
ax.legend(loc="lower right", scatterpoints=5)
FWIW, this visualization feels pretty complex and hard to process (and the two palettes you've chosen overlap a fair amount and aren't really appropriate for these data) so it might be worth starting with something simpler.
I'm trying to show multiple figures at once, but with an offset so I don't have to move the first figure to check that it showed all the figures (plots).
So here's an example:
from pylab import *
figure(0)
plot()
figure(1)
plot()
show()
These figures are shown on top of each other, but I want them to look like this when I run my program:
EDIT:
Any suggestions?
I usually do this with Figure.add_subplot:
fig = figure(0)
ax = fig.add_subplot(211)
ax.plot(...)
ax = fig.add_subplot(212)
ax.plot(...)
show()
If you're wondering what the magic 211 and 212 mean, see this question.
If you're using the tkagg backend, you can do:
import matplotlib.pyplot as plt
for i in range(5):
fig = plt.figure()
fig.canvas._tkcanvas.master.geometry('800x600+{:d}+{:d}'.format(70*i,70*i))
plt.show()
I think that the same treatment could be used for others backends...
Regards