找到matplotlib中绘制的两条曲线之间的区域(fill_between区域)

时间:2014-08-22 03:49:18

标签: python matplotlib scipy area

我有两条曲线的x和y值列表,两条曲线都有奇怪的形状,我没有任何一个函数。我需要做两件事:(1)绘制它并遮蔽曲线之间的区域,如下图所示; (2)找出曲线之间阴影区域的总面积。

我能够在matplotlib中用fill_between和fill_betweenx绘制和遮蔽这些曲线之间的区域,但我不知道如何计算它们之间的确切区域,特别是因为我没有任何函数那些曲线。

有什么想法吗?

我到处寻找,找不到一个简单的解决方案。我非常绝望,所以非常感谢任何帮助。

非常感谢!

Area between curves - how to calculate the area of the shaded region without curve's function?


编辑:为了将来的参考(如果有人遇到同样的问题),这就是我解决这个问题的方法(几个月后):将每条曲线的第一个和最后一个节点/点连接在一起,产生一个巨大的奇怪形状的多边形,然后使用shapely自动计算多边形的面积,这是曲线之间的确切区域,无论它们走向何方或它们是多么非线性。像魅力一样,已经成千上万的曲线。 :)

这是我的代码:

from shapely.geometry import Polygon

x_y_curve1 = [(0.121,0.232),(2.898,4.554),(7.865,9.987)] #these are your points for curve 1 (I just put some random numbers)
x_y_curve2 = [(1.221,1.232),(3.898,5.554),(8.865,7.987)] #these are your points for curve 2 (I just put some random numbers)

polygon_points = [] #creates a empty list where we will append the points to create the polygon

for xyvalue in x_y_curve1:
    polygon_points.append([xyvalue[0],xyvalue[1]]) #append all xy points for curve 1

for xyvalue in x_y_curve2[::-1]:
    polygon_points.append([xyvalue[0],xyvalue[1]]) #append all xy points for curve 2 in the reverse order (from last point to first point)

for xyvalue in x_y_curve1[0:1]:
    polygon_points.append([xyvalue[0],xyvalue[1]]) #append the first point in curve 1 again, to it "closes" the polygon

polygon = Polygon(polygon_points)
area = polygon.area
print(area)

5 个答案:

答案 0 :(得分:5)

将两条曲线定义为按分段线性的函数fg,例如在x1x2之间,f(x) = f(x1) + ((x-x1)/(x2-x1))*(f(x2)-f(x1))。 定义h(x)=abs(g(x)-f(x))。然后使用scipy.integrate.quad来集成h。

这样你就不需要为交叉点烦恼了。它将自动执行ch41rmn建议的“空中总结”。

答案 1 :(得分:3)

在两条曲线不相交的区块中,区域计算很简单:就像上面指出的梯形一样。如果它们相交,则在x [i]和x [i + 1]之间创建两个三角形,并且应该添加两个区域。如果您想直接执行此操作,则应分别处理这两种情况。这是解决问题的基本工作示例。首先,我将从一些假数据开始:

#!/usr/bin/python
import numpy as np

# let us generate fake test data
x = np.arange(10)
y1 = np.random.rand(10) * 20
y2 = np.random.rand(10) * 20

现在,主要代码。根据您的情节,看起来您在相同的X点定义了y1和y2。然后我们定义,

z = y1-y2
dx = x[1:] - x[:-1]
cross_test = np.sign(z[:-1] * z[1:])
每当两个图交叉时,

cross_test将为负。在这些点上,我们想要计算交叉的x坐标。为简单起见,我将计算y的所有段的交点的x坐标。对于两条曲线不相交的地方,它们将是无用的值,我们不会在任何地方使用它们。这只是让代码更容易理解。

假设在x1和x2处有z1和z2,那么我们求x0使得z = 0:

# (z2 - z1)/(x2 - x1) = (z0 - z1) / (x0 - x1) = -z1/(x0 - x1)
# x0 = x1 - (x2 - x1) / (z2 - z1) * z1
x_intersect = x[:-1] - dx / (z[1:] - z[:-1]) * z[:-1]
dx_intersect = - dx / (z[1:] - z[:-1]) * z[:-1]

曲线不相交的地方,区域简单地由下式给出:

areas_pos = abs(z[:-1] + z[1:]) * 0.5 * dx # signs of both z are same

在它们相交的地方,我们添加了两个三角形的区域:

areas_neg = 0.5 * dx_intersect * abs(z[:-1]) + 0.5 * (dx - dx_intersect) * abs(z[1:])

现在,要选择每个块x [i]到x [i + 1]的区域,我使用np.where:

areas = np.where(cross_test < 0, areas_neg, areas_pos)
total_area = np.sum(areas)

