How to make trapezoid and parallelogram in python using matplotlib - python

I have tried the following to produce a regular polygon:
import matplotlib.pyplot as plt
import matplotlib.patches as patches
fig2 = plt.figure()
ax2 = fig2.add_subplot(111, aspect='equal')
ax2.add_patch(
patches.RegularPolygon(
(0.5, 0.5),
3,
0.2,
fill=False # remove background
)
)
fig2.savefig('reg-polygon.png', dpi=90, bbox_inches='tight')
plt.show()
While this produces a triangle, I haven't found any way to produce a trapezoid and and a parallelogram.
Are there any commands to do this? Or can I transform the regular polygon into one of the other shapes?

You would need to use a matplotlib.patches.Polygon and define the corners yourself.
import matplotlib.pyplot as plt
import matplotlib.patches as patches
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
# Parallelogram
x = [0.3,0.6,.7,.4]
y = [0.4,0.4,0.6,0.6]
ax.add_patch(patches.Polygon(xy=list(zip(x,y)), fill=False))
# Trapez
x = [0.3,0.6,.5,.4]
y = [0.7,0.7,0.9,0.9]
ax.add_patch(patches.Polygon(xy=list(zip(x,y)), fill=False))
plt.show()
For filled patches with size greater than 1 x 1
fig = plt.figure()
ax = fig.add_subplot(111, aspect='equal')
ax.set_xlim(0, 3)
ax.set_ylim(0, 3)
x = [0, 1.16, 2.74, 2, 0]
y = [0, 2.8, 2.8, 0, 0]
ax.add_patch(patches.Polygon(xy=list(zip(x,y)), fill=True))
x = [0.3,0.6,.5,.4]
y = [0.7,0.7,0.9,0.9]
ax.add_patch(patches.Polygon(xy=list(zip(x,y)), fill=True, color='magenta'))

One simple way to do it is creating a list of lists as the end points of the polygon( parallelogram/trapezoid) and plotting(or rather tracing) them.
import matplotlib.pyplot as plt
import matplotlib.patches as patches
fig2 = plt.figure()
ax2 = fig2.add_subplot(111, aspect='equal')
points = [[0.2, 0.4], [0.4, 0.8], [0.8, 0.8], [0.6, 0.4], [0.2,0.4]] #the points to trace the edges.
polygon= plt.Polygon(points, fill=None, edgecolor='r')
ax2.add_patch(polygon)
fig2.savefig('reg-polygon.png', dpi=90, bbox_inches='tight')
plt.show()
Also, note that you should use Polygon instead of RegularPolygon.

Related

How to preserve axis aspect ratio with tight_layout

I have a plot with both a colorbar and a legend. I want to place the legend outside of the plot to the right of the colorbar. To accomplish this, I use bbox_to_anchor argument, but this causes the legend to get cut off:
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm
_, ax = plt.subplots()
extent = np.r_[0, 1, 0, 1]
space = np.linspace(0, 1)
probs = np.array([[norm.cdf(x + y) for x in space] for y in space])
colormap = ax.imshow(probs, aspect="auto", origin="lower", extent=extent, alpha=0.5)
colorbar = plt.colorbar(colormap, ax=ax)
colorbar.set_label(f"Probability")
ax.scatter(
[0.2, 0.4, 0.6], [0.8, 0.6, 0.4], color="r", label="Labeled Points",
)
plt.legend(loc="center left", bbox_to_anchor=(1.3, 0.5))
plt.title
plt.show()
Plot with legend cut off
To fix the legend, I insert a call to plt.tight_layout() before plt.show(), but this causes the aspect ratio to get distorted:
Plot with distorted aspect ratio
How can I show the entire legend and preserve the aspect ratio of the axes?
You can manage the ratio between axis height and width with matplotlib.axes.Axes.set_aspect. Since you want them to be equal:
ax.set_aspect(1)
Then you can use matplotlib.pyplot.tight_layout to fit the legend within the figure.
If you want to adjust margins too, you can use matplotlib.pyplot.subplots_adjust.
Complete Code
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm
_, ax = plt.subplots()
extent = np.r_[0, 1, 0, 1]
space = np.linspace(0, 1)
probs = np.array([[norm.cdf(x + y) for x in space] for y in space])
colormap = ax.imshow(probs, aspect="auto", origin="lower", extent=extent, alpha=0.5)
colorbar = plt.colorbar(colormap, ax=ax)
colorbar.set_label(f"Probability")
ax.scatter([0.2, 0.4, 0.6], [0.8, 0.6, 0.4], color="r", label="Labeled Points",)
plt.legend(loc="center left", bbox_to_anchor=(1.3, 0.5))
ax.set_aspect(1)
plt.tight_layout()
plt.subplots_adjust(left = 0.1)
plt.show()

