Cumulative & Difference Functions

Reviewed & published by Brayan K

Cumulative and difference functions turn an array of values into running totals or step-to-step changes, letting you compute rolling balances with cumsum and successive differences with diff in a single call.

Part of the free NumPy course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

You'll build running totals with cumsum, running products with cumprod, neighbour differences with diff (including along an axis), and a smoother rate of change with gradient.

What You'll Learn in This Lesson

1 Running Totals with cumsum & cumprod

Where np.sum collapses to one number, np.cumsum keeps the length and stores the running total at each step. np.cumprod does the same with multiplication, giving a running product — handy for cumulative growth factors.

import numpy as np

arr = np.array([1, 2, 3, 4])

np.cumsum(arr)    # [ 1  3  6 10]  -> 1, 1+2, 1+2+3, ...
np.cumprod(arr)   # [ 1  2  6 24]  -> 1, 1*2, 1*2*3, ...
arr.sum()         # 10  -> just the final total
import numpy as np

arr = np.array([1, 2, 3, 4])

print(np.cumsum(arr))      # [ 1  3  6 10]
print(np.cumprod(arr))     # [ 1  2  6 24]

# The final cumulative value equals the plain reduction
print(np.cumsum(arr)[-1], arr.sum())     # 10 10

# A running balance from deposits and withdrawals
moves = np.array([100, -30, 50, -20])
print(np.cumsum(moves))    # [100  70 120 100]

# ✅ Expected output:
# [ 1  3  6 10]
# [ 1  2  6 24]
# 10 10
# [100  70 120 100]

2 Step-to-Step Changes with np.diff

np.diff is the inverse idea: it returns arr[i+1] - arr[i] for each neighbouring pair, turning a series of values into the changes between them. Because each output needs two inputs, the result is one element shorter.

temps = np.array([20, 23, 19, 25])

np.diff(temps)        # [ 3 -4  6]   -> rose 3, fell 4, rose 6
len(np.diff(temps))   # 3  -> one shorter than the input (4)

# Keep the original length by prepending a start value
np.diff(temps, prepend=temps[0])   # [0 3 -4 6]
import numpy as np

temps = np.array([20, 23, 19, 25])

# Differences between consecutive readings
print(np.diff(temps))          # [ 3 -4  6]
print(len(np.diff(temps)))     # 3

# cumsum and diff are inverses
print(np.cumsum(np.diff(temps, prepend=0)))   # [20 23 19 25]

# diff of order 2: the difference of the differences
print(np.diff(temps, n=2))     # [-7 10]

# ✅ Expected output:
# [ 3 -4  6]
# 3
# [20 23 19 25]
# [-7 10]

3 Along an Axis & np.gradient

On 2D arrays, both cumsum and diff take an axis argument so you can accumulate down columns or across rows. For a smooth rate of change that keeps the original length, reach for np.gradient, which uses central differences in the middle and one-sided at the edges.

grid = np.array([[1, 2, 3],
                 [4, 5, 6]])

np.cumsum(grid, axis=1)   # along rows: [[1 3 6], [4 9 15]]
np.diff(grid, axis=0)     # down columns: [[3 3 3]]

# gradient keeps the length (central differences)
np.gradient(np.array([1, 4, 9, 16]))   # [3. 4. 6. 7.]
import numpy as np

grid = np.array([[1, 2, 3],
                 [4, 5, 6]])

# Running total ACROSS each row (axis=1)
print(np.cumsum(grid, axis=1))
# [[ 1  3  6]
#  [ 4  9 15]]

# Difference DOWN the columns (axis=0)
print(np.diff(grid, axis=0))   # [[3 3 3]]

# gradient: smooth rate, same length as input
print(np.gradient(np.array([1.0, 4.0, 9.0, 16.0])))
# [3. 4. 6. 7.]

# ✅ Expected output:
# [[ 1  3  6]
#  [ 4  9 15]]
# [[3 3 3]]
# [3. 4. 6. 7.]

🎯 YOUR TURN: Fill in the Blank

Replace ___ with the function that turns daily deposits into a running account balance.

import numpy as np

deposits = np.array([50, 20, 30, 10])

# 🎯 YOUR TURN:
# Show the balance after EACH deposit (a running total).
# Hint: running total = cumulative sum.
balance = np.___(deposits)

print(balance)
# Expected output:
# [ 50  70 100 110]

Answer: cumsum — it accumulates the deposits into a running balance.

! Common Errors (And How to Fix Them)

❌ Length mismatch after np.diff

days = np.arange(5)
days + np.diff(values)   # ❌ shapes (5,) vs (4,) clash

✅ Fix: use np.diff(values, prepend=values[0]) to keep the original length, or slice days[1:].

❌ Using sum when you wanted a running total

arr.sum()   # one number, not the step-by-step totals

✅ Fix: use np.cumsum(arr) to keep the value at every position.

