This question already has an answer here:
Embed matplotlib figure in larger figure
(1 answer)
Closed 8 years ago.
How can I use a matplotlib Figure object as a subplot? Specifically, I have a function that creates a matplotlib Figure object, and I would like to include this as a subplot in another Figure.
In short, here's stripped-down pseudocode for what I've tried:
fig1 = plt.figure(1, facecolor='white')
figa = mySeparatePlottingFunc(...)
figb = mySeparatePlottingFunc(...)
figc = mySeparatePlottingFunc(...)
figd = mySeparatePlottingFunc(...)
fig1.add_subplot(411, figure=figa)
fig1.add_subplot(412, figure=figb)
fig1.add_subplot(413, figure=figc)
fig1.add_subplot(414, figure=figd)
fig1.show()
Sadly, however, this fails. I know for a fact the individual plots returned from the function invocations are viable--I did a figa.show(),...,figd.show() to confirm that they are OK. What I get for the final line in the above code block--fig1.show()--is
a collection of four empty plots that have frames and x- and y- tickmarks/labels.
I've done quite a bit of googling around, and experimented extensively, but it's clear that I've missed something that is either really subtle, or embarrassingly obvious (I'll be happy for it to be the latter as long as I can get un-stuck).
Thanks for any advice you can offer!
You can't put a figure in a figure.
You should modify your plotting functions to take axes objects as an argument.
I am also unclear why the kwarg figure is there, I think it is an artifact of the way that inheritance works, the way that the documentation is auto-generated, and the way some of the getter/setter work is automated. If you note, it says figure is undocumented in the Figure documentation, so it might not do what you want;). If you dig down a bit, what that kwarg really controls is the figure that the created axes is attached too, which is not what you want.
In general, moving existing axes/artists between figures is not easy, there are too many bits of internal plumbing that need to be re-connected. I think it can be done, but will involving touching the internals and there is no guarantee that it will work with future versions or that you will get warning if the internals change in a way that will break it.
You need to your plotting functions to take an Axes object as argument. You can use a pattern like:
def myPlotting(..., ax=None):
if ax is None:
# your existing figure generating code
ax = gca()
so if you pass in an Axes object it gets drawn to (the new functionality you need), but if you don't all of your old code will work as expected.
Related
I'm learning some data science related topics and oh boy, this is a jungle of different libraries for everything π
Because of things, I went with Lets-plot, which has a nice Kotlin API that I'm using combined with Kotlin kernel for Jupyter notebooks
Overall, things are going pretty good. Most tutorials & docs I see online use different libraries for plotting (e.g. Seaborn, Matplotlib, Plotly) so most of the time I have to do some reading of the Lets-Plot-Kotlin reference and try/error until I find the equivalent code for my graphs
Currently, I'm trying to graph the distribution of differences between two values. Overall, this looks pretty good. I can just do something like
(letsPlot(df)
+ geomHistogram { x = "some-column" }
).show()
which gives a nice graph
It would be interesting to see the density estimator as well, geomDensity to the rescue!
(letsPlot(df)
+ geomDensity(color = "red") { x = "some-column" }
).show()
Nice! Now let's watch them both together
(letsPlot(df)
+ geomDensity(color = "red") { x = "some-column" }
+ geomHistogram() { x = "some-column" }
).show()
As you can see, there's a small red line in the bottom (the geomDensity!). Problem here (I would say) is that both layers are using the same Y scale. Histogram is working with 0-20 values and density with 0-0.02 so when plotted together it's just a line at the bottom
Is there any way to add several layers in the same plot that use their own scale? I've read some blogposts that claim that you should not go for it (seems to be pretty much accepted by the community.
My target is to achieve something similar to what you can do with Seaborn by doing
plt.figure(figsize=(10,4),dpi=200)
sns.histplot(data=df,x='some_column',kde=True,bins=25)
(yes I know I took the lets plot screenshot without the bins configured. Not relevant, I'd say Β―_(γ)_/Β― )
Maybe I'm just approaching the problem with a mindset I should not? As mentioned, I'm still learning so every alternative will be highly welcomed π
Just, please, don't go with the "Switch to Python". I'm exploring and I'd prefer to go one topic at a time
In order for histogram and density layers to share the same y-scale you need to map variable "..density.." to aesthetic "y" in the histogram layer (by default histogram maps "..count.." to "y").
You will find an example of it in cell [4] in this notebook: https://nbviewer.org/github/JetBrains/lets-plot-kotlin/blob/master/docs/examples/jupyter-notebooks/distributions.ipynb
BWT, many of the pages in Lets-Plot Kotlin API Reference are equipped with links on demo-notebooks, in "Examples" section: geomHistogram().
And of course you can find a lot of info online on the R ggplot2 package which is largely applicable to Lets-Plot as well. For example: Histogram with kernel density estimation.
Finally :) , calling show() is not necessary - Jupyter Kotlin kernel will render plot automatically if plot expression is the last one in the cell which is often the case.
This question already has answers here:
x-axis inverted unexpectedly by pandas.plot(...)
(2 answers)
Closed 4 years ago.
When I was plotting two series of data against eachother, the X axis was inverted unexpectedly. I know this question sounds pretty similar to this other: x-axis inverted unexpectedly by pandas.plot(...) and it actually is, but I want to know if this can be disabled or something, not a workaround. Let me explain myself.
I have a very simple DF that consists on a datetime index and two columns; one has humidity measurements and the other daily weights. Both of them are in descending order because when my sample loses water, it also loses weight and humidity. So my DF looks something like this, where my data is in descending order
But then, when I plot using X = "Peso" (weight), and Y = 'Humedad' (humidity), my X axis goes in ascending order insted of descending order.
My ploting code:
plt.figure(figsize=(12,9))
plt.scatter(data['Peso'],data['Humedad'])
plt.xlabel('Peso (kg)',fontsize=14)
plt.ylabel("Raw Counts",fontsize=14)
plt.xticks(rotation=90,fontsize=10)
plt.grid()
Resulting in this kind of plot, where X axis is inverted
So, I could do two simple types of workaround:
plt.scatter(sorted(data['Peso']),data['Humedad'])
or
plt.scatter(data['Peso'][::-1],data['Humedad'])
Both of them have the same result, they print my data as I wanted, BUT my xticks are still inverted:
So what I did was creating a list with my weight values in order to insert it as it follows:
semin=data['Peso']
semin=semin.tolist()
And then adding it to my plt.xticks like this
plt.xticks(semin,rotation=90,fontsize=10)
It "kind off" worked, overlaping some of the xticks as you can see in the image below:
I know I can solve this with [Locs] and general xticks information, but I really wanted to know if it's possible to just ask Pandas to follow the natural data descending order or anything similiar and avoiding all of this xticks stuff?
I've checked this too: https://github.com/pandas-dev/pandas/issues/10118
and I tried by doing the set_index suggestion:
plt.figure(figsize=(12,9))
data.set_index('Peso').Humedad.plot()
plt.xlabel('Peso (kg)',fontsize=14)
plt.ylabel("Raw Counts",fontsize=14)
plt.xticks(rotation=90,fontsize=10)
plt.grid()
And it went almost perfect, except that I needed it in scatter...
So I tried some stuff to "scatter it"
1. Putting the marker type:
data.set_index('Peso').Humedad.plot(marker='o')
Got a marker + line graph:
2. Changing .plot for .scatter to the plot:
data.set_index('Peso').Humedad.scatter()
Got this error:
AttributeError: 'Series' object has no attribute 'scatter'
3. Using both
data.set_index('Peso').Humedad.plot.scatter()
Got this one:
AttributeError: 'SeriesPlotMethods' object has no attribute 'scatter'
4. Making this giant question. Please help.
And that's all, sorry if I'm missing something or if my post is too long. I'm open to suggestions, corrections or anything you're willing to tell me.
Thanks!
Oh I just saw that the linked question actually does exactly what you need. Will leave this as here. But please refer to the linked question instead.
It's not a solution to change the ticks! The resultung plot may easily get completely wrong.
Instead google for "invert x axis" or so and find that you can invert the axis via
ax = df.plot(...)
ax.invert_xaxis()
This is not a workaround. It is the solution. (How much easier can it get?)
I'm wanting to mimic the figure extents observed in an output figure and apply them to the figure object itself. The output figure command I want to copy is:
plt.savefig(flname, bbox_inches='tight', pad_inches=0.03)
I've been able to grab the bounding box which generates the observed bbox in the figure using:
bbox = fig.get_tightbbox(fig.canvas.get_renderer())
but am lost as to how to apply that to the fig object!
If you go here:
http://matplotlib.org/api/figure_api.html
And look under the Figure class constructor you will find that in add_axes() and gca() there is a way to set the bbox using one of the kwargs, clip_box.
Additionally here is more information about the bbox.
http://stackoverflow.com/questions/29809238/definition-of-matplotlib-pyplot-axes-bbox
I hope this helps you like it did for me. In short, you cannot apply it to a figure, but you can seem to apply it to all axes.
If I read this question correctly, it hasn't quite been answered by dozens of answers on this general topic elsewhere on stack overflow. The overwhelming focus there actually is on savefig output, rather than on the figure handle in memory. My application was also on the figure handle, and it worked easily enough like this:
# make figure. w,h and dpi optional but allow for exact figure sizing
hf = plt.plot(x,y,figsize=(w,h),dpi=myscreendpi)
# adjust padding as required
hf.set_tight_layout({'pad':0.5})
tightbbox = hf.get_tightbbox(hf.canvas.get_renderer())
# just set it directly to figure size
hf.set(figheight=tightbbox.height,figwidth=tightbbox.width)
In an answer to an earlier question of mine regarding fixing the colorspace for scatter images of 4D data, Tom10 suggested plotting values as symbols in order to double-check my data. An excellent idea. I've run some similar demos in the past, but I can't for the life of me find the demo I remember being quite simple.
So, what's the easiest way to plot numerical values as the symbol in a scatter plot instead of 'o' for example? Tom10 suggested plt.txt(x,y,value)- and that is the implementation used in a number of examples. I however wonder if there's an easy way to evaluate "value" from my array of numbers? Can one simply say: str(valuearray) ?
Do you need a loop to evaluate the values for plotting as suggested in the matplotlib demo section for 3D text scatter plots?
Their example produces:
(source: sourceforge.net)
However, they're doing something fairly complex in evaluating the locations as well as changing text direction based on data. So, is there a cute way to plot x,y,C data (where C is a value often taken as the color in the plot data- but instead I wish to make the symbol)?
Again, I think we have a fair answer to this- I just wonder if there's an easier way?
The easiest way I've seen to do this is:
for x, y, val in zip(x_array, y_array, val_array):
plt.text(x, y, val)
Also, btw, you suggested using str(valarray), and this, as you may have noticed doesn't work. To convert an array of numbers to a sequence of strings you could use
valarray.astype(str)
to get a numpy array, or,
[str(v) for v in valarray]
to get a Python list. But even with valarray as a proper sequence of strings, plt.text won't iterate over it's inputs.
In GNU Octave you can make a picture where different colors represent different values in a matrix. You can also add a colorbar, which shows what color corresponds to what value.
Is it possible to somehow add units to the values shown in the colorbar? Instead of saying β0.36β it would say β0.36 V/nmβ? I know this is possible in Matlab, but I canβt figure out how to do it in Octave. Any good workarounds?
I assume someone here will mention that I should use matplotlib instead (that usually happens). How would you accomplish the same thing with that?
The matplotlib answer (using pylab) is
imshow(random((20,20)))
colorbar(format='%.2f V/nm')
In Octave it seems that the following works (but I'm no Octave expert so maybe there's a better way):
c=colorbar();
labels = {};
for v=get(c,'ytick'), labels{end+1} = sprintf('%.2f V/nm',v); end
set(c,'yticklabel',labels);