这是你想要的答案。如上所述,如果两个y图定义在不同的x点,这将变得更复杂。如果你想测试它,你可以简单地绘制它(在我的测试用例中,y范围将是-20到20)

negatives = np.where(cross_test < 0)
positives = np.where(cross_test >= 0)
plot(x, y1)
plot(x, y2)
plot(x, z)
plt.vlines(x_intersect[negatives], -20, 20)

答案 2 :(得分:2)

在两组数据共享同一组x坐标的意义上,您的数据集非常“漂亮”。因此,您可以使用一系列梯形来计算面积。

e.g。将两个函数定义为f(x)和g(x),然后,在x中的任意两个连续点之间,您有四个数据点:

(x1, f(x1))-->(x2, f(x2))
(x1, g(x1))-->(x2, g(x2))

然后,梯形的区域是

A(x1-->x2) = ( f(x1)-g(x1) + f(x2)-g(x2) ) * (x2-x1)/2                         (1)

出现了一个复杂因素,即等式(1)仅适用于简单连接的区域,即在该区域内不得有交叉:

|\             |\/|
|_|     vs     |/\|

交叉路口两侧的区域必须单独评估。您需要浏览数据以查找所有交叉点,然后将其坐标插入坐标列表中。必须保持x的正确顺序。然后,您可以遍历简单连接区域列表并获得梯形区域的总和。

修改

为了好奇,如果两个列表的x坐标不同,你可以改为构造三角形。 e.g。

.____.
|   / \
|  /   \
| /     \
|/       \
._________.

必须避免三角形之间的重叠,因此您需要再次找到交叉点并将它们插入到有序列表中。可以使用毕达哥拉斯公式计算三角形每边的长度,并使用Heron公式计算三角形的面积。

答案 3 :(得分:0)

我遇到了同样的问题。以下答案基于问题作者的尝试。但是,shape不会直接以紫色给出多边形的面积。您需要编辑代码以将其分解为组件多边形,然后获取每个组件的面积。之后,您只需将它们加起来即可。

Area Between two lines

请考虑以下几行:

Sample Two lines 如果您在下面运行代码,则面积为零,因为它采用顺时针方向并减去逆时针面积:

from shapely.geometry import Polygon

x_y_curve1 = [(1,1),(2,1),(3,3),(4,3)] #these are your points for curve 1 
x_y_curve2 = [(1,3),(2,3),(3,1),(4,1)] #these are your points for curve 2 

polygon_points = [] #creates a empty list where we will append the points to create the polygon

for xyvalue in x_y_curve1:
    polygon_points.append([xyvalue[0],xyvalue[1]]) #append all xy points for curve 1

for xyvalue in x_y_curve2[::-1]:
    polygon_points.append([xyvalue[0],xyvalue[1]]) #append all xy points for curve 2 in the reverse order (from last point to first point)

for xyvalue in x_y_curve1[0:1]:
    polygon_points.append([xyvalue[0],xyvalue[1]]) #append the first point in curve 1 again, to it "closes" the polygon

polygon = Polygon(polygon_points)
area = polygon.area
print(area)

因此,解决方案是根据直线相交的位置将多边形分成较小的部分。然后使用for循环将它们加起来:

from shapely.geometry import Polygon

x_y_curve1 = [(1,1),(2,1),(3,3),(4,3)] #these are your points for curve 1 
x_y_curve2 = [(1,3),(2,3),(3,1),(4,1)] #these are your points for curve 2 

polygon_points = [] #creates a empty list where we will append the points to create the polygon

for xyvalue in x_y_curve1:
    polygon_points.append([xyvalue[0],xyvalue[1]]) #append all xy points for curve 1

for xyvalue in x_y_curve2[::-1]:
    polygon_points.append([xyvalue[0],xyvalue[1]]) #append all xy points for curve 2 in the reverse order (from last point to first point)

for xyvalue in x_y_curve1[0:1]:
    polygon_points.append([xyvalue[0],xyvalue[1]]) #append the first point in curve 1 again, to it "closes" the polygon

polygon = Polygon(polygon_points)
area = polygon.area

x,y = polygon.exterior.xy
    # original data
ls = LineString(np.c_[x, y])
    # closed, non-simple
lr = LineString(ls.coords[:] + ls.coords[0:1])
lr.is_simple  # False
mls = unary_union(lr)
mls.geom_type  # MultiLineString'

Area_cal =[]

for polygon in polygonize(mls):
    Area_cal.append(polygon.area)
    Area_poly = (np.asarray(Area_cal).sum())
print(Area_poly)

答案 4 :(得分:0)

pypi库similaritymeasures(于2018年发布)中的area_between_two_curves函数可能会为您提供所需的内容。我尝试了一个简单的示例,比较了函数和常量值之间的面积,并非常接近Excel(在2%以内)。不知道为什么它不能给我100%的回扣,也许我做错了。值得考虑。