❌ Forgetting axis on a 2D array

Without axis, cumsum and diff flatten the whole array first.

✅ Fix: pass axis=0 (down columns) or axis=1 (across rows) explicitly.

🎯 Mini Challenge: Sales Momentum

From weekly sales, compute the cumulative total and the week-over-week change, then report the best single-week jump.

import numpy as np

sales = np.array([100, 130, 90, 150, 200])

# Cumulative sales to date
running = np.cumsum(sales)
print(running)                 # [100 230 320 470 670]

# Week-over-week change
change = np.diff(sales)
print(change)                  # [ 30 -40  60  50]

# Biggest single-week jump
print("best jump:", change.max())     # best jump: 60

# ✅ Expected output:
# [100 230 320 470 670]
# [ 30 -40  60  50]
# best jump: 60

❓ Frequently Asked Questions

Lesson complete — totals and changes unlocked!

You can build running totals with cumsum, running products with cumprod, step changes with diff (mind the shorter length), and smooth rates with np.gradient.

Practice quiz

What does np.cumsum([3, 1, 2]) return?

  • [3, 4, 6]
  • [6, 4, 3]
  • [3, 1, 2]
  • [6]

Answer: [3, 4, 6]. cumsum keeps the running total at each step: 3, 3+1, 3+1+2.

What does np.cumprod([1, 2, 3, 4]) return?

  • [24]
  • [1, 2, 6, 24]
  • [1, 3, 6, 10]
  • [4, 12, 24, 24]

Answer: [1, 2, 6, 24]. cumprod gives the running product: 1, 1*2, 1*2*3, 1*2*3*4.

What does np.diff([10, 13, 9]) return?

  • [10, 13, 9]
  • [10, 3, -4]
  • [23, 22]
  • [3, -4]

Answer: [3, -4]. diff returns each arr[i+1] - arr[i], so 13-10=3 and 9-13=-4.

How long is np.diff applied to an array of length 4?

  • 4
  • 5
  • 3
  • 2

Answer: 3. Each output needs two neighbours, so n inputs give n-1 differences.

The last element of np.cumsum(arr) always equals which value?

  • arr.sum()
  • arr.max()
  • arr.mean()
  • arr[0]

Answer: arr.sum(). The final running total is the total of every element, i.e. arr.sum().

Which function gives a smooth rate of change that keeps the original length?

  • np.diff
  • np.cumsum
  • np.gradient
  • np.sum

Answer: np.gradient. np.gradient uses central differences and returns an array the same length as the input.

What does np.gradient(np.array([1., 4., 9., 16.])) return?

  • [3., 5., 7.]
  • [3., 4., 6., 7.]
  • [1., 4., 9., 16.]
  • [3., 6., 9.]

Answer: [3., 4., 6., 7.]. gradient uses one-sided differences at the ends and central differences inside.

On a 2D array, which argument makes cumsum accumulate across each row?

  • axis=1
  • axis=0
  • axis=-1 only
  • order='C'

Answer: axis=1. axis=1 runs along the columns within each row; axis=0 goes down rows.

How do you keep np.diff the same length as the input?

  • Pass n=0
  • Use prepend= a starting value
  • Use np.sum instead
  • It is impossible

Answer: Use prepend= a starting value. prepend= adds a leading value so the result matches the original length.

What does np.diff([20, 23, 19, 25], n=2) return?

  • [3, -4, 6]
  • [-7, 10]
  • [20, 23]
  • [2]

Answer: [-7, 10]. n=2 differences the differences: diff([3,-4,6]) = [-7, 10].

Continue this course

Frequently asked questions

What is the difference between np.sum and np.cumsum?

np.sum collapses the array to a single total, while np.cumsum keeps the array's length and returns the running total at each position. For [1, 2, 3], sum gives 6 but cumsum gives [1, 3, 6] — each element is the sum of everything up to and including it. cumsum is what you want for running balances or cumulative charts.

How does np.diff work?

np.diff returns the difference between each pair of consecutive elements: out[i] = arr[i+1] - arr[i]. Because it compares neighbours, the result is one element SHORTER than the input. For [10, 13, 9] it returns [3, -4]. It is perfect for turning a series of readings into the changes between them.

Why is the result of np.diff one element shorter?

Each output value comes from a pair of neighbouring inputs, so an array of length n has only n-1 adjacent pairs. That is why np.diff([a, b, c]) returns 2 values. If you need the result to keep the original length, prepend a starting value with the prepend argument, e.g. np.diff(arr, prepend=arr[0]).

What does np.gradient compute that np.diff does not?

np.gradient estimates the rate of change while KEEPING the original length. Instead of a one-sided difference, it uses central differences in the interior (averaging the slope on both sides) and one-sided differences at the ends. The result is a smoother derivative estimate, which is why it is preferred for physics-style rates rather than raw step-to-step changes.