how to plot multiple legends from a list

I am trying to iterate over multiple plots each with their own labels. The legend values are taken from the list 'iou'. The code below only generates 1 label for iou=0.2. Any help is appreciated.
iou=[0.2, 0.4, 0.6, 0.8]
from matplotlib import pyplot as plt
fig = plt.figure()
for i in range(0,4):
p=plt.plot(recall[i], precision[i], marker='+')
plt.legend(p, iou)
plt.title("PR curves")
plt.xlabel("Recall")
plt.ylabel("Precision")
In each iteration of your loop you'll want to use the label attribute of plt.plot and only after the loop call to plt.legend
from matplotlib import pyplot as plt
iou=[0.2, 0.4, 0.6, 0.8]
fig, ax = plt.subplots(1, 1)
for i in range(0,4):
ax.plot(recall[i], precision[i], marker='+', label=iou[i])
fig.legend()
ax.set_title("PR curves")
ax.set_xlabel("Recall")
ax.set_ylabel("Precision")
A minimal working example could be the following:
from matplotlib import pyplot as plt
import numpy as np
iou=[0.2, 0.4, 0.6, 0.8]
precision = np.random.randint(0, 10, size=(4, 10))
fig, ax = plt.subplots(1, 1)
for i in range(0,4):
ax.plot(np.r_[:10], iou[i] * precision[i], marker='+', label=iou[i])
fig.legend()
ax.set_title("PR curves")
ax.set_xlabel("Recall")
ax.set_ylabel("Precision")
Which gives the following:

Creating a graph with 2 planes and colormaps via Python

I have two 2D arrays and I want to use to produce an image similar to the one that fallows, just with different limits on the axis.
Here is my attempt so far:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_xlim(-2.01, 2.01)
ax.set_ylim(-2.01, 2.01)
ax.set_zlim(-2.01, 2.01)
cmap = plt.cm.gray
im = ax.imshow(np.asarray(array1), cmap=cmap)
im.remove()
fig.colorbar(im)
plt.show()
The arrays I have, (array1 and array2) are two dimensional with sizes n by n. Any help or a point in the right direction will be greatly appreciated!
With help of Matplotlib - Plot a plane and points in 3D simultaneously, I am able to achieve this:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_xlim(0, 1)
ax.set_ylim(0, 1)
ax.set_zlim(0, 1)
ax.set_xticks([0, 0.2, 0.4, 0.6, 0.8, 1])
ax.set_yticks([0, 0.5, 1])
ax.set_zticks([0, 0.2, 0.4, 0.6, 0.8, 1])
cmap = plt.cm.gray
#plot vertical surface
y = 0.5
xx, zz = np.meshgrid(np.linspace(0,1,10), np.linspace(0,1,10))
p = ax.plot_surface(xx, y, zz, cmap=cmap, alpha=0.5)
x = 0.2
yy, zz = np.meshgrid(np.linspace(0,1,10), np.linspace(0,1,10))
p = ax.plot_surface(x, yy, zz, cmap=cmap, alpha=0.5)
fig.colorbar(p)
plt.show()
Note that I didn't use normal or dot just as another question do, because here you want to plot vertical planes.
Here's what I got(I'm working on the right occlusion):

Change matplotlib colorbar to custom height

