Redraw plot in same window with scipy / voronoi_plot_2d - python

I'm trying to make a Voronoi plot update in real time as the generating points change position.
My problem is how to reuse the same figure, since currently I get a new window each time I call voronoi_plot_2d.
See code:
#!/usr/bin/env python
import numpy as np
import time
from scipy.spatial import Voronoi, voronoi_plot_2d
import matplotlib.pyplot as plt
plt.ion()
(x,y) = (1,2)
plt.show()
while True:
print "loop "
x += 0.1
y += 0.1
points = np.array([[0, 0], [1, 3], [0, 2.5], [x,y], [4, 1], [6, 4]])
vor = Voronoi(points)
apa = voronoi_plot_2d(vor)
time.sleep(0.5)
I got some ideas for this from
http://docs.scipy.org/doc/scipy-0.15.1/reference/generated/scipy.spatial.Voronoi.html
real-time plotting in while loop with matplotlib

The code in the guide can be used to acheive this.
http://docs.scipy.org/doc/scipy/reference/tutorial/spatial.html
I haven't yet had time to read through and understand all the code, but it "manually" does what I want and it works.
Instead of using
voronoi_plot_2d(vor)
It step by step uses the different parts of vor to plot the voronoi plot, and this can be repeated in the loop. Full code example below:
#!/usr/bin/env python
import numpy as np
import time
from scipy.spatial import Voronoi, voronoi_plot_2d
import matplotlib.pyplot as plt
plt.ion()
(x,y) = (1,2)
plt.draw()
while True:
print "loop "
x += 0.1
y += 0.1
points = np.array([[0, 0], [1, 3], [0, 2.5], [x,y], [4, 1], [6, 4]])
plt.clf()
vor = Voronoi(points)
####MANUAL PLOTTING
plt.plot(points[:,0], points[:,1], 'o')
plt.plot(vor.vertices[:,0], vor.vertices[:,1], '*')
plt.xlim(-1, 3); plt.ylim(-1, 3)
for simplex in vor.ridge_vertices:
simplex = np.asarray(simplex)
if np.all(simplex >= 0):
plt.plot(vor.vertices[simplex,0], vor.vertices[simplex,1], 'k-')
center = points.mean(axis=0)
for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices):
simplex = np.asarray(simplex)
if np.any(simplex < 0):
i = simplex[simplex >= 0][0] # finite end Voronoi vertex
t = points[pointidx[1]] - points[pointidx[0]] # tangent
t /= np.linalg.norm(t)
n = np.array([-t[1], t[0]]) # normal
midpoint = points[pointidx].mean(axis=0)
far_point = vor.vertices[i] + np.sign(np.dot(midpoint - center, n)) * n * 100
plt.plot([vor.vertices[i,0], far_point[0]], [vor.vertices[i,1], far_point[1]], 'k--')
plt.draw()
time.sleep(0.5)

Related

python - Plotting implicit function f(x,y) = 0, where x,y undergo matrix multiplication

As the implicit function where 'A' is an n*2 matrix
0 = np.dot((x,y),A)
0 = xA11 yA12
0 = xA21 yA22
...
0 = xAn1 yAn2
Is it possible, via matplotlib or other means, to plot all the lines on the same plot without a large loop?
Given a n*2 matrix A, for each row i a line is defined by A[i,0]*x + A[i,1]*y == 0. This means 0,0 always lies on the line, as well as the point x=A[i,1],y=-A[i,0]. Multiplying with any value, e.g. by normalizing will again give points on the line.
The following code shows 3 ways to visualize these lines:
Some line segments cut by a circle, together with x=A[i,1],y=-A[i,0] and x=-A[i,1],y=A[i,0].
The same segments extended till the plot's border.
Just some end points on a circle.
import matplotlib.pyplot as plt
import numpy as np
from numpy.linalg import norm
from matplotlib.collections import LineCollection
n = 10
radius = 20
A = np.random.uniform(-10, 10, (n, 2))
B = A / norm(A, axis=1, keepdims=True) * radius # normalize and put on a circle with given radius
lines = np.dstack([B[:, 1], -B[:, 0], -B[:, 1], B[:, 0]]).reshape(-1, 2, 2)
fig, axes = plt.subplots(ncols=3, figsize=(14, 4))
for ax in axes:
ax.set_aspect('equal')
for ax in axes[:2]:
lc = LineCollection(lines, colors='blue', linewidths=2)
ax.add_collection(lc)
if ax == axes[0]:
ax.scatter(A[:, 1], -A[:, 0], color='crimson')
ax.scatter(-A[:, 1], A[:, 0], color='crimson')
elif ax == axes[1]:
ax.set_xlim(-radius / 2, radius / 2)
ax.set_ylim(-radius / 2, radius / 2)
for k in range(2):
axes[2].scatter(lines[:, k, 0], lines[:, k, 1], color='crimson')
axes[0].set_title('lines in circle and dots')
axes[1].set_title('lines till border')
axes[2].set_title('dots on circle')
plt.show()

