Main figure legend outside of subplots - python-3.x

I have a number of subplots within a single figure. Each figure plots multiple lines that represent the same thing (represented by color) but in different situations (different subplots). I would like to create a legend at the base of the figure showing what the color of the line means. However, I running into a problem with getting the legend to not overlap the subplots and if I can adjust the axes, getting the legend to save.
I have tried a few different solutions with some help here but have been unable to adapt to subplots. Below is an example code that I am working with.
import numpy as np
import matplotlib.pyplot as plt
m1=1
m2=10
x=np.linspace(0,100,num=101,endpoint=True)
y1m1=m1*x**2
y2m1=m1*x**0.5
y1m2=m2*x**2
y2m2=m2*x**0.5
fig=plt.figure(figsize=(4,4))
ax1=fig.add_subplot(211)
ax1.plot(x,y1m1,'b',label=r'$x^2$')
ax1.plot(x,y2m1,'r',label=r'$\sqrt{x}$')
ax2=fig.add_subplot(212)
ax2.plot(x,y1m2,'b')
ax2.plot(x,y2m2,'r')
fig.legend(loc='lower center',ncol=2)
fig.tight_layout()
fig.savefig('examplefig.png',dpi=300)
plt.show()
My goal is to save the output to a png for a good figure.

This is one way of doing it using the suggestion provided here. The idea is to add the legend at position with respect to a given axis object. In your case, since you want to add the legend at the base, it is preferred you specify the position relative to ax2. Using ncol=2 is a matter of personal choice.
fig=plt.figure(figsize=(4,4))
ax1=fig.add_subplot(211)
l1, = ax1.plot(x,y1m1,'b')
l2, = ax1.plot(x,y2m1,'r')
ax2=fig.add_subplot(212)
ax2.plot(x,y1m2, 'b')
ax2.plot(x,y2m2, 'r')
ax2.legend(handles = [l1,l2] , labels=[r'$x^2$', r'$\sqrt{x}$'],
bbox_to_anchor=(0.7, -0.2), ncol=2)
fig.tight_layout()

Related

Unexpected plots on matplotlib histograms

I am quite a beginner with matplotlib so apologies if this seems like a dumb question.
I have a csv file with weight values for individual neurons in the different layers of my deep learning model. As I have four layers in my model, the file structure looks like this:
weight_1,weight_2......weight_n
weight_1,weight_2......weight_n
weight_1,weight_2......weight_n
weight_1,weight_2......weight_n
I want to extract the weights from each layer and generate the distributions out of it. I already have a code for it and it's working but for some epochs, the histograms have some weird colors which look like more histograms. I am attaching a sample image with the question.
As you can see, there is some pinkish part which is masked by the blue bulk of the histogram. Can someone please help me to understand what is that?
My code currently looks like this (assume that my file is loaded in the reader):
for row in csv_reader:
a = np.array(row)
a_float = a.astype(np.float)
plt.hist(a_float,bins=20)
plt.xlabel("weight_range")
plt.ylabel("frequency")
Please note that FOUR different plots (images) are generated after finishing the loop as the csv file has four rows. I have only posted the sample image for one of them. I didn't try to plot all the rows in one graph.
EDIT
I reduced the number of bins and now it's more prominent. I am attaching another sample image.
SOLVED
Adding plt.figure() inside the loop solved it. Please check the comments and answer below for the details. The updated loop should be as follows:
for row in csv_reader:
a = np.array(row)
a_float = a.astype(np.float)
plt.figure()
plt.hist(a_float,bins=20)
plt.xlabel("weight_range")
plt.ylabel("frequency")
plt.close()
I was trying to reproduce your error, and most likely you are plotting several histograms in one plot:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
arrays = np.array([np.random.random() for i in range(200)]).reshape(2, 100)
fig = plt.figure()
ax = fig.add_subplot(111)
for array in arrays:
ax.hist(array, bins = 20)

Python - Pdf file with several figures in one page (not with subplots !!)

