Skip to main content

Net change and the round trip

Integrating a rate of change gives the total change: velocity gives displacement, a density gives a probability, a slope gives a change in loss. Differentiating and integrating undo each other up to a constant, exactly and on sampled data with NumPy and SciPy.
M04.6.4L1 Core25 minIn review

Before this lesson

M04.6.3 Part two: integrals from antiderivatives M04.5.4 The trapezoid rule and integrals of sampled data M04.2.4 Derivatives by computer: finite differences

After this lesson you can

  • Use the net change theorem to turn a rate of change into a total change, for a velocity, a growth rate, a probability density and the slope of a loss.
  • Explain why differentiating an area function gives back f exactly, while integrating a derivative gives back F only up to its starting value.
  • Show that numpy.diff undoes numpy.cumsum exactly, rounding aside, and that numpy.gradient undoes scipy.integrate.cumulative_trapezoid only approximately, with an error that shrinks as the samples get closer.
  • Tell net change from total distance, and compute both for a trip that turns back.

In review. The maths and code on this page are checked by automated tests on every change, but an independent reader has not signed it off yet. Spot a mistake or a confusing step? Tell us.

The question

A speedometer reports speed many times a second, never position. Adding up the speeds gives the distance so far, as the last lessons showed. Now go back the other way and take the slopes of that running total. Do you get the speeds back exactly, and what was lost on the way? The same round trip turns the slope of a loss into its total drop, and a density into a probability.

From the last lesson