I would like to set the colorbar of my plot to a custom height, not necessarily to match the size of the plot. In fact I would like the height of the colorbar PLUS the title on top of it to match the height of the figure.
With
ax3 = divider.append_axes('right', size='10%', pad=0.3)
cb = plt.colorbar(Q, cax=ax3, ticks=[0.0, 3.0, 6.0, 9.0, 12.0, 15.0], format='%.1f')
I managed to have a colorbar with the same height as the plot, which has been asked for many other times, now I would like to shrink it.
Following suggestion provided in other questions I decided to explicitly give the colorbar its own axes with add_axes, after getting the position of the last plot axes with get_position. Here is what I'm trying to do. There are no data and no colorbar in this example, just to show that I'm not getting the result I expected:
from __future__ import unicode_literals
import numpy as np
from scipy.interpolate import griddata
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
from matplotlib.pylab import cm
import matplotlib.colors as colors
from mpl_toolkits.axes_grid1 import make_axes_locatable, axes_size
matplotlib.rcParams.update({'font.size': 8})
fig = plt.figure()
fig.set_size_inches(6.3,6.3)
ax1 = plt.subplot(111)
divider = make_axes_locatable(ax1)
ax2 = divider.append_axes('right', size='100%', pad=0.3)
axes = [ax1, ax2]
ltypes = ['dashed', 'solid']
xi = np.linspace(-18.125, 18.125, 11)
yi = np.linspace(0, 28, 9)
xv, yv = np.meshgrid(xi, yi)
xcOdd = 0.2
zcOdd = 0.725
xcEven = 0.6
zcEven = 0.725
maskRadius = 0.15
for i in range(2):
ax = axes[i]
ax.set_xlabel('distance [m]')
if i == 0:
ax.set_ylabel('depth [m]')
if i == 1:
ax.set_yticklabels([])
ax.invert_yaxis()
ax.tick_params(direction='in')
ax.set_aspect('equal')
odd = Circle((xcOdd, zcOdd), .15, linewidth=1.2, color='k', fill=False)
even = Circle((xcEven, zcEven), .15, linewidth=1.2, linestyle=ltypes[i], color='k', fill=False)
vmax = 15.
vmin = 0.
norm = matplotlib.colors.Normalize(vmin,vmax, clip=False)
color_map = matplotlib.colors.ListedColormap(plt.cm.Greys(np.linspace(0.25, 1, 5)), "name")
ax.add_patch(odd)
pad = 0.03
width = 0.03
pos = ax2.get_position()
ax3 = fig.add_axes([pos.xmax + pad, pos.ymin, width, 0.7*(pos.ymax-pos.ymin) ])
plt.savefig('prova-vect-paper-test-2.eps', format='eps')
Why is get_position returning the wrong boundingbox?
You need to draw the canvas before obtaining the actual position from .get_position(). This is because due to the equal aspect ratio, the axes changes size and position at draw time.
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.colors as colors
from mpl_toolkits.axes_grid1 import make_axes_locatable
matplotlib.rcParams.update({'font.size': 8})
fig = plt.figure()
fig.set_size_inches(6.3,6.3)
ax1 = plt.subplot(111)
divider = make_axes_locatable(ax1)
ax2 = divider.append_axes('right', size='100%', pad=0.3)
axes = [ax1, ax2]
xi = np.linspace(-18.125, 18.125, 11)
yi = np.linspace(0, 28, 9)
xv, yv = np.meshgrid(xi, yi)
for i in range(2):
ax = axes[i]
ax.set_xlabel('distance [m]')
if i == 0:
ax.set_ylabel('depth [m]')
if i == 1:
ax.set_yticklabels([])
ax.invert_yaxis()
ax.tick_params(direction='in')
ax.set_aspect('equal')
vmax = 15.
vmin = 0.
norm = colors.Normalize(vmin,vmax, clip=False)
color_map = colors.ListedColormap(plt.cm.Greys(np.linspace(0.25, 1, 5)), "name")
im = ax.imshow(yv, cmap=color_map, norm=norm)
pad = 0.03
width = 0.03
fig.canvas.draw()
pos = ax2.get_position()
ax3 = fig.add_axes([pos.xmax + pad, pos.ymin, width, 0.7*(pos.ymax-pos.ymin) ])
fig.colorbar(im, cax=ax3)
plt.show()

How to sharex when using subplot2grid

