I am trying to merge an arbitrary number of line charts into a single image, and while there are many, many questions about this sort of thing, none of them seem applicable to the code I'm working with.
Unlike a large number of answers, I don't want to have the separate graphs displayed side by side, or above one another, in a single output, but rather, combined together.
For all of these graphs the value of the "y_x" column would be the same, but the "yhat_y" produced during each loop would be different.
Adding subplots = True to the plot method of a dataframe seems to change the return type to something that is no longer compatible with the code numpy.ndarray' object has no attribute 'get_figure'
#ax = plt.subplot(111) doesnt seem to do anything
for variable in range(max_num):
forecast = get_forecast(variable)
cmp1 = forecast.set_index("ds")[["yhat", "yhat_lower", "yhat_upper"]].join(
both.set_index("ds")
)
e.augmented_error[variable]= sklearn.metrics.mean_absolute_error(
cmp["y"].values, cmp1["yhat"].values
)
cmp2=cmp.merge(cmp1,on='ds')
plot = cmp2[['y_x', 'yhat_y']].plot(title =e)
fig1 = plot.get_figure()
plot.set_title("prediction")
plt.show()
fig1.savefig('output.pdf', format="pdf")
plt.close()
The most straightforward way would be to create a reusable ax handle outside the loop, then call ax.plot inside the loop:
fig, ax = plt.subplots() # create reusable `fig` and `ax` handles
for variable in range(max_num):
...
ax.plot(cmp2['y_x'], cmp2['yhat_y']) # use `ax.plot(cmp2...)` instead of `cmp2.plot()`
ax.set_title('predictions')
fig.savefig('output.pdf', format='pdf')
Related
N.B.: I have edited the question as it was probably unclear: I am looking for the best method to understand the type of plot in a given axis.
QUESTION:
I am trying to make a generic function which can arrange multiple figures as subplots.
As I loop over the subplots to set some properties (e.g. axis range) iterating over fig.axes, I need to understand which type every plot is in order to determine which properties I want to set for each of them (e.g. I want to set x range on images and line plots, but not on colorbar, otherwise my plot will explode).
My question is then how I can distinguish between different types.
I tried to play with try and except and select on the basis of different properties for different plot types, but they seem to be the same for all of them, so, at the moment, the best way I found is to check the content of each axis: in particular ax.images is a non empty list if a plot is an image, and ax.lines is not empty if it is a line plot, (and a colorbar has both empty).
This works for simple plots, but I wonder if this is still the best way and still working for more complex cases (e.g. insets, overlapped lines and images, subclasses)?
This is just an example to illustrate how the different type of plots can be accessed, with the following code creating three axes l, i and cb (respectively line, image, colorbar):
# create test figure
plt.figure()
b = np.arange(12).reshape([4,3])
plt.subplot(121)
plt.plot([1,2,3],[4,5,6])
plt.subplot(122)
plt.imshow(b)
plt.colorbar()
# create test objects
ax=plt.gca()
fig=plt.gcf()
l,i,cb = fig.axes
# do a simple test, images are different:
for o in l,i,cb: print(len(o.images))
# this also doesn't work in finding properties not in common between lines and colobars, gives empty list.
[a for a in dir(l) if a not in dir(cb)]
After creating the image above in IPython
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable
fig, ax = plt.subplots()
ax.imshow(((0,1),(2,3)))
ax.scatter((0,1),(0,1), fc='w', ec='k')
ax.plot((0,1),(0,1))
fig.colorbar(ScalarMappable(), ax=ax)
plt.show()
I tried to investigate
In [48]: fig.axes
Out[48]: [<AxesSubplot:>, <AxesSubplot:label='<colorbar>'>]
I can recognize that one of the two axes is a colorbar — but it's easy to inspect the content of the individual axes
In [49]: fig.axes[0]._children
Out[49]:
[<matplotlib.image.AxesImage at 0x7fad9dda2b30>,
<matplotlib.collections.PathCollection at 0x7fad9dad04f0>,
<matplotlib.lines.Line2D at 0x7fad9dad09d0>]
In [50]: fig.axes[1]._children
Out[50]:
[<matplotlib.patches.Polygon at 0x7fad9db525f0>,
<matplotlib.collections.LineCollection at 0x7fad9db52830>,
<matplotlib.collections.QuadMesh at 0x7fad9dad2320>]
I have to remind you that
Matplotib provides you with many different container objects,
You can store the Axes destination in a list, or a dictionary, when you use it — you can even say ax.ax_type = 'lineplot'.
That said, e.g.,
from matplotlib.pyplot import subplots, plot
fig, ax = subplots()
plot((1, 2), (2, 1))
...
axes_types = []
for ax_i in fig.axes:
try:
ax_i.__getattr__('get_clabel')
axes_types.append('colorbar')
except AttributeError:
axes_types.append('lineplot')
...
In other word, chose a method that is unique to each one of the differnt types you're testing and check if it's available.
I'm trying to plot in a single image, multiple columns of a table.
The idea is to optimize the process with a loop.
It is important to note that all the columns share the same y-axis, and that the x scale varies for each column.
The Final result should look something like this:
I've already tried some things, but with no success, in my code I'm creating several figures, only plotting in the first graph:
def facies_plot_all(logs):
logs = sort_values(by='y')
ztop=logs.Y.min(); zbot=logs.Y.max()
for col in logs.columns:
numcol = (logs.shape[1])
f, ax = plt.subplots (nrows=1, ncols=numcol, figsize (20,25))
ax[x+1].plot(logs[col],logs.Y,'-')
I'm relatively new to programming and still searching for a way to solve this issue.
Any help will be welcome!
Put subplots outside of for loop:
logs = sort_values(by='y')
ztop=logs.Y.min(); zbot=logs.Y.max()
numcol = (logs.shape[1])
f, axes es= plt.subplots (nrows=1, ncols=numcol,
sharey=True,
figsize=(20,25))
for (ax, col) in zip(axes,logs.columns):
ax.plot(logs[col],logs.Y,'-')
I'm kind of new in coding and thus in python so this may sound quite dumb, but what are the main differences between .subplot() and .subplots() methods from matplotlib in python?
I didn't find this explanation anywhere else and after reading the documentation from https://matplotlib.org/ I inferred that with both methods you can create as many figures and plots as you want...so for me both of them seem to be quite the same thing and they just differ the way you can handle plots, axes, etc...or am I wrong?
Btw, I am using python3 in jupyter notebook if it makes any difference.
1. matplotlib.pyplot.subplots()
From the documentation page on matplotlib.pyplot.subplots():
This utility wrapper makes it convenient to create common layouts of subplots, including the enclosing figure object, in a single call.
That means you can use this single function to create a figure with several subplots with only one line of code. For example, the code below will return both fig which is the figure object, and axes which is a 2x3 array of axes objects which allows you to easily access each subplot:
fig, axes = plt.subplots(nrows=2, ncols=3)
2. matplotlib.pyplot.subplot()
In contrast, matplotlib.pyplot.subplot() creates only a single subplot axes at a specified grid position. This means it will require several lines of code to achieve the same result as matplot.pyplot.subplots() did in a single line of code above:
# first you have to make the figure
fig = plt.figure(1)
# now you have to create each subplot individually
ax1 = plt.subplot(231)
ax2 = plt.subplot(232)
ax3 = plt.subplot(233)
ax4 = plt.subplot(234)
ax5 = plt.subplot(235)
ax6 = plt.subplot(236)
or you can also use built-in method of fig:
ax1 = fig.add_subplot(231)
ax2 = fig.add_subplot(232)
ax3 = fig.add_subplot(233)
ax4 = fig.add_subplot(234)
ax5 = fig.add_subplot(235)
ax6 = fig.add_subplot(236)
Conclusion
The code above can be condensed with a loop, but it is still considerably more tedious to use. I'd therefore recommend you use matplotlib.pyplot.subplots() since it is more concise and easy to use.
I've started working more with figures and axes, and at first blush it seems to be really nice: an axes object can be independently created and manipulated (either by adding plots to it, or changing scale/etc), however the issue I'm running into is that it appears that "Figure" is the only class that can control layout of axes objects.
I would like to do something like this:
def plot_side_by_side(lefts, rights, coupled=True, width_ratios=[2,1]):
import matplotlib.gridspec as gridspec
# lefts and rights are lists of functions that
# take axes objects as keywords, the length of this
# object is the number of subplots we have:
plots = list(zip(lefts, rights))
y_size = len(plots)
# create figure with a number of subplots:
fig = plt.figure(figsize=(10,y_size * 4))
gs = gridspec.GridSpec(y_size,2,width_ratios=width_ratios,height_ratios=[1 for _ in plots])
#get axes on the left
cleft_axes = [plt.subplot(gs[0,0])]
if y_size > 1:
cleft_axes += [plt.subplot(gs[i,0], sharex=cleft_axes[0]) for i in range(1,y_size)]
[plt.setp(ax.get_xticklabels(), visible=False) for ax in cleft_axes[:-1]]
# get axes on the right, if coupled we fix the yaxes
# together, otherwise we don't
if coupled:
yaxes = cleft_axes
else:
yaxes = [None for _ in cleft_axes]
cright_axes = [plt.subplot(gs[0,1], sharey=yaxes[0])]
if y_size > 1:
cright_axes += [plt.subplot(gs[i,1], sharey=yaxes[i], sharex=cright_axes[0]) for i in range(1,y_size)]
[plt.setp(ax.get_xticklabels(), visible=False) for ax in cright_axes[:-1]]
# for each plot in our list, give it an axes object if it is on
# the left or right. Now this function will plot on that axes
for (pl, pr), l, r, name in zip(plots,cleft_axes,cright_axes,names):
pl(ax=l)
pr(ax=r)
return fig
And I would like to be able to create a function that takes a axes object as a keyword and puts two plots on it:
def twoplots(ax=ax):
# make a grid of axes, allow them to be plotted to, etc.
# this is all within the space given me by `ax`.
Is this possible? How would I go about doing such a thing? I know that I can get the figure from the axes object that is passed, is it possible to modify the parent gridspec without messing up every other gridspec?
Hope I'm not committing a foul reviving a thread this old. I wanted to give some extra context on what I think the OP is trying to do. (At least I hope it is what he's trying to do, because I'm trying to do the same thing.)
Suppose I have a statistical model that is composed of K submodels of different types. I want the submodels to plot themselves. Most of the time, in the typical case, each submodel will plot itself on an axes object. Occasionally, a submodel might need multiple axes to plot itself.
For example: suppose a model is a time series model, and the submodels are showing trend, seasonality, regression effects, holiday effects, etc. If the seasonal effect is showing annual seasonality it will plot itself just like the trend model (its effect vs time). But if it is showing day of week seasonality the plot vs time will be ineffective, because the lines will wiggle too fast. It would be more effective to plot the time series of Mondays, then the time series of Tuesdays, etc. To fit in with the larger scheme you want this cluster of 7 plots to be "the seasonality plot."
With K submodels you can often start off with
fig, ax = plt.submodels(K) and then pass ax[k] to the submodel as model.submodel[k].plot(ax[k]). The question is what to do when you'd like to plot the day-of-week seasonality effect described above on ax[k].
One answer might be "don't use this mechanism: use GridSpec or something else." But that's what I think the question is asking.
How can I use matplotlib to create many different chart objects and then have the ability to control each chart object separately (without affecting the other chart objects)?
Ideally, I'd like to have something of the following:
# creating the chart handler object
chartHandler = ChartHandler()
# plotting some values for chart #0
chartHandler[0].plot( range(0,100) )
# plotting some values for chart #5
chartHandler[5].plot( range(500,700) )
Unless you are talking about something that I haven't dealt with in matplotlib yet, I think that what you are looking for is figure.add_subplot(). You should be able to capture the return from each figure.add_subplot() and operate on each individually from then on, kind of like this:
import matplotlib.pyplot as plt
#Create an 11x5 figure
fig = plt.figure(figsize=(11,5))
#Create subplots[0]
subplts = []
subplt = fig.add_subplot(121)
subplts.append(subplt)
#Create subplots[1:20]
for xind in range(4,8):
for yind in range(0,5):
subplt = fig.add_subplot(5,8,(yind*8+xind))
subplts.append(subplt)
plt.show()
It should be noted that there are a few problems with the above script. Mainly, the subplots overlap slightly. This can be solved using the position keyword to add_subplot and some simple math.
In any case, you can now modify each subplot by referencing its index in subplots. It should be pretty simple to add plots, modify ranges, etc.