'How to calculte the area between a set of coordinates and a give axis?

let's say I have an array of 20 values, for example: [ 0. , -2., 3., -4., etc.] And if I plot them with seaborn lineplot with y = 0, y = 1 and y = -1, they would look like this:

plot

The question is, if I want to calculate the area of the triangle on the left top (the one above the green line), how would I do that in Python? Thanks in advance



Solution 1:[1]

This solution is for calculating area between your curve (given x-values and y-values) and y=1, you can change the code accordingly to your needs.

Create the arrays and plot them

To do this we first have to create our arrays (x, y) and check if the line connecting two given point intersect y=1. First of all we create the arrays:

import random

x = [i for i in range(20)]
y = [random.randint(-5, 10) for i in range(20)]

To plot these points and naming them we can use matplotlib (or whatever you want). We also add our y=1 line:

import matplotlib.pyplot as plt
import string

plt.plot(x, y)          
plt.axhline(y=1)        # Straight line
alphabet = string.ascii_lowercase       # naming points on plot
for i in range(len(x)):
    plt.scatter(x[i], y[i])
    plt.annotate(alphabet[i], (x[i], y[i]))
plt.show()

Find the intersection point

Now we have to find if y=1 intercept our line and the x-value of the point:

def intersect(y1, y2):
    '''
    Find if the line connecting two points intercept y=1
    '''
    if (y1 <= 1 and y2 >= 1) or (y2 <= 1 and y1 >= 1):
        return True


def slope_intercept(x1,y1,x2,y2):
    '''
    Find the slope and intercept for the given coordinates 
    and find x value if y=1
    '''
    m = (y2 - y1) / (x2 - x1)
    q = y1 - m * x1    
    point = (1-q)/m 
    return point


intercept = []          # x values of intercept point
for i in range(len(x)-1):
    if intersect(y[i], y[i+1]):
        print(f"{alphabet[i]}-{alphabet[i+1]} intersect")       # which segment intersect y=1
        point_x = round(slope_intercept(x[i],y[i],x[i+1],y[i+1]), 2)  #round our result to two decimal places
        print(f"Intercepted point is x={point_x}")
        intercept.append(point_x)

Find the area

Now that we have our intersection point, we can calculate area of the given polygon. For function we would have used Integral, but here we have straight lines, so we can use the trapezoidal rule. First of all we calculate area under our curve, than we subtract the area under y=1 for the given interval in order to get the area of the polygon:

from math import ceil, floor

def area_calc(interval):
    '''
    Find area of the given interval 
    using the following formula:
    ((x2 - x1) * (f(x2) + f(x1)) / 2
    '''
    tot = 0
    for i in range(len(interval)-1):      # Calculate area for every trapezium    
        h = round(interval[i+1] - interval[i], 2)
        if i == 0:
            b2 = abs(y[interval[i+1]])    # We need to calculate distances, so we use absolute values
            a = (h * (1 + b2)) / 2
            print(f"[{h=} * (1 + {b2=})] / 2 = {a}")
        elif i == len(interval) - 2:
            b1 = abs(y[interval[i]])
            a = (h * (1 + b1)) / 2
            print(f"[{h=} * ({b1=} + 1)] / 2 = {a}")
        else:
            b1 = abs(y[interval[i]])
            b2 = abs(y[interval[i+1]])
            a = (h * (b1 + b2)) / 2
            print(f"[{h=} * ({b1=} + {b2=})] / 2 = {a}")
        tot += abs(a)                         # Total area will be the sum of all trapezium's areas
    tot -= 1 * (interval[-1] - interval[0])   # Subtract area under y=1 for the given interval
    return round(tot, 2)


for i in range(len(intercept)-1):
    start = intercept[i]
    finish = intercept[i+1]
    interval = [z for z in range(ceil(start), floor(finish)+1)]     # Interval used for calculate area
    interval.insert(0, start)       # Insert intercept point at the beginning
    interval.append(finish)         # Insert last point at the end
    #area = area_calc(interval)
    area = area_calc(interval)
    print(f"{start=} {finish=}: {interval=}     {area=}\n")

Finally we get what we wanted!

Conclusion

It might seems difficult at the beginning, but we have to remember some basic calculus and analytical geometry. Of course you can use whatever array you want and calculate areas between your curve and any other curve now, just change some parts of the code.

I hope my explaination was clear and the code work as expected Have fun!

Final code:

from math import ceil, floor
import matplotlib.pyplot as plt
import string
import random 

def intersect(y1, y2):
    '''
    Find if the line connecting two points intercept y=1
    '''
    if (y1 <= 1 and y2 >= 1) or (y2 <= 1 and y1 >= 1):
        return True


def slope_intercept(x1,y1,x2,y2):
    '''
    Find the slope and intercept for the given coordinates 
    and find x value if y=1
    '''
    m = (y2 - y1) / (x2 - x1)
    q = y1 - m * x1    
    point = (1-q)/m 
    return point


def area_calc(interval):
    '''
    Find area of the given interval 
    using the following formula:
    ((x2 - x1) * (f(x2) + f(x1)) / 2
    '''
    tot = 0
    for i in range(len(interval)-1):
        h = round(interval[i+1] - interval[i], 2)
        if i == 0:
            b2 = abs(y[interval[i+1]])
            a = (h * (1 + b2)) / 2
            print(f"[{h=} * (1 + {b2=})] / 2 = {a}")
        elif i == len(interval) - 2:
            b1 = abs(y[interval[i]])
            a = (h * (1 + b1)) / 2
            print(f"[{h=} * ({b1=} + 1)] / 2 = {a}")
        else:
            b1 = abs(y[interval[i]])
            b2 = abs(y[interval[i+1]])
            a = (h * (b1 + b2)) / 2
            print(f"[{h=} * ({b1=} + {b2=})] / 2 = {a}")
        tot += abs(a)
    tot -= 1 * (interval[-1] - interval[0])
    return round(tot, 2)


