简体   繁体   English

在python中通过不规则网格集成2D数据

[英]Integrating 2D data over an irregular grid in python

So I have 2D function which is sampled irregularly over a domain, and I want to calculate the volume underneath the surface. 因此,我具有在一个域中不规则采样的2D函数,并且我想计算表面下方的体积。 The data is organised in terms of [x,y,z] , taking a simple example: 数据以[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 

where np.unique sorts the data in the first column then the second. 其中np.unique对第一列中的数据进行排序,然后对第二列中的数据进行排序。 The data is arranged in this way as I need to sample more heavily around the origin as there is a sharp feature that must be resolved. 数据是以这种方式排列的,因为我需要围绕原点进行更多采样,因为必须解决一个尖锐的特征。

Now I wondered about constructing a 2D interpolating function using scipy.interpolate.interp2d , then integrating over this using dblquad . 现在,我想知道是使用构建二维插值功能scipy.interpolate.interp2d ,然后用这个积分, dblquad As it turns out, this is not only inelegant and slow, but also kicks out the error: 事实证明,这不仅笨拙而且缓慢,而且还会引发错误:

RuntimeWarning: No more knots can be added because the number of B-spline
coefficients already exceeds the number of data points m. 

Is there a better way to integrate data arranged in this fashion or overcoming this error? 是否有更好的方法来集成以这种方式排列或克服此错误的数据?

If you can sample the data with high enough resolution around the feature of interest, then more sparsely everywhere else, the problem definition then becomes how to define the area under each sample. 如果您可以围绕感兴趣的特征以足够高的分辨率对数据进行采样,然后在其他地方进行稀疏采样,那么问题定义就变成了如何定义每个采样下的区域。 This is easy with regular rectangular samples, and could likely be done stepwise in increments of resolution around the origin. 对于常规的矩形样本,此操作很容易,并且可能以原点附近的分辨率为增量逐步进行。 The approach I went after is to generate the 2D Voronoi cells for each sample in order to determine their area. 我追求的方法是为每个样本生成2维Voronoi单元,以确定它们的面积。 I pulled most of the code from this answer, as it had almost all the components needed already. 我把大部分的代码从这个答案,因为它几乎都已经所需的组件。

import numpy as np
from scipy.spatial import Voronoi

#taken from: # https://stackoverflow.com/questions/28665491/getting-a-bounded-polygon-coordinates-from-voronoi-cells
#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: https://stackoverflow.com/questions/28665491/getting-a-bounded-polygon-coordinates-from-voronoi-cells
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)

I haven't exactly tested this, but the principle should work, and the math checks out. 我还没有完全测试过,但是原理应该起作用,并且数学运算也可以完成。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM