Matplotlib: Grid lines behind bars on twinned axes? - python

I'm plotting two series on one chart using twiny() and want the grid lines behind the bars. However, by default the behaviour is mixed: in front of elements of the original axes and behind the twin's.
Neither axes.set_axisbelow(True) nor zorder=0 with the grid call fixes this. Is this a bug in matplotlib? Anyone know of a work around?
Here's an example to illustrate:
import matplotlib, matplotlib.pyplot as plt
print('matplotlib: {}'.format(matplotlib.__version__))
series1 = [1.25,2.25,3]
series2 = [12,9.75,6.75]
categories = ['A','B','C']
fig, axes1 = plt.subplots(nrows=1,ncols=1)
axes2 = axes1.twiny()
axes1.barh(categories, series1, color='red', align='edge', height=-0.4)
axes2.barh(categories, series2, color='blue', align='edge', height=+0.4)
axes1.set_axisbelow(True)
axes2.set_axisbelow(True)
axes1.grid(axis='x')
axes2.grid(axis='x')
plt.show()
matplotlib v. 3.2.2

Everything drawn on one axis (including the grid) is always or completely before or completely behind everything drawn on the other axis. The only solution in your case is to only draw a grid for axes1, removing the call to the grid for the other axis.

Related

How to change the positions of subplot titles and axis labels in Seaborn FacetGrid?