x = [i for i in range(20)]
y = [random.randint(-5, 10) for i in range(20)]

plt.plot(x, y)          
plt.axhline(y=1)        # Straight line
alphabet = string.ascii_lowercase       # naming points on plot
intercept = []          # x values of intercept point
for i in range(len(x)):
    plt.scatter(x[i], y[i])
    plt.annotate(alphabet[i], (x[i], y[i]))
for i in range(len(x)-1):
    if intersect(y[i], y[i+1]):
        print(f"{alphabet[i]}-{alphabet[i+1]} intersect")       # which segment intersect y=1
        point_x = round(slope_intercept(x[i],y[i],x[i+1],y[i+1]), 2)
        print(f"Intercepted point is x={point_x}")
        intercept.append(point_x)

print()

for i in range(len(intercept)-1):
    start = intercept[i]
    finish = intercept[i+1]
    interval = [z for z in range(ceil(start), floor(finish)+1)]     # Interval used for calculate area
    interval.insert(0, start)       # Insert intercept point at the beginning
    interval.append(finish)         # Insert last point at the end
    #area = area_calc(interval)
    area = area_calc(interval)
    print(f"{start=} {finish=}: {interval=}     {area=}\n")

plt.show()

Simulation case Simulation results:

a-b intersect
Intercepted point is x=0.83
c-d intersect
Intercepted point is x=2.62
d-e intersect
Intercepted point is x=3.36
e-f intersect
Intercepted point is x=4.75
g-h intersect
Intercepted point is x=6.5
i-j intersect
Intercepted point is x=8.43
j-k intersect
Intercepted point is x=9.31
l-m intersect
Intercepted point is x=11.33
m-n intersect
Intercepted point is x=12.18
n-o intersect
Intercepted point is x=13.6
o-p intersect
Intercepted point is x=14.55
q-r intersect
Intercepted point is x=16.57
r-s intersect
Intercepted point is x=17.5
s-t intersect
Intercepted point is x=18.75

[h=0.17 * (1 + b2=2)] / 2 = 0.255
[h=1 * (b1=2 + b2=9)] / 2 = 5.5
[h=0.62 * (b1=9 + 1)] / 2 = 3.1
start=0.83 finish=2.62: interval=[0.83, 1, 2, 2.62]     area=7.07

[h=0.38 * (1 + b2=4)] / 2 = 0.95
[h=0.36 * (b1=4 + 1)] / 2 = 0.8999999999999999
start=2.62 finish=3.36: interval=[2.62, 3, 3.36]     area=1.11

[h=0.64 * (1 + b2=10)] / 2 = 3.52
[h=0.75 * (b1=10 + 1)] / 2 = 4.125
start=3.36 finish=4.75: interval=[3.36, 4, 4.75]     area=6.25

[h=0.25 * (1 + b2=2)] / 2 = 0.375
[h=1 * (b1=2 + b2=2)] / 2 = 2.0
[h=0.5 * (b1=2 + 1)] / 2 = 0.75
start=4.75 finish=6.5: interval=[4.75, 5, 6, 6.5]     area=1.38

[h=0.5 * (1 + b2=4)] / 2 = 1.25
[h=1 * (b1=4 + b2=4)] / 2 = 4.0
[h=0.43 * (b1=4 + 1)] / 2 = 1.075
start=6.5 finish=8.43: interval=[6.5, 7, 8, 8.43]     area=4.4

[h=0.57 * (1 + b2=3)] / 2 = 1.14
[h=0.31 * (b1=3 + 1)] / 2 = 0.62
start=8.43 finish=9.31: interval=[8.43, 9, 9.31]     area=0.88

[h=0.69 * (1 + b2=10)] / 2 = 3.795
[h=1 * (b1=10 + b2=2)] / 2 = 6.0
[h=0.33 * (b1=2 + 1)] / 2 = 0.495
start=9.31 finish=11.33: interval=[9.31, 10, 11, 11.33]     area=8.27

[h=0.67 * (1 + b2=1)] / 2 = 0.67
[h=0.18 * (b1=1 + 1)] / 2 = 0.18
start=11.33 finish=12.18: interval=[11.33, 12, 12.18]     area=0.0

[h=0.82 * (1 + b2=10)] / 2 = 4.51
[h=0.6 * (b1=10 + 1)] / 2 = 3.3
start=12.18 finish=13.6: interval=[12.18, 13, 13.6]     area=6.39

[h=0.4 * (1 + b2=5)] / 2 = 1.2000000000000002
[h=0.55 * (b1=5 + 1)] / 2 = 1.6500000000000001
start=13.6 finish=14.55: interval=[13.6, 14, 14.55]     area=1.9

[h=0.45 * (1 + b2=6)] / 2 = 1.575
[h=1 * (b1=6 + b2=9)] / 2 = 7.5
[h=0.57 * (b1=9 + 1)] / 2 = 2.8499999999999996
start=14.55 finish=16.57: interval=[14.55, 15, 16, 16.57]     area=9.9

[h=0.43 * (1 + b2=5)] / 2 = 1.29
[h=0.5 * (b1=5 + 1)] / 2 = 1.5
start=16.57 finish=17.5: interval=[16.57, 17, 17.5]     area=1.86

[h=0.5 * (1 + b2=7)] / 2 = 2.0
[h=0.75 * (b1=7 + 1)] / 2 = 3.0
start=17.5 finish=18.75: interval=[17.5, 18, 18.75]     area=3.75

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Milziade