I'm a Matlab user recently converted to Python. Most of the Python skills I manage on my own, but with plotting I have hit the wall and need some help.
This is what I'm trying to do...
I need to make a figure that consists of 3 subplots with following properties:
subplot layout is 311, 312, 313
the height of 312 and 313 is approximately half of the 311
all subplots share common X axis
the space between the subplots is 0 (they touch each other at X axis)
By the way I know how to make all this, only not in a single figure. That is the problem I'm facing now.
For example, this is my ideal subplot layout:
import numpy as np
import matplotlib.pyplot as plt
t = np.arange(0.0, 2.0, 0.01)
s1 = np.sin(2*np.pi*t)
s2 = np.exp(-t)
s3 = s1*s2
fig = plt.figure()
ax1 = plt.subplot2grid((4,3), (0,0), colspan=3, rowspan=2)
ax2 = plt.subplot2grid((4,3), (2,0), colspan=3)
ax3 = plt.subplot2grid((4,3), (3,0), colspan=3)
ax1.plot(t,s1)
ax2.plot(t[:150],s2[:150])
ax3.plot(t[30:],s3[30:])
plt.tight_layout()
plt.show()
Notice how the x axis of different subplots is misaligned. I do not know how to align the x axis in this figure, but if I do something like this:
import numpy as np
import matplotlib.pyplot as plt
t = np.arange(0.0, 2.0, 0.01)
s1 = np.sin(2*np.pi*t)
s2 = np.exp(-t)
s3 = s1*s2
fig2, (ax1, ax2, ax3) = plt.subplots(nrows=3, ncols=1, sharex=True)
ax1.plot(t,s1)
ax2.plot(t[:150],s2[:150])
ax3.plot(t[30:],s3[30:])
plt.tight_layout()
plt.show()
Now the x axis is aligned between the subplots, but all subplots are the same size (which is not what I want)
Furthermore, I would like that the subplots are touching at x axis like this:
import numpy as np
import matplotlib.pyplot as plt
t = np.arange(0.0, 2.0, 0.01)
s1 = np.sin(2*np.pi*t)
s2 = np.exp(-t)
s3 = s1*s2
fig1 = plt.figure()
plt.subplots_adjust(hspace=0)
ax1 = plt.subplot(311)
ax2 = plt.subplot(312, sharex=ax1)
ax3 = plt.subplot(313, sharex=ax1)
ax1.plot(t,s1)
ax2.plot(t[:150],s2[:150])
ax3.plot(t[30:],s3[30:])
xticklabels = ax1.get_xticklabels()+ax2.get_xticklabels()
plt.setp(xticklabels, visible=False)
plt.show()
So to rephrase my question:
I would like to use
plt.subplot2grid(..., colspan=3, rowspan=2)
plt.subplots(..., sharex=True)
plt.subplots_adjust(hspace=0)
and
plt.tight_layout()
together in the same figure. How to do that?
Just specify sharex=ax1 when creating your second and third subplots.
import numpy as np
import matplotlib.pyplot as plt
t = np.arange(0.0, 2.0, 0.01)
s1 = np.sin(2*np.pi*t)
s2 = np.exp(-t)
s3 = s1*s2
fig = plt.figure()
ax1 = plt.subplot2grid((4,3), (0,0), colspan=3, rowspan=2)
ax2 = plt.subplot2grid((4,3), (2,0), colspan=3, sharex=ax1)
ax3 = plt.subplot2grid((4,3), (3,0), colspan=3, sharex=ax1)
ax1.plot(t,s1)
ax2.plot(t[:150],s2[:150])
ax3.plot(t[30:],s3[30:])
fig.subplots_adjust(hspace=0)
for ax in [ax1, ax2]:
plt.setp(ax.get_xticklabels(), visible=False)
# The y-ticks will overlap with "hspace=0", so we'll hide the bottom tick
ax.set_yticks(ax.get_yticks()[1:])
plt.show()
If you still what to use fig.tight_layout(), you'll need to call it before fig.subplots_adjust(hspace=0). The reason for this is that tight_layout works by automatically calculating parameters for subplots_adjust and then calling it, so if subplots_adjust is manually called first, anything in the first call to it will be overridden by tight_layout.
E.g.
fig.tight_layout()
fig.subplots_adjust(hspace=0)
A possible solution is to manually create the axis using the add_axis method like shown here:
import numpy as np
import matplotlib.pyplot as plt
t = np.arange(0.0, 2.0, 0.01)
s1 = np.sin(2*np.pi*t)
s2 = np.exp(-t)
s3 = s1*s2
left, width = 0.1, 0.8
rect1 = [left, 0.5, width, 0.4]
rect2 = [left, 0.3, width, 0.15]
rect3 = [left, 0.1, width, 0.15]
fig = plt.figure()
ax1 = fig.add_axes(rect1) #left, bottom, width, height
ax2 = fig.add_axes(rect2, sharex=ax1)
ax3 = fig.add_axes(rect3, sharex=ax1)
ax1.plot(t,s1)
ax2.plot(t[:150],s2[:150])
ax3.plot(t[30:],s3[30:])
# hide labels
for label1,label2 in zip(ax1.get_xticklabels(),ax2.get_xticklabels()):
label1.set_visible(False)
label2.set_visible(False)
plt.show()
But this way you cannot use tight_layout as you explicitly define the size of each axis.

Categories

Resources