I am trying to plot a polar plot using Seaborn's facetGrid, similar to what is detailed on seaborn's gallery
I am using the following code:
sns.set(context='notebook', style='darkgrid', palette='deep', font='sans-serif', font_scale=1.25)
# Set up a grid of axes with a polar projection
g = sns.FacetGrid(df_total, col="Construct", hue="Run", col_wrap=5, subplot_kws=dict(projection='polar'), size=5, sharex=False, sharey=False, despine=False)
# Draw a scatterplot onto each axes in the grid
g.map(plt.plot, 'Rad', ''y axis label', marker=".", ms=3, ls='None').set_titles("{col_name}")
plt.savefig('./image.pdf')
Which with my data gives the following:
I want to keep this organisation of 5 plots per line.
The problem is that the title of each subplot overlap with the values of the ticks, same for the y axis label.
Is there a way to prevent this behaviour? Can I somehow shift the titles slightly above their current position and can I shift the y axis labels slightly on the left of their current position?
Many thanks in advance!
EDIT:
This is not a duplicate of this SO as the problem was that the title of one subplot overlapped with the axis label of another subplot.
Here my problem is that the title of one subplot overlaps with the ticks label of the same subplot and similarly the axis label overlaps with the ticks label of the same subplot.
I also would like to add that I do not care that they overlap on my jupyter notebook (as it as been created with it), however I want the final saved image with no overlap, so perhaps there is something I need to do to save the image in a slightly different format to avoid that, but I don't know what (I am only using plt.savefig to save it).
EDIT 2: If someone would like to reproduce the problem here is a minimal example:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
sns.set()
sns.set(context='notebook', style='darkgrid', palette='deep', font='sans-serif', font_scale=1.5)
# Generate an example radial datast
r = np.linspace(0, 10000, num=100)
df = pd.DataFrame({'label': r, 'slow': r, 'medium-slow': 1 * r, 'medium': 2 * r, 'medium-fast': 3 * r, 'fast': 4 * r})
# Convert the dataframe to long-form or "tidy" format
df = pd.melt(df, id_vars=['label'], var_name='speed', value_name='theta')
# Set up a grid of axes with a polar projection
g = sns.FacetGrid(df, col="speed", hue="speed",
subplot_kws=dict(projection='polar'), size=4.5, col_wrap=5,
sharex=False, sharey=False, despine=False)
# Draw a scatterplot onto each axes in the grid
g.map(plt.scatter, "theta", "label")
plt.savefig('./image.png')
plt.show()
Which gives the following image in which the titles are not as bad as in my original problem (but still some overlap) and the label on the left hand side overlap completely.
In order to move the title a bit higher you can set at new position,
ax.title.set_position([.5, 1.1])
In order to move the ylabel a little further left, you can add some padding
ax.yaxis.labelpad = 25
To do this for the axes of the facetgrid, you'd do:
for ax in g.axes:
ax.title.set_position([.5, 1.1])
ax.yaxis.labelpad = 25
The answer provided by ImportanceOfBeingErnest in this SO question may help.

Gridlines that overlap with axes spines have different width from other gridlines

I'm using Seaborn to make some plots using the whitegrid style. After calling despine(), I'm seeing that the gridlines that would overlap with the axes spines have smaller linewidth than the other gridlines. But it seems like this only happens when I save the plots as pdf. I'm sharing
three different figures with different despine configurations that show the effect.
Does anyone know why this occurs? And is there a simple fix?
PDF plot with all spines
PDF plot that despines all axes
PDF plot that despines left, top, and right axes
Code:
splot = sns.boxplot(data=df, palette=color, whis=np.inf, width=0.5, linewidth = 0.5)
splot.set_ylabel('Normalized WS')
plt.xticks(rotation=90)
plt.tight_layout()
sns.despine(left=True, bottom=True)
plt.savefig('test.pdf', bbox_inches='tight')
Essentially what's happening here is that the grid lines are centered on the tick position, so the outer half of the extreme grid lines are not drawn because they extend past the limits of the axes.
One approach is to disable clipping for the grid lines:
import numpy as np
import seaborn as sns
sns.set(style="whitegrid", rc={"grid.linewidth": 5})
x = np.random.randn(100, 6)
ax = sns.boxplot(data=x)
ax.yaxis.grid(True, clip_on=False)
sns.despine(left=True)
My hacking solution now is to not despine the top and bottom axes and make them the same width as the gridlines. This is not ideal. If someone can point out a way to fix the root cause, I will really appreciate that.

Matplotlib ylim and xlim not working in combination of scatter plot and line plot or fill_between

I encounter a plotting issue I don't understand. Below code shall draw a straight line, fill the area above the line with a colour and plot several scattered dots in it. That all works but if I combine scatter and any of line or fill_between I cannot set the plot limits. The plot area is much larger than it had to be.
So how do I set the plot limits?
from matplotlib import pyplot as plt
import numpy as np
x = np.linspace(0,160,100)
MCSample = np.random.normal(112,10,1000)
YSample = np.random.normal(100,2.41,1000)
y_limit = max(160, np.max(YSample))
fig, ax = plt.subplots(1, 1)
ax.plot(x,x, label="Limit State function")
ax.scatter(MCSample,YSample, marker='.', color='b', alpha=0.5)
ax.fill_between(x,y_limit,x, alpha=0.1, color='r')
ax.set_xlim=(0,160)
ax.set_ylim=(0,y_limit)
plt.show()
I'm using Python 3.5.1 and Matplotlib 1.5.1.
In your code you are setting ax.set_xlim to equal (0,160).
All you have to do to make your code work is to get rid of the equal signs as shown below:
ax.set_xlim(0,160)
ax.set_ylim(0,y_limit) # no equals sign on these 2 lines
Now you are applying those limits to the graph rather than defining them to equal the limits.

inset imshow within figure matplotlib

I'm trying to have an imshow plot inset into another in matplotlib.
I have a figure that has an plot on top and an imshow on the bottom:
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
plt.plot()
ax2 = fig.add_subplot(2,1,2)
plt.imshow()
and then I have another figure, which also is comprised of a plot on top and an imshow below. I want this second figure to be inset into the top plot on the first figure.
I tried follow this example. The code ran without an error but I had an empty axis in the position I wanted it.
My problem is just that I'm not sure where to put the plt.setp() command If that's what I am supposed to use.
First, I don't think you can put a figure into a figure in matplotlib. You will have to arrange your Axes objects (subplots) to achieve the look you want.
The example you provided uses absolute positioning to do that. setp there is not related to positioning, though — it just removes axis ticks from insets. An example of code that does what you want:
import numpy
import matplotlib.pyplot as plt
x = numpy.linspace(0, 1)
xx, yy = numpy.meshgrid(x, x)
im = numpy.sin(xx) + numpy.cos(yy)**2
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1)
ax1.plot(x, x**2)
ax2 = fig.add_subplot(2,1,2)
ax2.imshow(im)
inset1 = fig.add_axes([.15, .72, .15, .15])
inset1.plot(x, x**2)
plt.setp(inset1, xticks=[], yticks=[])
inset2 = fig.add_axes([0.15, 0.55, .15, .15])
inset2.imshow(im)
plt.setp(inset2, xticks=[], yticks=[])
fig.savefig('tt.png')
Here the insets use explicit positioning with coordinates given in "figure units" (the whole figure has size 1 by 1).
Now, of course, there's plenty of room for improvement. You probably want the widths of your plots to be equal, so you'll have to:
specify the positioning of all subplots explicitly; or
play with aspect ratios; or
use two GridSpec objects (this way you'll have the least amount of magic numbers and manual adjustment)

autofmt_xdate deletes x-axis labels of all subplots

I use autofmt_xdate to plot long x-axis labels in a readable way. The problem is, when I want to combine different subplots, the x-axis labeling of the other subplots disappears, which I do not appreciate for the leftmost subplot in the figure below (two rows high). Is there a way to prevent autofmt_xdate from quenching the other x-axis labels? Or is there another way to rotate the labels? As you can see I experimented with xticks and "rotate" as well, but the results were not satisfying because the labels were rotated around their center, which resulted in messy labeling.
Script that produces plot below:
from matplotlib import pyplot as plt
from numpy import arange
import numpy
from matplotlib import rc
rc("figure",figsize=(15,10))
#rc('figure.subplot',bottom=0.1,hspace=0.1)
rc("legend",fontsize=16)
fig = plt.figure()
Test_Data = numpy.random.normal(size=20)
fig = plt.figure()
Dimension = (2,3)
plt.subplot2grid(Dimension, (0,0),rowspan=2)
plt.plot(Test_Data)
plt.subplot2grid(Dimension, (0,1),colspan=2)
for i,j in zip(Test_Data,arange(len(Test_Data))):
plt.bar(i,j)
plt.legend(arange(len(Test_Data)))
plt.subplot2grid(Dimension, (1,1),colspan=2)
xticks = [r"%s (%i)" % (a,b) for a,b in zip(Test_Data,Test_Data)]
plt.xticks(arange(len(Test_Data)),xticks)
fig.autofmt_xdate()
plt.ylabel(r'$Some Latex Formula/Divided by some Latex Formula$',fontsize=14)
plt.plot(Test_Data)
#plt.setp(plt.xticks()[1],rotation=30)
plt.tight_layout()
#plt.show()
This is actually a feature of the autofmt_xdate method. From the documentation of the autofmt_xdate method:
Date ticklabels often overlap, so it is useful to rotate them and right align them. Also, a common use case is a number of subplots with shared xaxes where the x-axis is date data. The ticklabels are often long, and it helps to rotate them on the bottom subplot and turn them off on other subplots, as well as turn off xlabels.
If you want to rotate the xticklabels of the bottom right subplot only, use
plt.setp(plt.xticks()[1], rotation=30, ha='right') # ha is the same as horizontalalignment
This rotates the ticklabels 30 degrees and right aligns them (same result as when using autofmt_xdate) for the bottom right subplot, leaving the two other subplots unchanged.

Categories