Calculate center of mass of surface from set of x, y coordinates

I'm trying to determine the center of mass of a surface determined by a set of RANDOM non-equivalently spaced x, y points.
Here is a quick test set to show what I mean.
from scipy.spatial import ConvexHull
import numpy as np
import matplotlib.pyplot as plt
def PolyArea(x, y):
return 0.5*np.abs(np.dot(x, np.roll(y,1))-np.dot(y, np.roll(x,1)))
points = np.random.rand(30, 2) # 30 random points in 2-D
hull = ConvexHull(points)
plt.plot(points[:,0], points[:,1])
for simplex in hull.simplices:
plt.plot(points[simplex, 0], points[simplex, 1])
plt.plot(points[hull.vertices, 0], points[hull.vertices, 1], 'r--', lw=2)
plt.show()
From here we get:
x = points[hull.vertices, 0]
y = points[hull.vertices, 1]
surface_size = PolyArea(x, y)
I wish to determine the center of mass of the area from the points set (x, y), NOT the mean of the points. I know this to be calculated by a double integral of the surface (see: http://tutorial.math.lamar.edu/Classes/CalcII/CenterOfMass.aspx), but I don't know how to implement it in Python.
Thanks in advance.
There's surely a much more elegant solution, but here's a quick and dirty, probably slow and overkill, image based one which kind of works.
import skimage.measure
import skimage.draw
GRIDW = 1000
GRIDH = 1000
img = np.zeros((GRIDW, GRIDH))
rr, cc = skimage.draw.polygon(x*GRIDW,y*GRIDH)
img[rr,cc] = 1
label = skimage.measure.label(img)
rprops = skimage.measure.regionprops(label)
print rprops[0].centroid / np.asarray([GRIDW, GRIDH])

Ploting 2d projection of 3d cube with tricontourf in python

I have a cube of which I know the x,y,z positions of its vertices, I also have an array relating faces to vertices (the faces are composed of 2 triangles):
import numpy as np
x = np.array([ 0.16257299, -0.370805 , -1.09232295, 1.62570095,
-1.62570095, 1.09232295, 0.370805 , -0.16257299])
y = np.array([-1.71022499, -0.81153202, -0.52910602, -0.36958599,
0.369587 , 0.52910602, 0.81153202, 1.71022499])
z = np.array([ 0.22068501, -1.48456001, 1.23566902, 0.469576 ,
-0.469576 , -1.23566902, 1.48456001, -0.22068501])
faces = ([[3, 0, 1],[6, 7, 4],[3, 6, 2],[0, 2, 4],[1, 4, 7],[6, 3, 5],
[1, 5, 3],[4, 2, 6],[2, 0, 3],[4, 1, 0],[7, 5, 1],[5, 7, 6]])
I manage to plot the 3D visualization of the cube with the following:
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_trisurf(x,y,z, triangles = faces)
But what I would like is to plot the 2D projection of the cube on the Y axis, I tried the following:
valuesOfFaces = [5,10,9,1,2,3,7,8]
import matplotlib.pyplot as plt
%matplotlib notebook
fig, ax = plt.subplots()
ax.tricontourf(x,z,valuesOfFaces,triangles = faces,zdir='y',levels=np.sort(valuesOfFaces))
But it results in the following:
What I would like is to be able to color each face given a constant value and also that faces that are not visible do not appear. Is that possible with matplotlib ? If yes how would you suggest I proceed ?
You're lucky that I happen to have answered this question, Plot 3D convex closed regions in matplot lib,
recently. The approach can be quite similar. You first simplify the triangles into faces of the cube (this is done in the linked answer) and then just have to remove the faces which are hidden. Here the approach would be sort the faces by their center of mass along the viewing direction and remove the last 3 faces.
Finally projecting to 2D is done by removing the y dimension.
from scipy.spatial import ConvexHull
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import PolyCollection
import mpl_toolkits.mplot3d as a3
from mpl_toolkits.mplot3d import Axes3D
x = np.array([ 0.16257299, -0.370805 , -1.09232295, 1.62570095,
-1.62570095, 1.09232295, 0.370805 , -0.16257299])
y = np.array([-1.71022499, -0.81153202, -0.52910602, -0.36958599,
0.369587 , 0.52910602, 0.81153202, 1.71022499])
z = np.array([ 0.22068501, -1.48456001, 1.23566902, 0.469576 ,
-0.469576 , -1.23566902, 1.48456001, -0.22068501])
verts = np.c_[x,y,z]
hull = ConvexHull(verts)
simplices = hull.simplices
org_triangles = [verts[s] for s in simplices]
class Faces():
def __init__(self,tri, sig_dig=12, method="convexhull"):
self.method=method
self.tri = np.around(np.array(tri), sig_dig)
self.grpinx = list(range(len(tri)))
norms = np.around([self.norm(s) for s in self.tri], sig_dig)
_, self.inv = np.unique(norms,return_inverse=True, axis=0)
def norm(self,sq):
cr = np.cross(sq[2]-sq[0],sq[1]-sq[0])
return np.abs(cr/np.linalg.norm(cr))
def isneighbor(self, tr1,tr2):
a = np.concatenate((tr1,tr2), axis=0)
return len(a) == len(np.unique(a, axis=0))+2
def order(self, v):
if len(v) <= 3:
return v
v = np.unique(v, axis=0)
n = self.norm(v[:3])
y = np.cross(n,v[1]-v[0])
y = y/np.linalg.norm(y)
c = np.dot(v, np.c_[v[1]-v[0],y])
if self.method == "convexhull":
h = ConvexHull(c)
return v[h.vertices]
else:
mean = np.mean(c,axis=0)
d = c-mean
s = np.arctan2(d[:,0], d[:,1])
return v[np.argsort(s)]
def simplify(self):
for i, tri1 in enumerate(self.tri):
for j,tri2 in enumerate(self.tri):
if j > i:
if self.isneighbor(tri1,tri2) and \
self.inv[i]==self.inv[j]:
self.grpinx[j] = self.grpinx[i]
groups = []
for i in np.unique(self.grpinx):
u = self.tri[self.grpinx == i]
u = np.concatenate([d for d in u])
u = self.order(u)
groups.append(u)
return groups
def order_along_axis(self,faces,axis):
midpoints = np.array([f.mean(axis=0) for f in faces])
s = np.dot(np.array(axis),midpoints.T)
return np.argsort(s)
def remove_last_n(self, faces, order, n=1):
return np.array(faces)[order][::-1][n:][::-1]
f = Faces(org_triangles, sig_dig=4)
g = f.simplify()
order = f.order_along_axis(g, [0,1,0])
g = f.remove_last_n(g, order, 3)
# Reduce dimension, ommit y axis:
g2D = g[:,:,[0,2]]
fig = plt.figure(figsize=(8,3))
ax = fig.add_subplot(121, projection="3d")
ax2 = fig.add_subplot(122)
colors = np.random.rand(len(g),3)
pc = a3.art3d.Poly3DCollection(g, facecolors=colors,
edgecolor="k", alpha=0.9)
ax.add_collection3d(pc)
pc2 = PolyCollection(g2D, facecolors=colors,
edgecolor="k", alpha=0.9)
ax2.add_collection(pc2)
ax2.autoscale()
ax2.set_aspect("equal")
ax.set_xlim([-1.5,2])
ax.set_ylim([-1.5,2])
ax.set_zlim([-1.5,2])
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
plt.show()

heatmap with variable datapoint width

I want to plot the coefficients of a linear model over time.
On the y-axis you have the i-th feature of my model, on the x-axis is time and the value of the i-th coefficient is color coded.
In my example, the coefficients are constant from 0 to t1, t1 to t2 and so on. The intervals are not equally sized. Currently I circumvent this by creating many points spaced by delta t:
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
xi1 = [0, 1, 2]
t1 = range(4)
xi2 = [1, 1, 2]
t2 = range(5, 8)
data= np.vstack([xi1]*len(t1) + [xi2]*len(t2)).T
sns.heatmap(data)
Is there a way to do this more efficiently (without creating the redundant information)? I am also looking to have the right x-axis labels according to my t values.
You can use a matplotlib pcolormesh.
import matplotlib.pyplot as plt
import numpy as np
a = [[0,1],[1,1],[2,2]]
y = [0,1,2,3]
x = [0,5,8]
X,Y = np.meshgrid(x,y)
Z = np.array(a)
cmap = plt.get_cmap("RdPu", 3)
plt.pcolormesh(X,Y,Z, cmap=cmap)
plt.gca().invert_yaxis()
plt.colorbar(boundaries=np.arange(-0.5,3), ticks=np.unique(Z))
plt.show()

Return surface triangle of 3D scipy.spatial.Delaunay

I have this problem. I try to triangulate points cloud by scipy.spatial.Delaunay. I used:
tri = Delaunay(points) # points: np.array() of 3d points
indices = tri.simplices
vertices = points[indices]
But, this code return tetrahedron. How is it possible return triangle of surface only?
Thanks
To get it to work as in code form, you have to parametrize the surface to 2D. For example in the case of ball (r,theta, psi), radius is constant (drop it out) and points are given by (theta,psi) which is 2D.
Scipy Delaunay is N-dimensional triangulation, so if you give 3D points it returns 3D objects. Give it 2D points and it returns 2D objects.
Below is a script that I used to create polyhedra for openSCAD. U and V are my parametrization (x and y) and these are the coordinates that I give to Delaunay. Note that now the "Delaunay triangulation properties" apply only in u,v coordinates (angles are maximized in uv -space not xyz -space, etc).
The example is a modified copy from http://matplotlib.org/1.3.1/mpl_toolkits/mplot3d/tutorial.html which originally uses Triangulation function (maps to Delaunay eventually?)
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.tri as mtri
from scipy.spatial import Delaunay
# u, v are parameterisation variables
u = np.array([0,0,0.5,1,1])
v = np.array([0,1,0.5,0,1])
x = u
y = v
z = np.array([0,0,1,0,0])
# Triangulate parameter space to determine the triangles
#tri = mtri.Triangulation(u, v)
tri = Delaunay(np.array([u,v]).T)
print 'polyhedron(faces = ['
#for vert in tri.triangles:
for vert in tri.simplices:
print '[%d,%d,%d],' % (vert[0],vert[1],vert[2]),
print '], points = ['
for i in range(x.shape[0]):
print '[%f,%f,%f],' % (x[i], y[i], z[i]),
print ']);'
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')
# The triangles in parameter space determine which x, y, z points are
# connected by an edge
#ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap=plt.cm.Spectral)
ax.plot_trisurf(x, y, z, triangles=tri.simplices, cmap=plt.cm.Spectral)
plt.show()
Below is the (slightly more structured) text output:
polyhedron(
faces = [[2,1,0], [3,2,0], [4,2,3], [2,4,1], ],
points = [[0.000000,0.000000,0.000000],
[0.000000,1.000000,0.000000],
[0.500000,0.500000,1.000000],
[1.000000,0.000000,0.000000],
[1.000000,1.000000,0.000000], ]);
It looks like you want to compute the convex hull of your point cloud. I think this is what you want to do:
from scipy.spatial import ConvexHull
hull = ConvexHull(points)
indices = hull.simplices
vertices = points[indices]
Following Jaime's answer, but elaborating a bit more with an example:
import matplotlib as mpl
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d as a3
import numpy as np
import scipy as sp
from scipy import spatial as sp_spatial
def icosahedron():
h = 0.5*(1+np.sqrt(5))
p1 = np.array([[0, 1, h], [0, 1, -h], [0, -1, h], [0, -1, -h]])
p2 = p1[:, [1, 2, 0]]
p3 = p1[:, [2, 0, 1]]
return np.vstack((p1, p2, p3))
def cube():
points = np.array([
[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1],
[1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1],
])
return points
points = icosahedron()
# points = cube()
hull = sp_spatial.ConvexHull(points)
indices = hull.simplices
faces = points[indices]
print('area: ', hull.area)
print('volume: ', hull.volume)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.dist = 30
ax.azim = -140
ax.set_xlim([0, 2])
ax.set_ylim([0, 2])
ax.set_zlim([0, 2])
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
for f in faces:
face = a3.art3d.Poly3DCollection([f])
face.set_color(mpl.colors.rgb2hex(sp.rand(3)))
face.set_edgecolor('k')
face.set_alpha(0.5)
ax.add_collection3d(face)
plt.show()
Which should depict the following figure:

Categories

Resources