For visual effect purpose, I wish I could remove the grids outside the circle and only keep those are within the circle.
Btw, how to fulfill the cell ([8,9],[9,10]) with red color, I mean, the cell on the right of x=8 and down y=9.
My code is below and current image is also attached.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.transforms import BlendedGenericTransform
fig, ax = plt.subplots()
ax.text(0, -0.02, 'y', transform=BlendedGenericTransform(ax.transData, ax.transAxes), ha='center')
ax.text(1.01, 0, 'x', transform=BlendedGenericTransform(ax.transAxes, ax.transData), va='center')
ax.set_xticks(np.arange(0,side+1,1))
ax.set_yticks(np.arange(0,side+1,1))
plt.grid()
ax.xaxis.tick_top()
plt.gca().invert_yaxis()
circle = plt.Circle((15, 15), radius=15, fc='w')
plt.gca().add_patch(circle)
fig.set_size_inches(18.5, 10.5)
The trick is to set the clip_path property on the gridline artists
Here's a simplified (minimal) example:
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
# draw the circle
circle = plt.Circle((15, 15), radius=15, fc='w')
ax.add_patch(circle)
# settings for the axes
ax.grid()
ax.set_xlim(0,30)
ax.set_ylim(0,30)
ax.set_aspect(1)
# clip the gridlines
plt.setp(ax.xaxis.get_gridlines(), clip_path=circle)
plt.setp(ax.yaxis.get_gridlines(), clip_path=circle)
plt.show()
Result:
Related
I have a matplotlib plot with a colorbar attached. I want to position the colorbar so that it is horizontal, and underneath my plot.
I have almost done this via the following:
plt.colorbar(orientation="horizontal",fraction=0.07,anchor=(1.0,0.0))
But the colorbar is still overlapping with the plot slightly (and the labels of the x axis). I want to move the colorbar further down, but I can't figure out how to do it.
using padding pad
In order to move the colorbar relative to the subplot, one may use the pad argument to fig.colorbar.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)
fig, ax = plt.subplots(figsize=(4,4))
im = ax.imshow(np.random.rand(11,16))
ax.set_xlabel("x label")
fig.colorbar(im, orientation="horizontal", pad=0.2)
plt.show()
using an axes divider
One can use an instance of make_axes_locatable to divide the axes and create a new axes which is perfectly aligned to the image plot. Again, the pad argument would allow to set the space between the two axes.
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np; np.random.seed(1)
fig, ax = plt.subplots(figsize=(4,4))
im = ax.imshow(np.random.rand(11,16))
ax.set_xlabel("x label")
divider = make_axes_locatable(ax)
cax = divider.new_vertical(size="5%", pad=0.7, pack_start=True)
fig.add_axes(cax)
fig.colorbar(im, cax=cax, orientation="horizontal")
plt.show()
using subplots
One can directly create two rows of subplots, one for the image and one for the colorbar. Then, setting the height_ratios as gridspec_kw={"height_ratios":[1, 0.05]} in the figure creation, makes one of the subplots much smaller in height than the other and this small subplot can host the colorbar.
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)
fig, (ax, cax) = plt.subplots(nrows=2,figsize=(4,4),
gridspec_kw={"height_ratios":[1, 0.05]})
im = ax.imshow(np.random.rand(11,16))
ax.set_xlabel("x label")
fig.colorbar(im, cax=cax, orientation="horizontal")
plt.show()
Edit: Updated for matplotlib version >= 3.
Three great ways to do this have already been shared in this answer.
The matplotlib documentation advises to use inset_locator. This would work as follows:
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
import numpy as np
rng = np.random.default_rng(1)
fig, ax = plt.subplots(figsize=(4,4))
im = ax.imshow(rng.random((11, 16)))
ax.set_xlabel("x label")
axins = inset_axes(ax,
width="100%",
height="5%",
loc='lower center',
borderpad=-5
)
fig.colorbar(im, cax=axins, orientation="horizontal")
import numpy as np
import seaborn as sns
from matplotlib.patches import Patch
from matplotlib.lines import Line2D
data_box = np.random.random((10, 3))
data_line = np.random.random(3)
ax = sns.boxplot(data=data_box, color='red', saturation=0.5)
sns.lineplot(data=data_line, color='blue')
legend_elements = [Line2D([0], [0], color='blue', lw=4, label='box'),
Patch(facecolor='red', edgecolor='grey', linewidth=1.5,
label='line')]
ax.legend(handles=legend_elements, fontsize='xx-large')
I overlay a lineplot to a boxplot as in the image above, and draw the legend manually using matplotlib.
But seaborn sets the saturation of the colors, whose default value is 0.75 (I set it to 0.5 make the difference clear). So the legend color generated by matplotlib is not accurate. Is there any way to change the saturation of the matplotlib legend? Or how can I draw legend color accurately, except setting saturation=1.
Use seaborn's desaturate function
import numpy as np
import seaborn as sns
from matplotlib.patches import Patch
from matplotlib.lines import Line2D
data_box = np.random.random((10, 3))
data_line = np.random.random(3)
fig, ax = plt.subplots()
ax = sns.boxplot(data=data_box, color='red', saturation=0.5)
sns.lineplot(data=data_line, color='blue')
legend_elements = [Line2D([0], [0], color='blue', lw=4, label='box'),
Patch(facecolor=sns.desaturate('red',0.5), edgecolor='grey', linewidth=1.5,
label='line')]
ax.legend(handles=legend_elements, fontsize='xx-large')
I'm using matplotlib to produce a plot where I want to show labels on the right and left y-axis. You will notice by running the code that the grid-lines formed by the right-side y-axis appear on top of the plot line, where the left-side lines appear below. I would like them all to appear below the plot. I've tried zorder and set_axisbelow(True) without success.
Example code below:
import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
t = np.linspace(0,5)
x = np.exp(-t)*np.sin(2*t)
fig, ax1 = plt.subplots()
ax1.plot(t, x)
ax2 = ax1.twinx()
ax2.plot(t, x, alpha=0.0)
ax1.set_xticks([0,1,2])
ax1.set_yticks([0.1, 0.2])
ax2.set_yticks([0.3, 0.4, 0.5])
ax1.grid(True, color='lightgray')
ax2.grid(True, color='lightgray')
for a in [ax1, ax2]:
a.spines["top"].set_visible(False)
a.spines["right"].set_visible(False)
a.spines["left"].set_visible(False)
a.spines["bottom"].set_visible(False)
ax1.set_axisbelow(True)
ax2.set_axisbelow(True)
plt.savefig('fig.pdf')
plt.show()
import matplotlib
import numpy as np
from matplotlib.patches import Circle
import matplotlib.pyplot as plt
matplotlib.rcParams["figure.figsize"]=(6.4, 4.8)
fig, ax = plt.subplots()
circle1 = Circle((0.1, 0.1), 0.2, facecolor = "k", edgecolor = 'red', linewidth = 30)
circle2 = Circle((0.5, 0.5), 0.2, facecolor = "k")
ax.axis("equal")
ax.add_artist(circle1);
ax.add_artist(circle2);
plt.show()
When I run the above code, which tries to draw 2 circles, the patches overflow outside the visible area. How can I fit both circles into view ?
First of all, to add a patch to an axes, use ax.add_patch().
Then to make sure the axes is scaled according to its content, use ax.autoscale()
ax.add_artist(circle1)
ax.add_artist(circle2)
ax.autoscale()
I am trying to plot a rectangle onto the legend in matplotlib.
To illustrate how far I have gotten I show my best attempt, which does NOT work:
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import numpy as np
Fig = plt.figure()
ax = plt.subplot(111)
t = np.arange(0.01, 10.0, 0.01)
s1 = np.exp(t)
ax.plot(t, s1, 'b-', label = 'dots')
leg = ax.legend()
rectangle = Rectangle((leg.get_frame().get_x(),
leg.get_frame().get_y()),
leg.get_frame().get_width(),
leg.get_frame().get_height(),
fc = 'red'
)
ax.add_patch(rectangle)
plt.show()
The rectangle just isn't draw anywhere in the figure.
If I look at the values of leg.get_frame().get_x(), leg.get_frame().get_y()), leg.get_frame().get_width() and leg.get_frame().get_height(), I see that they are
0.0, 0.0, 1.0 and 1.0 respectively.
My problem thus sees to be, to find the co-ordinates of the frame of the legend.
It would be really great if you could help me out.
Thanks for reading this far.
This link may have the exact thing you are looking for.
http://matplotlib.org/users/legend_guide.html#creating-artists-specifically-for-adding-to-the-legend-aka-proxy-artists
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
red_patch = mpatches.Patch(color='red', label='The red data')
plt.legend(handles=[red_patch])
plt.show()
The trouble is that the position of the legend is not known in advance. Only by the time you render the figure (calling plot()), is the position decided.
A solution I came across is to draw the figure twice. In addition, I've used axes coordinates (default is data coordinates) and scaled the rectangle so you still see a bit of the legend behind it. Note that I had to set the legend and rectangle zorder as well; the legend gets drawn later than the rectangle and thus the rectangle otherwise disappears behind the legend.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
Fig = plt.figure()
ax = plt.subplot(111)
t = np.arange(0.01, 10.0, 0.01)
s1 = np.exp(t)
ax.plot(t, s1, 'b-', label = 'dots')
leg = ax.legend()
leg.set_zorder(1)
plt.draw() # legend position is now known
bbox = leg.legendPatch.get_bbox().inverse_transformed(ax.transAxes)
rectangle = Rectangle((bbox.x0, bbox.y0),
bbox.width*0.8, bbox.height*0.8,
fc='red', transform=ax.transAxes, zorder=2)
ax.add_patch(rectangle)
plt.show()