I want to superimpose Circles on top of a 2D plot in Matplotlib as position markers. Currently they look very much like what they are, colored Circles:
import matplotlib.pyplot as plt
plt.axes()
circle = plt.Circle((0, 0), radius=0.3,fc='b')
circle1 = plt.Circle((1, 1), radius=0.3, fc='y')
circle2 = plt.Circle((1, 0), radius=0.3, fc='r')
plt.gca().add_patch(circle)
plt.gca().add_patch(circle1)
plt.gca().add_patch(circle2)
plt.axis('scaled')
plt.show()
Is there a way to give theses circles the appearance of a 3D object without using mayavi?
These are examples of my goal:
EDIT
With the information in the link supplied by user3419537 and the idea presented here Custom color maps i created the following idea, that lets me somehow plot circles filled with a gradient:
import numpy as np
import matplotlib.colors as mcolors
def make_colormap(seq):
"""Return a LinearSegmentedColormap
seq: a sequence of floats and RGB-tuples. The floats should be increasing
and in the interval (0,1).
"""
seq = [(None,) * 3, 0.0] + list(seq) + [1.0, (None,) * 3]
cdict = {'red': [], 'green': [], 'blue': []}
for i, item in enumerate(seq):
if isinstance(item, float):
r1, g1, b1 = seq[i - 1]
r2, g2, b2 = seq[i + 1]
cdict['red'].append([item, r1, r2])
cdict['green'].append([item, g1, g2])
cdict['blue'].append([item, b1, b2])
return mcolors.LinearSegmentedColormap('CustomMap', cdict)
def gauplot(centers, radiuses, xr=None, yr=None, P_color='black'):
c = mcolors.ColorConverter().to_rgb
# Maybe it is possible to change the values to get a better gradient?
current_cmap = make_colormap([c(P_color),0.05,c(P_color),0.1,c(P_color), c('white')])
nx, ny = 1000.,1000.
xgrid, ygrid = np.mgrid[xr[0]:xr[1]:(xr[1]-xr[0])/nx,yr[0]:yr[1]:(yr[1]-yr[0])/ny]
im = xgrid*0 + np.nan
xs = np.array([np.nan])
ys = np.array([np.nan])
fis = np.concatenate((np.linspace(-np.pi,np.pi,100), [np.nan]) )
#cmap = plt.cm.gray
cmap = current_cmap
cmap.set_bad('white')
thresh = 2.8
for curcen,currad in zip(centers,radiuses):
curim=(((xgrid-curcen[0])**2+(ygrid-curcen[1])**2)**.5)/currad*thresh
im[curim<thresh]=np.exp(-.5*curim**2)[curim<thresh]
xs = np.append(xs, curcen[0] + currad * np.cos(fis))
ys = np.append(ys, curcen[1] + currad * np.sin(fis))
plt.imshow(im.T, cmap=cmap, extent=xr+yr)
plt.plot(xs, ys, 'r-')
gauplot([(0,0), (2,3), (5,1), (6, 7), (6.1, 6.1)], [.3,.4, .5, 1, .4], [-1,10], [-1,10],P_color="#75507b")
plt.show()
Unfortunately only the red circles appear at the right position:
I would appreciate a tip what might be the cause of this.
At the moment i am plotting my circles in the script in question like this:
circle = Circle(x,y,*kwargs)
plt.gca().add_patch(circle)
would it be possible to adapt the above solution in a way that it can plot circles with different colors at the position (x,y) and superimpose them to an existing plot as well?
The imshow plot is mirrored against the desired plot. This is because the imshow plot has its origin in the upper left corner, but the plot to show the circles has the origin in the lower left corner.
The solution would be to set the origin of the imshow in the lower left corner
plt.imshow(im.T, cmap=cmap, extent=xr+yr, origin="lower")
Related
I want to draw a bar plot in 3d. I know how to do that using the following code:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(111, projection='3d')
nbins = 50
# for c, z in zip(['r', 'g', 'b', 'y'], [30, 20, 10, 0]):
ys = np.random.normal(loc=10, scale=10, size=2000)
hist, bins = np.histogram(ys, bins=nbins)
xs = (bins[:-1] + bins[1:])/2
ax.bar(xs, hist, zs=30, zdir='y', color='r', ec='r', alpha=0.8)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
This will render something like this: https://i.stack.imgur.com/KK2If.png
However, my goal is to make the bar plot follows a line that I give as parameter. For example here, the parameter zdir='y' makes the plot have its current direction. Ideally I want to pass a parameter that makes the plot follows a given line for example y=2x+1.
Could someone help arrive at the desired result?
One way to achieve that is by using Poly3DCollection: the idea is to compute the coordinates and orientation of each bar, then add it to the plot.
The position and orientation of each bar can be computed starting from a rectangle in 3D space and applying the appropriate transformation matrix.
If you are going to change the curve, you will also need to change the bar width.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
from matplotlib.patches import Rectangle
################
# Generates data
################
nbins = 50
ys = np.random.normal(loc=10, scale=10, size=2000)
hist, bins = np.histogram(ys, bins=nbins)
xs = (bins[:-1] + bins[1:])/2
#################################################
# Create a single bar and a transformation matrix
#################################################
# rectangle of width=height=1, centered at x,y=0
# covering the z range [0, height]
rect = np.array([
[-0.5, 0, 0, 1],
[0.5, 0, 0, 1],
[0.5, 0, 1, 1],
[-0.5, 0, 1, 1],
])
def translate(x, y, z):
d = np.eye(4, dtype=float)
d[:, -1] = [x, y, z, 1]
return d
def scale(sx, sy, sz):
d = np.eye(4, dtype=float)
d[np.diag_indices(4)] = [sx, sy, sz, 1]
return d
def rotate(t):
d = np.eye(4, dtype=float)
d[:2, :2] = np.array([
[np.cos(t), -np.sin(t)],
[np.sin(t), np.cos(t)]])
return d
def transformation_matrix(t, x, y, z, w, h):
return translate(x, y, z) # rotate(t) # scale(w, 1, h)
def apply_transform(t, x, y, z, w, h):
"""Apply the transformation matrix to the rectangle"""
verts = transformation_matrix(t, x, y, z, w, h) # rect.T
return verts.T
#################
# Create the plot
#################
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
curve = lambda x: 2 * x + 1
# curve = lambda x: np.sin(0.05 * x)
xstep = abs(xs[0] - xs[1])
# NOTE: chose an appropriate bar width
width = xstep * 1.5
ys = curve(xs)
# previous bar coordinates
xp = np.roll(xs, 1)
yp = np.roll(ys, 1)
xp[0] = xs[0] - xstep
yp[0] = curve(xp[0])
# compute the orientation of the bars
theta = np.arctan2((ys - yp), (xs - xp))
# customize the appearance of the bar
facecolor = "tab:red"
edgecolor = "k"
linewidth = 0
# loop to add each bar
for x, y, t, h in zip(xs, ys, theta, hist):
verts_matrix = apply_transform(t, x, y, 0, width, h)
x, y, z = verts_matrix[:, 0], verts_matrix[:, 1], verts_matrix[:, 2]
verts = [list(zip(x, y, z))]
c = Poly3DCollection(verts, facecolor=facecolor, edgecolor=edgecolor, linewidth=linewidth)
ax.add_collection3d(c)
# eventually show a legend
ax.legend([Rectangle((0, 0), 1, 1, facecolor=facecolor, edgecolor=edgecolor, linewidth=linewidth)], ["Bar Plot"])
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.set_xlim(xs.min(), xs.max())
ax.set_ylim(ys.min(), ys.max())
ax.set_zlim(0, 100)
plt.show()
EDIT to explain what is going on:
Consider a generic rectangle with 4 vertices: bottom left, bottom right, top right, top left. For simplicity, let's fix width=height=1. Then we consider a reference system x,y,z and we draw this rectangle. The coordinates of vertices are: bottom left (-0.5, 0, 0), bottom right (0.5, 0, 0), top right (0.5, 0, 1) and top left (-0.5, 0, 1). Note that this rectangle is centered around the zero in the x direction. If we move it to x=2, then it will be centered at that location. You can see the above coordinates in rect: why does this variable has a fourth column filled with ones? That's a mathematical trick to be able to apply a translation matrix to the vertices.
Let's talk about transformation matrices (wikipedia has a nice page about it). Consider again our generic rectangle: we can scale it, rotate it and translate it to get a new rectangle in the position and orientation we want.
So, the code above defines a function for each transformation, translate, scale, rotate. Turns out that we can multiply together multiple transformation matrices to get an overall transformation: that's what transformation_matrix does, it combines the aforementioned transformations into a single matrix.
Finally, I used apply_transform to apply the transformation matrix to the generic rectangle: this will compute the coordinates of the vertices of the new rectangle, in the specified position/orientation with the specified size (width, height).
I have a pretty rough data set I am using to draw a phase diagram with matplotlib's pcolormesh.
x,y are 2D numpy arrays and represent a uniform grid at the moment. z contains integers ranging from 1-9, each number matching a phase. The option shading='nearest' centers the color chosen according to z at (x,y). My colormap is segmented matching the possible z values.
vmin, vmax = 1, 9
colors = ['blue', 'orange', 'black', 'gray', 'cyan', 'lime', 'yellow', 'green', 'red']
cmap = ListedColormap(colors)
axes[0].pcolormesh(x, y, z, shading = 'nearest', vmin = vmin, vmax = vmax, cmap = cmap)
With this I get the upper subplot which is acceptable for me.
However, some of the phases have common properties which is why I would like to add contours. For example, I would like to draw a contour that separates the colored and the black/gray parts. I have two problems here:
It would be great if I could use contour but I cannot manage to do so, see the second subplot drawn with contourf, without it triangulating(?) my data. This would not be a problem if I had a lot more data points but it is unlikely that I will increase the resolution by much. Even if I could live with triangulation: No yellow area should be drawn. But since z jumps from z=8(green) to z = 6(lime) contour inserts an intermediate yellow area.
Depending on how we solve this problem: I would really like to be able to draw contours both for connected and disconnected areas.
An idea I have is defining a new phase that covers lime, green and cyan and then outline that area. The data manipulation for this is simple, however, I do not know how to proceed with matplotlib after that. Besides, I do not know how one would identify connected and disconnected cells.
I managed to come of with the following setup that I am almost satisfied with:
The key is the so-called alpha shape. It is in essence a triangulation, roughly speaking for determining the bounding polygon of a set of points. Here is the corresponding python module. It was very simple to implement. I had no prior experience with shapely. In addition, I had to dig a bit into matplotlib's pcolor source code. In the end I came up with the following script, main code is at the bottom.
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import numpy.ma as ma
from descartes import PolygonPatch
import alphashape
#various parameters
nrows, ncols = 1, 1
# create segmented colormap with 9 colors (I will need 9 colors in my actual application)
colors = ['blue', 'orange', 'black', 'gray', 'cyan', 'lime', 'yellow', 'green', 'red']
cmap = mpl.colors.ListedColormap(colors)
# set vmin and vmax from 1 to 9
vmin = 1
vmax = 9
# name of the saved imagefile
savename = 'plot_alpha_shape'
# alpha value for the alpha shape, dont confuse it with the opacity from the
# standard mpl kwargs
alpha = 0.5
# grid discretization
dx = 2.0
dy = 1
# create 2d rectangular mesh
x, y = np.meshgrid(dx*np.arange(3), dy*np.arange(4))
# create homogeneous demo data
z = np.ones_like(x)*2
# change some values to make z heterogeneous
z[0] = np.ones(3)
z[1,-1] = 1
# define mask for the contour
mask = z>1
z_masked = ma.masked_array(z, mask = mask)
pcolor_kwargs = dict(shading = 'nearest', vmin = vmin, vmax = vmax, cmap = cmap)
contour_kwargs = dict(fc = 'none', ec = 'k', linewidth = 3)
def get_quadrilateral_vertices(x,y):
X = interp_grid(x)
Y = interp_grid(y)
X = interp_grid(X.T).T
Y = interp_grid(Y.T).T
return X, Y
def interp_grid(X):
dX = np.diff(X, axis=1)/2.
X = np.hstack((X[:, [0]] - dX[:, [0]],
X[:, :-1] + dX,
X[:, [-1]] + dX[:, [-1]]))
return X
def get_xymask(x,y):
# merge x and y masks in case they are different
mask = ma.getmaskarray(x) + ma.getmaskarray(y)
# map mask to the cells in order to merge it with z mask
xymask = (mask[0:-1, 0:-1] + mask[1:, 1:] +
mask[0:-1, 1:] + mask[1:, 0:-1])
return xymask
def execute_masking(x,y,z):
# get dimensions
Ny, Nx = x.shape
xymask = get_xymask(x,y)
# merge all masks
# don't plot if C or any of the surrounding vertices are masked.
mask = ma.getmaskarray(z) + xymask
unmask = ~mask
X1 = ma.filled(x[:-1, :-1])[unmask]
Y1 = ma.filled(y[:-1, :-1])[unmask]
X2 = ma.filled(x[1:, :-1])[unmask]
Y2 = ma.filled(y[1:, :-1])[unmask]
X3 = ma.filled(x[1:, 1:])[unmask]
Y3 = ma.filled(y[1:, 1:])[unmask]
X4 = ma.filled(x[:-1, 1:])[unmask]
Y4 = ma.filled(y[:-1, 1:])[unmask]
# npoly = len(X1)
xy = np.stack([X1, Y1, X2, Y2, X3, Y3, X4, Y4], axis=-1)
# one vertex is duplicate in the original code
# xy = np.stack([X1, Y1, X2, Y2, X3, Y3, X4, Y4, X1, Y1], axis=-1)
# transform to array of xy pairs
verts = xy.reshape((-1, 2))
z = ma.filled(z[:Ny - 1, :Nx - 1])[unmask]
return verts, z
def get_masked_data(x,y,z):
X, Y = get_quadrilateral_vertices(x,y)
# convert to MA, if necessary.
z = ma.asarray(z)
X = ma.asarray(X)
Y = ma.asarray(Y)
return execute_masking(X,Y,z)
def plot_vertices(ax, verts):
verts = verts.T
ax.plot(*verts, linestyle = '', marker = 'x', color = 'r', ms = 10)
# main code
# x,y,z are cellcentered data
# use get_masked_data and its inner functions to get the vertices of the
# cells used in pcolor
# we are not using zdummy here
verts, zdummy = get_masked_data(x,y,z_masked)
# map vertices to a list of (x,y) tuples, each representing one vertex
contour_data = list(zip(verts[:,0], verts[:,1]))
# create an alpha shape from the vertices
contour_alphashape = alphashape.alphashape(verts, alpha)
# create figure with one subplot
fig = plt.figure(figsize=(15/2.54,nrows*4/2.54), constrained_layout=True)
gs = GridSpec(nrows, ncols, figure=fig)
axes = [fig.add_subplot(gs[j,i]) for j in range(nrows) for i in range(ncols)]
# plot vertices
plot_vertices(axes[0], verts)
# plot pcolor
pmesh1 = axes[0].pcolor(x,y,z, **pcolor_kwargs)
# plot the contour using alphashape
contour = PolygonPatch(contour_alphashape,**contour_kwargs)
axes[0].add_patch(contour)
# save the plot
plt.savefig(savename + '.png')
Almost all of the defined functions are taken from matplotlib's pcolor and _pcolorargs.
The alpha shape will become more detailed the larger you choose alpha. For very small alpha you will get a convex hull. I am attaching the result of the script.
As you can see, the contour does not exactly match the blue area. If alpha becomes too large, alpha shape will not return a proper polygon if I understood it correctly which is why I cannot make the contour align even tighter. I think it has something to do with the regular spacing of my data, too.
I have written the following code that calculates the orientation of a blob using eigenvalues. When the orientation is determined, the function "straighten_up" straightens the blob out.
The only thing I'm missing to be fully satisfied, is a 1px white border in the second output figure between the black area and the green area. How can I do this?
I'm using a mask image as input:
code:
import numpy as np
import matplotlib.pyplot as plt
import cv2
img = cv2.imread('input_image.png',100)
edges = cv2.Canny(img,0,255) #searching for a border
# compute the orientation of a blob
img = edges
y, x = np.nonzero(img) # Find the index of the white pixels
x = x - np.mean(x) #The average of an array of elements
y = y - np.mean(y)
coords = np.vstack([x, y])
cov = np.cov(coords) #determine covariance matrix
evals, evecs = np.linalg.eig(cov) #eigenvectors
sort_indices = np.argsort(evals)[::-1] #Sort Eigenvalues in decreasing order
x_v1, y_v1 = evecs[:, sort_indices[0]]
x_v2, y_v2 = evecs[:, sort_indices[1]]
scale = 30
plt.plot([x_v1*-scale*2, x_v1*scale*2], #plot to show the eigenvectors
[y_v1*-scale*2, y_v1*scale*2], color='red')
plt.plot([x_v2*-scale, x_v2*scale],
[y_v2*-scale, y_v2*scale], color='blue')
plt.plot(x, y, 'k.')
plt.axis('equal')
plt.gca().invert_yaxis()
plt.show()
def straighten_up(x_v1,y_v1,coords):
theta = np.arctan((x_v1)/(y_v1))
rotation_mat =np.matrix([[np.cos(theta), -np.sin(theta)],[np.sin(theta),np.cos(theta)]])
transformed_mat = rotation_mat*coords
x_transformed, y_transformed = transformed_mat.A
fig, ax = plt.subplots(nrows=1, ncols=1)
ax = fig.add_subplot(1, 1, 1) # nrows, ncols, index
ax.set_facecolor((1.0, 0.47, 0.42))
plt.plot(x_transformed,y_transformed,"black")
straighten_up(x_v1,y_v1,coords)
plt.show()
with output:
Your x_transformed and y_transformed are the x and y coordinates of the rotated border. So you can draw them e.g. with plt.scatter. This draws dots (the third parameter is the size) on these x,y positions. Use zorder to make sure the scatter dots are not hidden by the previous parts of the plot.
Following code does just that:
fig, ax = plt.subplots(nrows=1, ncols=1)
ax = fig.add_subplot(1, 1, 1) # nrows, ncols, index
ax.set_facecolor('fuchsia')
plt.axis('equal')
plt.plot(x_transformed, y_transformed, c="lime")
plt.scatter(x_transformed, y_transformed, 1, c="white", zorder=3)
plt.show()
As you notice, there is another problem: the plot of the filled figure isn't similar to your input image. What is happening, is that plot draws lines(x[0],y[0]) to (x[1],y[1]) to (x[2],y[2]) etc.. As your x and y are only the border points, not ordered as a polygon, it is more complicated to get a correctly filled polygon. For a random input image, you can have many borders, that can form polygons with holes and islands and which can touch the image borders.
To properly get the interior points, you might get y, x = np.nonzero(img) from the original image (instead of only the edges), then do the same shift subtracting the mean of the edges, and use the same transformation matrix.
This question leads on from a previous question I asked yesterday that got me most of the way to what I am after:
Rotate transformation on matplotlib axis in subplot
I want to create a two by two array of graphs where the bottom-left is a scatter plot and the other three are histograms projecting the x, y, and x-y data from that plot. That final histogram I want to have at a 45 degree angle, and it is the positioning of that plot which I am trying to adjust.
Currently, I have this:
from matplotlib.transforms import Affine2D
import mpl_toolkits.axisartist.floating_axes as floating_axes
import matplotlib.pyplot as plt
def setup_axes(fig, rect, rotation, axisScale, axisLimits, doShift):
tr_rot = Affine2D().scale(axisScale[0], axisScale[1]).rotate_deg(rotation)
# This seems to do nothing
if doShift:
tr_trn = Affine2D().translate(-90,-5)
else:
tr_trn = Affine2D().translate(0,0)
tr = tr_rot + tr_trn
grid_helper = floating_axes.GridHelperCurveLinear(tr, extremes=axisLimits)
ax = floating_axes.FloatingSubplot(fig, rect, grid_helper=grid_helper)
fig.add_subplot(ax)
aux_ax = ax.get_aux_axes(tr)
return ax, aux_ax
fig = plt.figure(1, figsize=(8, 8))
axes = []
axisOrientation = [0, 0, 270, -45]
axisScale = [[1,1],[6,1],[6,1],[6,1]]
axisPosition = [223,221,224,222]
axisLimits = [(-0.5, 4.5, -0.5, 4.5),
(-0.5, 4.5, 0, 12),
(-0.5, 4.5, 0, 12),
(-3.5, 3.5, 0, 12)]
doShift = [False, False, False, True]
label_axes = []
for i in range(0, len(axisOrientation)):
ax, aux_ax = setup_axes(fig, axisPosition[i], axisOrientation[i],
axisScale[i], axisLimits[i], doShift[i])
axes.append(aux_ax)
label_axes.append(ax)
numPoints = 100
x = []
y = []
for i in range(0,numPoints):
x.append(np.random.rand() + i/100.0)
y.append(np.random.rand() + i/100.0 + np.mod(i,2)*2)
axes[0].plot(x,y,ls='none',marker='x')
label_axes[0].axis["bottom"].label.set_text('Variable 1')
label_axes[0].axis["left"].label.set_text('Variable 2')
b = np.linspace(-0.5,4.5,50)
axes[1].hist(x, bins = b)
axes[2].hist(y, bins = b)
b = np.linspace(-3.5,3.5,50)
axes[3].hist(np.array(x)-np.array(y), bins=b)
for i in range(1,len(label_axes)):
for axisLoc in ['top','left','right']:
label_axes[i].axis[axisLoc].set_visible(False)
label_axes[i].axis['bottom'].toggle(ticklabels=False)
fig.subplots_adjust(wspace=-0.30, hspace=-0.30, left=0.00, right=0.99, top=0.99, bottom=0.0)
plt.show()
Which gives:
As you can see in the code, I try to shift the position of that top-right plot with an Affine2D().translate() but it seems to have no effect. Does anybody know how I might move this plot so that its x-axis almost-touches the top-right corner of the bottom-left plot's axes?
Edit:
I have also just noticed that the bottom-right plot is upside-down compared to how it should be. It needs to be top-bottom mirrored somehow.
Edit 2:
This code before fig.subplots_adjust() will fix that:
label_axes[2].invert_yaxis()
I just want to draw simple shape by points, like this:
import matplotlib.pyplot as plt
rectangle = [(0,0),(0,1),(1,1),(1,0)]
hexagon = [(0,0),(0,1),(1,2),(2,1),(2,0),(1,-1)]
l_shape = [(0,0),(0,3),(1,3),(1,1),(3,1),(3,0)]
concave = [(0,0),(0,3),(1,3),(1,1),(2,1),(2,3),(3,3),(3,0)]
for points in [rectangle, hexagon, l_shape, concave]:
# 1. Can I get rid of the zip? plot directly by points
# 2. How can I make the shape complete?
xs, ys = zip(*points)
plt.plot(xs, ys, 'o')
plt.plot(xs, ys, '-')
automin, automax = plt.xlim()
plt.xlim(automin-0.5, automax+0.5)
automin, automax = plt.ylim()
plt.ylim(automin-0.5, automax+0.5)
# Can I display the shapes 2 in 1 line?
plt.show()
My question is
How can I get rid of the *zip? I mean, directyly draw by points, rather than 2 array.
How to make these shapes complete? Since I'm looping through all the points, the first and last cannot connect together, how can I do it?
Can I draw the shape without giving the specific points order?(Something like convex hull?)
To close the shape, just add the first point again at the end of the list:
# rectangle = [(0,0),(0,1),(1,1),(1,0)]
rectangle = [(0,0),(0,1),(1,1),(1,0),(0,0)]
plt.plot takes a list of x coordinates and a list of y coordinates. I would say that the way you're doing it now is already the way of doing it "by points rather than 2 arrays". Because if you wanted to do it without zip, it would look like this:
rectangleX = [0, 0, 1, 1, 0]
rectangleY = [0, 1, 1, 0, 0]
plt.plot(rectangleX, rectangleY, 'o')
plt.plot(rectangleX, rectangleY, '-')
Update:
For better polygon support, use the patches module [example]. This may be more along the lines of what you're looking for. By default (closed = True), it will close the path for you, and it also allows you to add vertices directly to a list (not two separate lists):
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
rectangle = [(0,0),(0,1),(1,1),(1,0)]
fig, ax = plt.subplots()
ax.add_patch(mpatches.Polygon(rectangle))
automin, automax = plt.xlim()
plt.xlim(automin-0.5, automax+0.5)
automin, automax = plt.ylim()
plt.ylim(automin-0.5, automax+0.5)
plt.show()
The code below doesn't use temporary variables xs and ys, but a direct tuple unpacking. Also I add first point from points list to make shapes complete.
rectangle = [(0,0),(0,1),(1,1),(1,0)]
hexagon = [(0,0),(0,1),(1,2),(2,1),(2,0),(1,-1)]
l_shape = [(0,0),(0,3),(1,3),(1,1),(3,1),(3,0)]
concave = [(0,0),(0,3),(1,3),(1,1),(2,1),(2,3),(3,3),(3,0)]
for points in [rectangle, hexagon, l_shape, concave]:
plt.plot(*zip(*(points+points[:1])), marker='o')
automin, automax = plt.xlim()
plt.xlim(automin-0.5, automax+0.5)
automin, automax = plt.ylim()
plt.ylim(automin-0.5, automax+0.5)
plt.show()
Provide this answer as an alternative leekaiinthesky's post