1. If \({F' = f}\) on an interval, what are all the antiderivatives of \(f\) there?

Show answer

\({F(x) + C}\), for any constant \(C\): two antiderivatives differ by a constant.

2. Why does the constant \(C\) not change \({F(b) - F(a)}\)?

Show answer

It appears in both terms and cancels: \({(F(b) + C) - (F(a) + C)} = {F(b) - F(a)}\).

Intuition

Rates and amounts come in pairs: speed and position, a growth rate and a population, a density and a probability, the slope of a loss and the loss. Integrating takes you from the rate to the amount; differentiating takes you back.

Two plots joined by two arrows. Left, titled Rate: speed in metres per second, the blue speed curve v of t of the trip from 0 to 4 seconds with the region under it shaded grey and labelled area 32 thirds. Right, titled Amount: position in metres, two sky-blue curves of the same shape: a solid one starting at 0 and labelled s of t minus s of 0, and a dashed one starting at 5, labelled started at 5 m. A black arrow from left to right is labelled integrate from 0. A green arrow from right to left is labelled differentiate, both curves.
M04.6.4-F1

The round trip for the running trip. Differentiating either position curve gives the same speed. Integrating the speed from 0 gives back only the curve that starts at 0: the starting position, 5 metres for the dashed cart, is lost.

The trip is not symmetric. Going from rate to amount and back, nothing is lost: the slope of the area function is the rate (part one). Going from amount to rate and back, the starting value is lost, because every curve slid up or down has the same slopes.

Build it up

Step 1. A rate integrates to a total change. If \({F'}\) is continuous on \({[a, b]}\), then \(F\) is an antiderivative of \({F'}\), and part two gives

\[ \int_a^b F'(t)\,dt = F(b) - F(a) . \]

In words: integrating a rate of change gives the total change. This is the net change theorem.

Step 2. Four readings.

  • Velocity gives displacement. \({\int_a^b v(t)\,dt} = {s(b) - s(a)}\): for the trip over 4 seconds, \({\tfrac{32}{3}}\) metres.
  • A growth rate gives growth. An app gains users at \({r(t) = 30 + 6t}\) thousand per month (an invented example). Over a year it gains \({\int_0^{12} (30 + 6t)\,dt} = {\big[30t + 3t^2\big]_0^{12}} = 792\) thousand.
  • A density gives a probability. If \(X\) has density \({2x}\) on \({[0, 1]}\), the chance that it lands between \({\tfrac14}\) and \({\tfrac12}\) is the area there, \({\big[x^2\big]_{1/4}^{1/2}} = {\tfrac{3}{16}}\) (M06.6).
  • A slope gives a change in loss. For the running loss, \({L'(w) = 15w - 23.5}\). From \({w = 0}\) to the minimum at \({\tfrac{47}{30}}\) the loss changes by \({\int_0^{47/30} L'(w)\,dw}\), which is \({L(\tfrac{47}{30}) - L(0)} \approx {0.0917 - 18.5} = {-18.41}\): a drop of about 18.41.
Left, titled Density to probability: the blue line density 2 x from 0 to 1, with the strip under it from 0.25 to 0.5 shaded grey and labelled 3 sixteenths, and beside it x squared from one quarter to one half equals one quarter minus one sixteenth equals 3 sixteenths. Right, titled Slope to change in loss: the green line L prime of w equals 15 w minus 23.5, crossing zero at w equals 47 thirtieths, marked with a pink dot. The hatched triangle between the line and the axis from 0 to 47 thirtieths lies below the axis and is labelled area about minus 18.41, with the note: the drop, L of 47 thirtieths minus L of 0.
M04.6.4-F4

Two more readings of the same theorem. The area under a density between two values is a probability. The area under the slope of a loss is the change in the loss; here it lies below the axis, so the loss falls.

Step 3. Integrate, then differentiate: exact. Start from \(f\), form its area function, and take its slope. Part one says you get \(f\) back:

\[ \frac{d}{dx} \int_a^x f(t)\,dt = f(x) . \]

Step 4. Differentiate, then integrate: the start is lost. Start from \(F\), take its slope, and integrate from \(a\). The net change theorem with \(x\) as the upper end gives

\[ \int_a^x F'(t)\,dt = F(x) - F(a) , \]

which is \(F\) less its starting value. A cart that started 5 metres along has position \({2t^2 - \tfrac13 t^3 + 5}\); its speed is the same \({v(t)}\), and integrating that speed from 0 gives \({2x^2 - \tfrac13 x^3}\). The 5 metres are gone. This is the constant \(C\) again, and it is why a differential equation needs a starting value (M04.9).

Step 5. On samples, sums and differences are exact twins. Data comes as samples, so the integral becomes a running total (numpy.cumsum) and the derivative becomes a difference between neighbours (numpy.diff, which gives \({a_{k+1} - a_k}\)). The same two facts hold with no approximation at all; the formal statement proves them. On a computer only the rounding of decimals can touch the last digits, and whole numbers are not rounded.

Step 6. With trapezoids and slopes, the round trip is close, not exact. The better tools, scipy.integrate.cumulative_trapezoid (the trapezoid rule of M04.5.4 as a running total) and numpy.gradient (central differences, M04.2.4), are not exact inverses. The worked example shows the error, and the code shows it shrinking.

The formal statement

NoteTheorem: net change

If \({F'}\) is continuous on \({[a, b]}\), then

\[ \int_a^b F'(t)\,dt = F(b) - F(a) . \]

The integral of a rate over an interval is the total change over that interval.

Proof. \(F\) is an antiderivative of the continuous function \({F'}\); apply part two. \(\square\)

The total change is a net change: where the rate is negative, the amount goes down, and that counts against the total. For velocity the net change is the displacement. The total distance counts every metre, forwards or back, so it integrates the speed \({\abs{v(t)}}\) instead.

The round trips, on samples. Let \({x_1, \dots, x_n}\) be numbers, and write \({S_k = x_1 + \dots + x_k}\) for their running totals, with \({S_0 = 0}\).

Total, then difference: exact. Each total is the one before plus one more term, so

\[ S_k - S_{k-1} = x_k . \]

The differences of the running totals are the numbers you started with. In NumPy, np.diff(np.cumsum(x), prepend=0) gives back x: exactly for whole numbers, and for decimals up to rounding in the last digits, because each running total is rounded as it is stored. Without prepend=0, the first entry is missing.

Difference, then total: the start is lost. Let \({s_0, s_1, \dots, s_n}\) be values and add up their differences. Every inner value appears once with a plus sign and once with a minus sign, so the sum telescopes:

\[ \begin{aligned} &(s_1 - s_0) + (s_2 - s_1) \\ &\quad + \dots + (s_k - s_{k-1}) \\ &\quad = s_k - s_0 . \end{aligned} \]

So np.cumsum(np.diff(s)) gives s[1:] - s[0], again up to rounding for decimals: the values less the starting one, the discrete twin of \({F(x) - F(a)}\).

Worked example

The round trip on the trip’s five samples, by hand. The speeds at \({t = 0, 1, 2, 3, 4}\) seconds are 0, 3, 4, 3, 0 metres per second, one second apart.

Integrate with trapezoids. Each one-second strip becomes a trapezoid of area equal to the average of its two end speeds: 1.5, 3.5, 3.5 and 1.5 metres. The running totals are

\[ 0,\; 1.5,\; 5,\; 8.5,\; 10 , \]

against the true distances 0, 1.67, 5.33, 9, 10.67 metres. The total, 10 against \({\tfrac{32}{3}}\), is the trapezoid rule’s answer from M04.5.4.

Differentiate back with np.gradient. Inside, it takes the central difference over two seconds; at each end, the one-sided difference over one:

\[ \begin{aligned} &\tfrac{1.5 - 0}{1},\; \tfrac{5 - 0}{2},\; \tfrac{8.5 - 1.5}{2},\; \tfrac{10 - 5}{2},\; \tfrac{10 - 8.5}{1} \\ &\quad = 1.5,\; 2.5,\; 3.5,\; 2.5,\; 1.5 . \end{aligned} \]

Left, titled 1. Add trapezoids: the sky-blue curve exact s of t rising from 0 to about 10.67, and vermillion dots joined by straight lines at the running totals 0, 1.5, 5, 8.5 and 10 at t equals 0 to 4, each labelled. Right, titled 2. Differentiate back: the blue speed curve with blue dots at the samples 0, 3, 4, 3, 0, labelled speeds v: 0, 3, 4, 3, 0, and hollow vermillion dots joined by a dashed line at the recovered values 1.5, 2.5, 3.5, 2.5, 1.5, each labelled.
M04.6.4-F2

The round trip on samples one second apart. Left: trapezoid running totals trail the true distance. Right: their slopes, by np.gradient, come back as 1.5, 2.5, 3.5, 2.5, 1.5 instead of 0, 3, 4, 3, 0: a blurred copy of the speeds.

The speeds come back blurred. To see why, write out the central difference at an inside sample \({t_k}\), where each trapezoid has area \({\tfrac12 (v_{k-1} + v_k)}\) or \({\tfrac12 (v_k + v_{k+1})}\):

\[ \begin{aligned} &\frac{S_{k+1} - S_{k-1}}{2} \\ &\quad = \tfrac12 \Big(\tfrac{v_{k-1} + v_k}{2} + \tfrac{v_k + v_{k+1}}{2}\Big) \\ &\quad = \tfrac14 \big(v_{k-1} + 2v_k + v_{k+1}\big) . \end{aligned} \]

Each recovered speed is a weighted average of the speed and its two neighbours, so the peak of 4 is pulled down to 3.5. When the samples are close, neighbours are nearly equal and the blur fades.

By contrast, plain running totals of the speeds, 0, 3, 7, 10, 10, have differences 3, 4, 3, 0: the speeds again, exactly, less only the first.

Code

First the exact pair, np.cumsum and np.diff: on whole numbers, where nothing is rounded, and then on three decimals.

import numpy as np

v = np.array([0, 3, 4, 3, 0])                  # the trip's speeds, one second apart
S = np.cumsum(v)
print("cumsum:", S, "  diff of it:", np.diff(S), "  with prepend=0:", np.diff(S, prepend=0))

s = np.array([5, 8, 12, 15, 15])               # positions of a cart that started at 5
print("cumsum of diff:", np.cumsum(np.diff(s)), "  s[1:] - s[0]:", s[1:] - s[0])

x = np.array([0.1, 0.2, 0.3])                  # decimals: each running total is rounded
back = np.diff(np.cumsum(x), prepend=0)
print("decimals back:", back, f"  largest gap from x: {np.max(np.abs(back - x)):.1e}")
cumsum: [ 0  3  7 10 10]   diff of it: [3 4 3 0]   with prepend=0: [0 3 4 3 0]
cumsum of diff: [ 3  7 10 10]   s[1:] - s[0]: [ 3  7 10 10]
decimals back: [0.1 0.2 0.3]   largest gap from x: 5.6e-17

On whole numbers both round trips are exact. On decimals the gap is below \({10^{-16}}\): rounding in the last digits, not an error of the method.

Now the trapezoid pair on the five samples, as in the worked example, and then on samples a hundredth of a second apart. cumulative_trapezoid with initial=0 puts the area up to the first sample, 0, in front, so the result has one entry per sample.

from scipy.integrate import cumulative_trapezoid

def speed(t):
    return 4 * t - t ** 2

def position(t):
    return 2 * t ** 2 - t ** 3 / 3

t5 = np.arange(5.0)
total5 = cumulative_trapezoid(speed(t5), t5, initial=0)
print("trapezoid totals:", total5, "  np.gradient:", np.gradient(total5, t5))

t = np.linspace(0, 4, 401)                     # samples 0.01 s apart
total = cumulative_trapezoid(speed(t), t, initial=0)
back = np.gradient(total, t, edge_order=2)     # second-order differences at the two ends too
print(f"largest error in distance: {np.max(np.abs(total - position(t))):.2e} m")
print(f"largest error in speed:    {np.max(np.abs(back - speed(t))):.2e} m/s")
trapezoid totals: [ 0.   1.5  5.   8.5 10. ]   np.gradient: [1.5 2.5 3.5 2.5 1.5]
largest error in distance: 6.67e-05 m
largest error in speed:    5.00e-05 m/s

With samples a hundredth of a second apart the round trip is good to about \({7 \times 10^{-5}}\) metres and \({5 \times 10^{-5}}\) metres per second. The third exercise shows where the second number comes from: for this speed curve every inside value comes back too low by exactly half the square of the spacing.

Last, each reading of the net change theorem, checked with quad. For the distance, points=[4] tells quad where the speed changes sign, so that it treats the corner of \({\abs{v}}\) carefully.

from scipy.integrate import quad

L = lambda w: 7.5 * w ** 2 - 23.5 * w + 18.5           # the running loss
print(f"users gained: {quad(lambda t: 30 + 6 * t, 0, 12)[0]:.1f} thousand")
print(f"probability: {quad(lambda x: 2 * x, 0.25, 0.5)[0]:.4f}")
print(f"loss change: {quad(lambda w: 15 * w - 23.5, 0, 47 / 30)[0]:.4f}   L(47/30) - L(0): {L(47 / 30) - L(0):.4f}")
net = round(quad(speed, 0, 6)[0], 4) + 0.0           # + 0.0 turns a rounded -0.0 into 0.0
print(f"0 to 6 s, net: {net:.4f} m   distance: {quad(lambda t: abs(speed(t)), 0, 6, points=[4])[0]:.4f} m")
users gained: 792.0 thousand
probability: 0.1875
loss change: -18.4083   L(47/30) - L(0): -18.4083
0 to 6 s, net: 0.0000 m   distance: 21.3333 m

Explore

In the area accumulator of lesson M04.6.2, choose \({\cos t}\) and sweep the end from 0 to \({2\pi}\). The area function climbs to 1, falls to \({-1}\) while the curve is below the axis, and ends where it began: a net change of 0 over a whole turn, although plenty of area was swept.

Pitfalls

Two plots of the trip run on to 6 seconds. Left, marked with a cross and titled Net change: the blue velocity curve v of t rising to 4 at t equals 2, crossing zero at 4 and falling to minus 12 at 6; the hump above the axis is shaded grey and labelled plus 32 thirds, the deeper part below the axis is hatched and labelled minus 32 thirds, with the notes net: 32 thirds minus 32 thirds equals 0, so it went nowhere? Right, marked with a tick and titled Total distance: the speed, absolute value of v of t, with both humps above the axis shaded and each labelled 32 thirds, and the note distance: 32 thirds plus 32 thirds equals 64 thirds m.
M04.6.4-F3

Wrong beside right, for the trip run on for two more seconds, when the cart rolls back. The velocity integrates to a net change of 0: the cart ends where it started. The distance integrates the speed and counts both legs: \({\tfrac{64}{3} \approx 21.33}\) metres.

“The integral of the velocity is the distance travelled.”

It is the displacement, the net change in position. Run the trip on to 6 seconds: \({\int_0^6 v(t)\,dt = s(6) - s(0) = 0}\), yet the cart went \({\tfrac{32}{3}}\) metres out and \({\tfrac{32}{3}}\) back. For the distance, split at the turning point \({t = 4}\) and add the sizes of the pieces: \({\tfrac{32}{3} + \tfrac{32}{3} = \tfrac{64}{3}}\) metres.

“Integrating undoes differentiating, so I get the function back.”

You get it back less its starting value: \({\int_a^x F'(t)\,dt = F(x) - F(a)}\). From a cart’s speed alone you know how far it moved, not where it is. To know where it is, you also need where it started.

“np.gradient(np.cumsum(v)) gives back v.”

Twice wrong. np.cumsum(v) adds speeds, not areas, so it must be multiplied by the spacing first. And np.gradient takes central differences, which blur the data as the worked example showed. The exact inverse of np.cumsum is np.diff.

Check yourself

Recall

1. State the net change theorem.

Show answer

If \({F'}\) is continuous on \({[a, b]}\), then \({\int_a^b F'(t)\,dt = F(b) - F(a)}\): integrating a rate gives the total change.

2. What does \({\int_a^b v(t)\,dt}\) measure when \(v\) is a velocity?

Show answer

The displacement \({s(b) - s(a)}\), the net change in position. It is the distance travelled only when \(v\) is never negative.

3. What do you get if you differentiate \({\int_a^x f(t)\,dt}\), for a continuous \(f\)?

Show answer

\({f(x)}\), exactly. This is part one of the fundamental theorem.

4. What do you get if you integrate \({F'}\) from \(a\) to \(x\)?

Show answer

\({F(x) - F(a)}\): the function less its starting value, which is lost.

5. Which NumPy function exactly undoes np.cumsum?

Show answer

np.diff, with prepend=0 so that the first entry comes back too. On decimals only rounding in the last digits can differ.

Exercises

1. ★ Water runs into a tank at \({r(t) = 6 - t}\) litres per minute for \({0 \le t \le 6}\). How much water enters?

Show solution

The amount is the net change, the integral of the rate:

\[ \begin{aligned} \int_0^6 (6 - t)\,dt &= \Big[6t - \tfrac12 t^2\Big]_0^6 \\ &= 36 - 18 = 18 \end{aligned} \]

litres. The rate is never negative, so this is also the total that flowed in.

2. ★★ Run the trip on to 5 seconds. Find the cart’s displacement and the distance it travels.

Show solution

The displacement is \({s(5) - s(0)} = {50 - \tfrac{125}{3}} = {\tfrac{25}{3}} \approx 8.33\) metres. The velocity \({4t - t^2}\) is positive before \({t = 4}\) and negative after, so split there. Out: \({s(4) - s(0) = \tfrac{32}{3}}\) metres. Back: \({s(5) - s(4) = \tfrac{25}{3} - \tfrac{32}{3}}\), which is \({-\tfrac73}\), so \({\tfrac73}\) metres. The distance is \({\tfrac{32}{3} + \tfrac73 = 13}\) metres.

3. ★★★ Samples of the trip’s speed are a distance \(h\) apart. Show that at an inside sample the trapezoid round trip gives back \({v(t_k) - \tfrac12 h^2}\), exactly.

Show solution

By the worked example, the recovered value is \({\tfrac14 (v_{k-1} + 2v_k + v_{k+1})}\). For \({v(t) = 4t - t^2}\), expanding gives

\[ \begin{aligned} &v(t_k \pm h) \\ &\quad = 4t_k \pm 4h - (t_k \pm h)^2 \\ &\quad = 4t_k \pm 4h - t_k^2 \mp 2t_k h - h^2 \\ &\quad = v(t_k) \pm (4 - 2t_k)\,h - h^2 . \end{aligned} \]

Adding the two signs cancels the middle terms: \({v_{k-1} + v_{k+1}} = {2v_k - 2h^2}\). So

\[ \begin{aligned} &\tfrac14 \big(v_{k-1} + 2v_k + v_{k+1}\big) \\ &\quad = \tfrac14 \big(4v_k - 2h^2\big) \\ &\quad = v_k - \tfrac12 h^2 . \end{aligned} \]

With \({h = 1}\) the inside values come back 0.5 too low (2.5, 3.5, 2.5), and with \({h = 0.01}\) the error is \({5 \times 10^{-5}}\), as the code found.

Summary card

Integrate a rate to get the change; differentiate the change to get the rate back.

The anchor figure again: the trip's speed with its area, and the two position curves that start at 0 and at 5, joined by an arrow labelled integrate from 0 and an arrow back labelled differentiate.
M04.6.4-F1

The round trip loses only the start.

  • Net change. \({\int_a^b F'(t)\,dt = F(b) - F(a)}\): velocity, growth, probability, loss.
  • Exact one way. \({\frac{d}{dx} \int_a^x f(t)\,dt = f(x)}\).
  • Start lost the other way. \({\int_a^x F'(t)\,dt = F(x) - F(a)}\).
  • On samples. np.diff undoes np.cumsum, rounding aside; np.gradient undoes cumulative_trapezoid only approximately.
  • Distance integrates \({\abs{v}}\); displacement integrates \(v\).

Sources and next steps

Sources. Each was opened and checked against this lesson on 12 November 2026.

  • Gilbert Strang and Edwin Herman, Calculus Volume 1 (OpenStax), Section 5.4: Theorem 5.6, the net change theorem \({F(b) = F(a) + \int_a^b F'(x)\,dx}\); net displacement as the integral of the velocity, and total distance as the integral of its absolute value.
  • Matt Boelkins, Active Calculus, Section 4.4: the total change theorem, the integral of the rate of change of a function is its total change, read for position and velocity as the change in position.
  • David Jerison, MIT 18.01 Single Variable Calculus (Fall 2006), Lecture 19 notes: the speedometer and the odometer, and a round trip whose net change is 0 while the odometer counts the whole distance.
  • NumPy documentation (version 2.5): numpy.diff, whose first difference is out[i] = a[i+1] - a[i], and its prepend argument; numpy.gradient, central differences inside and one-sided differences at the boundaries, of first order unless edge_order=2.
  • SciPy documentation (version 1.18), scipy.integrate.cumulative_trapezoid: the running trapezoid integral, where initial=0 adds a first entry so that the result matches the input in length.

Next. Module M04.7 Integration techniques finds antiderivatives that the table cannot read off directly, and M04.9 meets the lost starting value again as the initial condition of a differential equation.

This lesson unlocks

M04.7 Integration techniques M04.9 Differential equations, a first look M06.6 Continuous random variables and densities (planned)

Your progress is saved in this browser only. No account, no tracking.
Back to top