I'm trying to produce a pdf file (with PdfFile) containing several figures in one page.
The most obvious solution is to use subplots. However, in my case this is not possible because each plot is produced by a different function. For example, there is a function def plotPDF(inputData) that will plot a probability distribution function (PDF), and another function def plotCDF(inputData) that will plot a cumulative distribution function (CDF). My code contains up to 20 different functions that will produce different plots when they are called.
What I want to do is to select some of these plots and produce a pdf file where they are contained in the same page. Following the example of PDF and CDF, I would like to produce a pdf file which contains one page where both plots are next to each other (in a similar way to the subplots).
I have tried to do this with subplots, but I cannot directly call the function within a subplot. That is, the following code wouldn't work:
fig, ax = plt.subplots(nrows=1, ncols=2)
plt.subplot(1, 2, 1)
plotPDF(inputData)
plt.subplot(1, 2, 2)
plotCDF(inputData)
plt.show()
Does anybody know how to solve this issue ? I need to proceed like this because I need the plot functions to be independent for other purposes. Making subplots would mean changing this structure, and it would make the code less versatile.
Thanks in advance !
I don't know if there's a way to do what you are asking, maybe someone else will know...
but
the recommended way to write a plotting function is to pass a reference to an Axes object to the function, and write the function to use that axes to do the plotting.
so in your case:
def plotPDF(data, ax=None):
ax = ax or plt.gca() # if no axes, use current axes
plt.sca(ax) # set axes as current axes (important if you are using the pyplot API)
# the rest of the function goes here
def plotCDF(data, ax=None):
ax = ax or plt.gca()
plt.sca(ax)
(...)
fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2)
plotPDF(inputData, ax=ax1)
plotCDF(inputData, ax=ax2)
plt.show()
Have you read this answer to a similar post? The answers in that post contain many ways to save an image into a pdf file, and only one (the matplotlib backend) requires subplots, as far as I know.
You can also save the files separately as png files, then use LaTeX / Word / another primitive way to arrange them into a pdf, which could be tedious.
Otherwise, could you maybe elaborate why using subplots wouldn't work with your functions? Maybe there is a way to use subplots, but then you'll need to show us the code.

How to retrieve the bounding box of a scalebar from a AnchoredSizeBar from Matplotlib

I am facing serious difficulties in retrieving the bounding box of a mpl_toolkits.axes_grid1.anchored_artists "AnchoredSizeBar" object.
So far, all I found was how to insert (create) an AnchoredSizeBar into my figure. But I can't retrieve its position. A relative position in respect to its given axes would also be welcome.
Ultimately, I would like to make a AnchoredSizeBar as Behnam asks in1. But in order to do so, I would need to create a series of AnchoredSizeBars all contiguous one to another in the figure. In order to do so, I would need a function to retrieve their relative positions and apply those into the creation of the other AnchoredSizeBars, creating finally a full scalebar.
This subject is related to other links presented below:
1) How to insert scale bar in a map in matplotlib
2) Add fill_bar argument to AnchoredSizeBar
Here is a snippet code of a desired API for AnchoredSizeBar object extent retrieval.
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar
fig, ax = plt.subplots(figsize=(3, 3))
bar0 = AnchoredSizeBar(ax.transData, 0.3, 'unfilled', loc=3, frameon=False,
size_vertical=0.05, fill_bar=False)
ax.add_artist(bar0)
bar0_extent = bar0.get_extent()
fig.show()
I thank you for your time, sincerely yours,
Philipe Leal
To get the extent you can pass in ax.figure.canvas.renderer where ax is the axes to which the object has been added to:
bar0_extent = bar0.get_extent(ax.figure.canvas.renderer)
But I second #ImportanceOfBeingErnest's suggestion for your particular use case. The doc-string for AnchoredSizeBar suggests that methods:
Docstring: An offset box placed according to the legend location
loc. AnchoredOffsetbox has a single child. When multiple children is
needed, use other OffsetBox class to enclose them. By default, the
offset box is anchored against its parent axes. You may explicitly
specify the bbox_to_anchor.
Hope this helps.

Matplotlib legend was not displayed properly

I am trying to plot the same geospatial data reading this tutorial:
https://www.datacamp.com/community/tutorials/geospatial-data-python
But the legend of her final graph has shwown wind speed in ranges. But when I used the same code mentioned on her tutorial, I could not produce the same legend. Any thing missing in her code? or What is wrong?
The categorical legend entries are provided via PySAL, and seem to need the scheme=<> and legend=True arguments. Perhaps these were set by default in previous versions of the libraries. For me, the following works ok:
fig, ax = plt.subplots(1, figsize=(20, 20))
base = country[country['NAME'].isin(['Alaska','Hawaii']) == False].plot(
ax=ax, color='#3B3C6E')
florence.plot(
ax=ax, column='Wind', marker="<", markersize=10,
cmap='cool', scheme="Quantiles", legend=True)
plt.axis('off')
Unfortunately the markers do not seem to be inherited into the legend, but the colors are the more salient difference anyway.
The geopandas.plot docs indicate 3 supported schemes: Quantiles, Equal_interval, fisher_jenks -- the first one seems to correspond to the datacamp example chart. See also the parameter k which defines the number of classes (5 is default as this example).

Adding a line to a Pandas plot

I'm currently tracking my internet speed and want to generate a plot of my measurements with a Timestamp, Upload value and Download value.
I'm using this to create the plot
df.plot(
kind='line',
x=timestamp_column_name,
y=[download_column_name, upload_column_name],
figsize=(12,5)
)
Generated plot:
Now I would like to add a line to this plot with the constant height of y=100000 but I can't figure out how to do this correctly. How should I do this with Pandas?
You can use axhline. Since df.plot() is a wrapper for matplotlib and returns the Matplotlib axes, which contain all the methods for interacting with the plot, it can be used straight forward as:
ax = df.plot( ... )
ax.axhline(y=100000)

Resources