在 python 中的不规则网格上集成二维数据
Integrating 2D data over an irregular grid in python
所以我有一个 2D 函数,它在一个域上不规则地采样,我想计算表面下的体积。数据按照[x,y,z]
组织,举个简单的例子:
def f(x,y):
return np.cos(10*x*y) * np.exp(-x**2 - y**2)
datrange1 = np.linspace(-5,5,1000)
datrange2 = np.linspace(-0.5,0.5,1000)
ar = []
for x in datrange1:
for y in datrange2:
ar += [[x,y, f(x,y)]]
for x in xrange2:
for y in yrange2:
ar += [[x,y, f(x,y)]]
val_arr1 = np.array(ar)
data = np.unique(val_arr1)
xlist, ylist, zlist = data.T
其中 np.unique
对第一列中的数据进行排序,然后对第二列中的数据进行排序。数据以这种方式排列,因为我需要在原点周围进行更多采样,因为必须解决一个尖锐的特征。
现在我想知道如何使用 scipy.interpolate.interp2d
构造一个 2D 插值函数,然后使用 dblquad
对其进行积分。事实证明,这不仅不优雅和缓慢,而且还踢出了错误:
RuntimeWarning: No more knots can be added because the number of B-spline
coefficients already exceeds the number of data points m.
有没有更好的方法来整合以这种方式排列的数据或克服这个错误?
如果您可以在感兴趣的特征周围以足够高的分辨率对数据进行采样,然后在其他地方更稀疏,那么问题定义就变成了如何定义每个样本下的区域。这对于常规的矩形样本来说很容易,并且可以在原点周围以分辨率的增量逐步完成。我采用的方法是为每个样本生成 2D Voronoi 单元以确定它们的面积。我从 答案中提取了大部分代码,因为它已经包含了几乎所有需要的组件。
import numpy as np
from scipy.spatial import Voronoi
#taken from: #
#computes voronoi regions bounded by a bounding box
def square_voronoi(xy, bbox): #bbox: (min_x, max_x, min_y, max_y)
# Select points inside the bounding box
points_center = xy[np.where((bbox[0] <= xy[:,0]) * (xy[:,0] <= bbox[1]) * (bbox[2] <= xy[:,1]) * (bbox[2] <= bbox[3]))]
# Mirror points
points_left = np.copy(points_center)
points_left[:, 0] = bbox[0] - (points_left[:, 0] - bbox[0])
points_right = np.copy(points_center)
points_right[:, 0] = bbox[1] + (bbox[1] - points_right[:, 0])
points_down = np.copy(points_center)
points_down[:, 1] = bbox[2] - (points_down[:, 1] - bbox[2])
points_up = np.copy(points_center)
points_up[:, 1] = bbox[3] + (bbox[3] - points_up[:, 1])
points = np.concatenate((points_center, points_left, points_right, points_down, points_up,), axis=0)
# Compute Voronoi
vor = Voronoi(points)
# Filter regions (center points should* be guaranteed to have a valid region)
# center points should come first and not change in size
regions = [vor.regions[vor.point_region[i]] for i in range(len(points_center))]
vor.filtered_points = points_center
vor.filtered_regions = regions
return vor
#also stolen from:
def area_region(vertices):
# Polygon's signed area
A = 0
for i in range(0, len(vertices) - 1):
s = (vertices[i, 0] * vertices[i + 1, 1] - vertices[i + 1, 0] * vertices[i, 1])
A = A + s
return np.abs(0.5 * A)
def f(x,y):
return np.cos(10*x*y) * np.exp(-x**2 - y**2)
#sampling could easily be shaped to sample origin more heavily
sample_x = np.random.rand(1000) * 10 - 5 #same range as example linspace
sample_y = np.random.rand(1000) - .5
sample_xy = np.array([sample_x, sample_y]).T
vor = square_voronoi(sample_xy, (-5,5,-.5,.5)) #using bbox from samples
points = vor.filtered_points
sample_areas = np.array([area_region(vor.vertices[verts+[verts[0]],:]) for verts in vor.filtered_regions])
sample_z = np.array([f(p[0], p[1]) for p in points])
volume = np.sum(sample_z * sample_areas)
我还没有完全测试过这个,但是这个原理应该可行,并且数学检查出来了。
所以我有一个 2D 函数,它在一个域上不规则地采样,我想计算表面下的体积。数据按照[x,y,z]
组织,举个简单的例子:
def f(x,y):
return np.cos(10*x*y) * np.exp(-x**2 - y**2)
datrange1 = np.linspace(-5,5,1000)
datrange2 = np.linspace(-0.5,0.5,1000)
ar = []
for x in datrange1:
for y in datrange2:
ar += [[x,y, f(x,y)]]
for x in xrange2:
for y in yrange2:
ar += [[x,y, f(x,y)]]
val_arr1 = np.array(ar)
data = np.unique(val_arr1)
xlist, ylist, zlist = data.T
其中 np.unique
对第一列中的数据进行排序,然后对第二列中的数据进行排序。数据以这种方式排列,因为我需要在原点周围进行更多采样,因为必须解决一个尖锐的特征。
现在我想知道如何使用 scipy.interpolate.interp2d
构造一个 2D 插值函数,然后使用 dblquad
对其进行积分。事实证明,这不仅不优雅和缓慢,而且还踢出了错误:
RuntimeWarning: No more knots can be added because the number of B-spline
coefficients already exceeds the number of data points m.
有没有更好的方法来整合以这种方式排列的数据或克服这个错误?
如果您可以在感兴趣的特征周围以足够高的分辨率对数据进行采样,然后在其他地方更稀疏,那么问题定义就变成了如何定义每个样本下的区域。这对于常规的矩形样本来说很容易,并且可以在原点周围以分辨率的增量逐步完成。我采用的方法是为每个样本生成 2D Voronoi 单元以确定它们的面积。我从
import numpy as np
from scipy.spatial import Voronoi
#taken from: #
#computes voronoi regions bounded by a bounding box
def square_voronoi(xy, bbox): #bbox: (min_x, max_x, min_y, max_y)
# Select points inside the bounding box
points_center = xy[np.where((bbox[0] <= xy[:,0]) * (xy[:,0] <= bbox[1]) * (bbox[2] <= xy[:,1]) * (bbox[2] <= bbox[3]))]
# Mirror points
points_left = np.copy(points_center)
points_left[:, 0] = bbox[0] - (points_left[:, 0] - bbox[0])
points_right = np.copy(points_center)
points_right[:, 0] = bbox[1] + (bbox[1] - points_right[:, 0])
points_down = np.copy(points_center)
points_down[:, 1] = bbox[2] - (points_down[:, 1] - bbox[2])
points_up = np.copy(points_center)
points_up[:, 1] = bbox[3] + (bbox[3] - points_up[:, 1])
points = np.concatenate((points_center, points_left, points_right, points_down, points_up,), axis=0)
# Compute Voronoi
vor = Voronoi(points)
# Filter regions (center points should* be guaranteed to have a valid region)
# center points should come first and not change in size
regions = [vor.regions[vor.point_region[i]] for i in range(len(points_center))]
vor.filtered_points = points_center
vor.filtered_regions = regions
return vor
#also stolen from:
def area_region(vertices):
# Polygon's signed area
A = 0
for i in range(0, len(vertices) - 1):
s = (vertices[i, 0] * vertices[i + 1, 1] - vertices[i + 1, 0] * vertices[i, 1])
A = A + s
return np.abs(0.5 * A)
def f(x,y):
return np.cos(10*x*y) * np.exp(-x**2 - y**2)
#sampling could easily be shaped to sample origin more heavily
sample_x = np.random.rand(1000) * 10 - 5 #same range as example linspace
sample_y = np.random.rand(1000) - .5
sample_xy = np.array([sample_x, sample_y]).T
vor = square_voronoi(sample_xy, (-5,5,-.5,.5)) #using bbox from samples
points = vor.filtered_points
sample_areas = np.array([area_region(vor.vertices[verts+[verts[0]],:]) for verts in vor.filtered_regions])
sample_z = np.array([f(p[0], p[1]) for p in points])
volume = np.sum(sample_z * sample_areas)
我还没有完全测试过这个,但是这个原理应该可行,并且数学检查出来了。