web-dev-qa-db-ja.com

matplotlibにプロットされた2つの曲線の間の領域を見つけます(fill_between area)

2つの曲線のx値とy値のリストがあり、どちらも奇妙な形状をしていますが、いずれの関数もありません。私は2つのことをする必要があります:(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)
16
F4R

面積の計算は、2つの曲線が交差しないブロックでは簡単です。これは、上記で指摘した台形です。それらが交差する場合は、x [i]とx [i + 1]の間に2つの三角形を作成し、2つの領域を追加する必要があります。直接実行する場合は、2つのケースを別々に処理する必要があります。これがあなたの問題を解決するための基本的な実用的な例です。まず、いくつかの偽のデータから始めます。

#!/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

さて、メインコード。プロットに基づくと、y1とy2が同じXポイントで定義されているように見えます。次に、次のように定義します。

z = y1-y2
dx = x[1:] - x[:-1]
cross_test = np.sign(z[:-1] * z[1:])

cross_testは、2つのグラフが交差するたびに負になります。これらのポイントで、クロスオーバーのx座標を計算します。簡単にするために、yのすべてのセグメントの交点のx座標を計算します。 2つの曲線が交差しない場所では、それらは役に立たない値になり、どこにも使用しません。これにより、コードが理解しやすくなります。

X1とx2にz1とz2があるとすると、z = 0となるようにx0を解きます。

# (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)
4
VBB

2つの曲線を、セグメントごとに線形である関数fおよびgとして定義します。 _x1_と_x2_の間、f(x) = f(x1) + ((x-x1)/(x2-x1))*(f(x2)-f(x1))h(x)=abs(g(x)-f(x))を定義します。次に、_scipy.integrate.quad_を使用してhを統合します。

そうすれば、交差点を気にする必要はありません。 ch41rmnが提案する「空中ブランコ加算」を自動的に行います。

5
JulienD

2つのデータセットが同じx座標のセットを共有するという意味で、データのセットは非常に「素晴らしい」ものです。したがって、一連の台形を使用して面積を計算できます。

例えば2つの関数をf(x)およびg(x)として定義すると、x内の任意の2つの連続するポイント間に、4つのデータポイントがあります。

(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の正しい順序を維持する必要があります。次に、単連結領域のリストをループして、台形の面積の合計を取得できます。

編集:

好奇心のために、2つのリストのx座標が異なる場合は、代わりに三角形を作成できます。例えば.

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

三角形間の重複は避ける必要があるため、交点を再度見つけて、順序付きリストに挿入する必要があります。三角形の各辺の長さはピタゴラスの公式を使用して計算でき、三角形の面積はヘロンの公式を使用して計算できます。

2
ch41rmn

私も同じ問題を抱えていました。以下の回答は、質問の作成者による試みに基づいています。ただし、shapelyは、ポリゴンの領域を直接紫色にすることはありません。コードを編集してコンポーネントポリゴンに分割し、それぞれの面積を取得する必要があります。その後-あなたは単にそれらを合計します。

2行の間の領域

以下の行を検討してください。

サンプル2行 以下のコードを実行すると、時計回りに反時計回りの面積が差し引かれるため、面積がゼロになります。

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)
0
Rubbani Boris