1

I'm having trouble to get the positive area (above y=0). Mathematically the area should be 1.125.

x=np.arange(1,5,1)

y=np.array([-1.5,-0.5,0.5,1.5])

But none of the function below gives me the answer. The first one is 0. The second one gives me 1.25 which doesn't interpolate to calculate the area. I need to get the answer 1.125. Can anyone help? Thanks!

np.trapz(y,x)

np.trapz(y.clip(min=0),x)

chart

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264

1 Answers1

1

To do this, you have to find where the linear interpolation of your data crosses the x axis. This function is a variation of a function that I included in another answer (Digitizing an analog signal):

def find_zero_crossings(t, y):
    """
    Given the input signal `y` with samples at times `t`,
    find the times where the linearly interpolated graph
    crosses 0.

    `t` and `y` must be 1-D numpy arrays.
    """
    transition_indices = np.where((np.sign(y[:-1]) * np.sign(y[1:])) == -1)[0]

    # Linearly interpolate the time values where the transition occurs.
    t0 = t[transition_indices]
    t1 = t[transition_indices + 1]
    y0 = y[transition_indices]
    y1 = y[transition_indices + 1]
    slope = (y1 - y0) / (t1 - t0)
    transition_times = t0 - y0 / slope

    return transition_times

You can use this for your example in a script such as this:

xx = np.arange(1, 5, 1)
yy = np.array([-1.5, -0.5, 0.5, 1.5])

xz = find_zero_crossings(xx, yy)
print("xz:", xz)

# Insert the x intercepts into the data.
xx2 = np.append(xx, xz)
yy2 = np.append(yy, np.zeros_like(xz))

# Restore the order of xx2, and order yy2 in the same way.
k = xx2.argsort()
xx2 = xx2[k]
yy2 = yy2[k]

print("xx2:", xx2)
print("yy2:", yy2)

# pos_yy2 is the clipped version of yy2.
pos_yy2 = np.maximum(yy2, 0.0)

print("pos_yy2:", pos_yy2)

# Compute the area with `trapz`.
pos_area = np.trapz(pos_yy2, xx2)
print("pos_area:", pos_area)

Output:

xz: [2.5]
xx2: [1.  2.  2.5 3.  4. ]
yy2: [-1.5 -0.5  0.   0.5  1.5]
pos_yy2: [0.  0.  0.  0.5 1.5]
pos_area: 1.125

A function to do all that is:

def pos_trapz(y, x=None, dx=1.0):
    if x is None:
        x = np.arange(len(y))*dx
    xz = find_zero_crossings(x, y)

    # Insert the x intercepts into the data.
    x2 = np.append(x, xz)
    y2 = np.append(y, np.zeros_like(xz))

    # Restore the order of x2, and order y2 in the same way.
    # (The function assumes that the input `x` is in ascending order.)
    k = x2.argsort()
    x2 = x2[k]
    y2 = y2[k]

    # pos_y2 is the clipped version of y2.
    pos_y2 = np.maximum(y2, 0.0)

    # Compute the area with `trapz`.
    return np.trapz(pos_y2, x2)

In an ipython session:

In [107]: xx = np.arange(1, 5, 1)

In [108]: yy = np.array([-1.5, -0.5, 0.5, 1.5])

In [109]: pos_trapz(yy, xx)                        
Out[109]: 1.125
Warren Weckesser
  • 110,654
  • 19
  • 194
  • 214
  • You might be interested in [Ernest's two-liner](https://stackoverflow.com/questions/46909373/how-to-find-the-exact-intersection-of-a-curve-as-np-array-with-y-0/46911822#46911822) to pinpoint the zero crossings. – JohanC Aug 19 '20 at 11:25
  • Thanks for all the answers and comments. I read all of them and solve the problem. – user3874862 Aug 19 '20 at 18:31
  • @JohanC, yes, that's a nice and concise version! – Warren Weckesser Aug 19 '20 at 18:35