【问题标题】:Integrating 2D data over an irregular grid in python在 python 中在不规则网格上集成 2D 数据
【发布时间】:2019-07-28 06:58:40
【问题描述】:

所以我有一个在域上不规则采样的二维函数,我想计算表面下的体积。数据按照[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 构造一个二维插值函数,然后使用dblquad 对其进行积分。事实证明,这不仅不优雅而且速度慢,而且还会抛出错误:

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

有没有更好的方法来整合以这种方式排列的数据或克服这个错误?

【问题讨论】:

  • 您似乎正在尝试为不精确的(采样)数据拟合精确的解决方案,而 scipy 正在递归寻找精确的解决方案。老实说,我的第一种方法是使用interp2d(kind='linear') 并使用固定的 dx、dy 重新采样线性网格,然后将其相加,看看是否能获得所需的精度。
  • 我认为这可能有问题,因为在我要采样的函数的起源处有一个非常尖锐的特征(几乎是奇异的)。我希望,只要插值函数是用具有足够高分辨率的数据构建的,那么dblquad 就可以有效地对其进行采样。修复网格步骤需要非常小的 dx 和 dy。
  • 您是否总是知道尖锐特征的位置以便更彻底地对其进行采样?
  • 是的,它应该始终在 (0,0) 左右
  • 我的下一个想法是使用 x,y 点生成一个 voronoi 图,以获得sum(z/area) 的每个样本下方的区域,尽管我不太确定如何处理图的边缘scipy.spatial.Voronoi... 正在努力...

标签: python numpy scipy integration


【解决方案1】:

如果您可以在感兴趣的特征周围以足够高的分辨率对数据进行采样,那么在其他任何地方都更加稀疏,那么问题定义就变成了如何定义每个样本下的区域。对于常规的矩形样本,这很容易,并且可能以围绕原点的分辨率增量逐步完成。我采用的方法是为每个样本生成 2D Voronoi 细胞以确定它们的面积。我从this 答案中提取了大部分代码,因为它已经拥有几乎所有需要的组件。

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)

我还没有完全测试过这个,但原理应该有效,并且数学检查出来了。

【讨论】:

  • +1 以获得优雅的解决方案。但是,我在运行您的示例代码时收到错误ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()。你有类似的问题吗?
  • 我不确定我是否完全理解这里发生了什么。为什么需要生成列表points_left 等?我可以不直接从样本点计算 Voronoi 图吗?
  • @Jiles 关于错误:我实际上并没有真正测试过这个,我认为它在线:points_center = xy[np.where((bbox[0] &lt;= xy[:,0] ...?您可能需要稍微编辑一下语法。基本上只是检查以消除所需边界框之外的点。
  • 代码的一般思想是 voronoi 单元定义了一个点周围的区域,该点是最近的点。根据定义,周界上的区域延伸到无穷大,因此为了限制区域,我们使用对称技巧将区域限制在正方形中。
  • 使用对称来限制区域的想法非常聪明,我没有参与其中;P
猜你喜欢
  • 2018-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-03
  • 2011-04-21
  • 2011-08-02
  • 1970-01-01
  • 2014-05-12
相关资源
最近更新 更多