Объем ячейки Вороного (питона)
Я использую Scipy 0.13.0 в Python 2.7 для вычисления набора ячеек Вороного в 3d. Мне нужно получить объем каждой ячейки для (де) взвешивания выходного сигнала проприетарной симуляции. Есть ли какой-нибудь простой способ сделать это - конечно, это общая проблема или обычное использование ячеек Вороного, но я ничего не могу найти. Следующий код запускается и сбрасывает все, что знает руководство scipy.spatial.Voronoi.
from scipy.spatial import Voronoi
x=[0,1,0,1,0,1,0,1,0,1]
y=[0,0,1,1,2,2,3,3.5,4,4.5]
z=[0,0,0,0,0,1,1,1,1,1]
points=zip(x,y,z)
print points
vor=Voronoi(points)
print vor.regions
print vor.vertices
print vor.ridge_points
print vor.ridge_vertices
print vor.points
print vor.point_region
1 ответ
Я думаю, что взломал это. Мой подход ниже:
- Для каждого региона диаграммы Вороного
- выполнить триангуляцию Делоне по вершинам области
- это вернет набор неправильных тетраэдров, которые заполняют область
- Объем тетраэдра можно легко рассчитать (википедия)
- Суммируйте эти объемы, чтобы получить объем региона.
Я уверен, что будут и ошибки и плохое кодирование - я буду искать первое, комментарии приветствуются ко второму - тем более, что я довольно плохо знаком с Python. Я все еще проверяю несколько вещей - иногда дается индекс вершины -1, который согласно руководству scipy "указывает вершину вне диаграммы Вороного", но кроме того, вершины генерируются с координатами, выходящими далеко за пределы исходных данных (вставить numpy.random.seed(42)
и проверьте координаты области для точки 7, они идут к ~(7,-14,6), точка 49 аналогична. Поэтому мне нужно выяснить, почему иногда это происходит, а иногда я получаю индекс -1.
from scipy.spatial import Voronoi,Delaunay
import numpy as np
import matplotlib.pyplot as plt
def tetravol(a,b,c,d):
'''Calculates the volume of a tetrahedron, given vertices a,b,c and d (triplets)'''
tetravol=abs(np.dot((a-d),np.cross((b-d),(c-d))))/6
return tetravol
def vol(vor,p):
'''Calculate volume of 3d Voronoi cell based on point p. Voronoi diagram is passed in v.'''
dpoints=[]
vol=0
for v in vor.regions[vor.point_region[p]]:
dpoints.append(list(vor.vertices[v]))
tri=Delaunay(np.array(dpoints))
for simplex in tri.simplices:
vol+=tetravol(np.array(dpoints[simplex[0]]),np.array(dpoints[simplex[1]]),np.array(dpoints[simplex[2]]),np.array(dpoints[simplex[3]]))
return vol
x= [np.random.random() for i in xrange(50)]
y= [np.random.random() for i in xrange(50)]
z= [np.random.random() for i in xrange(50)]
dpoints=[]
points=zip(x,y,z)
vor=Voronoi(points)
vtot=0
for i,p in enumerate(vor.points):
out=False
for v in vor.regions[vor.point_region[i]]:
if v<=-1: #a point index of -1 is returned if the vertex is outside the Vornoi diagram, in this application these should be ignorable edge-cases
out=True
else:
if not out:
pvol=vol(vor,i)
vtot+=pvol
print "point "+str(i)+" with coordinates "+str(p)+" has volume "+str(pvol)
print "total volume= "+str(vtot)
#oddly, some vertices outside the boundary of the original data are returned, meaning that the total volume can be greater than the volume of the original.
Как уже упоминалось в комментариях, вы можете вычислить ConvexHull каждой ячейки Вороного. Поскольку клетки Вороного выпуклые, вы получите нужные объемы.
def voronoi_volumes(points):
v = Voronoi(points)
vol = np.zeros(v.npoints)
for i, reg_num in enumerate(v.point_region):
indices = v.regions[reg_num]
if -1 in indices: # some regions can be opened
vol[i] = np.inf
else:
vol[i] = ConvexHull(v.vertices[indices]).volume
return vol
Этот метод работает в любых измерениях