cumsum & cumprod
Build running totals and running products — a batter's running score across the season, the team's scoreboard over by over, and compounding growth.
Watch the lesson
1 video on CodeWithMunnaX
A TV scoreboard doesn't show runs per over — it shows the running total: 8, then 15, then 27… Each number is "everything so far". That is a cumulative sum. NumPy computes it in one call with cumsum, and its multiplying cousin cumprod handles things that compound, like growth rates.
Definition
cumsum(cumulative sum) returns an array of the same length where each element is the sum of all elements up to and including that position.cumprod(cumulative product) does the same with multiplication. Unlikesum/prod, they do not reduce — they keep every intermediate step.
- Same length — 6 overs in → 6 running totals out.
- Up to and including — position
iofcumsum=a[0] + a[1] + … + a[i]. - Last element — the last value of
cumsumalways equalssum(); the last value ofcumprodequalsprod().
text
runs per over: 8 7 12 4 10 9
cumsum: 8 15 27 31 41 50 <- scoreboard after each over
8 8+7 15+12 ...Syntax
python
np.cumsum(a, axis=None, dtype=None) a.cumsum(axis=None)
np.cumprod(a, axis=None, dtype=None) a.cumprod(axis=None)
# NumPy 2.1+ also has:
np.cumulative_sum(a, axis=None, include_initial=False)
np.cumulative_prod(a, axis=None, include_initial=False)| Parameter | Meaning | Default |
|---|---|---|
a | input array | — required |
axis | None = flatten first and give one long running total; 0 = running total down each column; 1 = across each row | None |
dtype | type of the accumulator, e.g. np.int64 / np.float64 | input's type (small ints are upgraded) |
include_initial | (cumulative_sum only) put the starting value 0 (or 1) at the front | False |
Example — the running scoreboard
python
import numpy as np
runs_per_over = np.array([8, 7, 12, 4, 10, 9])
scoreboard = runs_per_over.cumsum()
print("per over :", runs_per_over)
print("running :", scoreboard)
print("final :", scoreboard[-1], "== sum:", runs_per_over.sum())
print("first over where total >= 30:", np.argmax(scoreboard >= 30) + 1)text
per over : [ 8 7 12 4 10 9]
running : [ 8 15 27 31 41 50]
final : 50 == sum: 50
first over where total >= 30: 4How it works: each position adds the new over to the previous total. scoreboard >= 30 gives a boolean array, and argmax on booleans returns the first True — over 4 is where the team crossed 30.
Example — running season total on 2D data (axis)
python
import numpy as np
scores = np.array([
[72, 45, 88, 31, 64], # Virat
[56, 0, 74, 88, 32], # Rohit
])
print("axis=1 (each player's running total across matches):")
print(scores.cumsum(axis=1))
print("axis=0 (running team total down the players):")
print(scores.cumsum(axis=0))
print("axis=None (flattened):", scores.cumsum())text
axis=1 (each player's running total across matches):
[[ 72 117 205 236 300]
[ 56 56 130 218 250]]
axis=0 (running team total down the players):
[[ 72 45 88 31 64]
[128 45 162 119 96]]
axis=None (flattened): [ 72 117 205 236 300 356 356 430 518 550]How it works: axis=1 runs along each row (player), which is what you usually want for "runs so far in the season". axis=0 runs down columns. With no axis, the array is flattened first and you get one long 1D running total — usually not what you want for a table.
Example — cumprod for compounding
A franchise's brand value grows by a different percentage each season. Growth compounds (multiplies), so use cumprod:
python
import numpy as np
growth = np.array([1.10, 1.05, 1.20, 0.95]) # +10%, +5%, +20%, -5%
value = 100 * growth.cumprod()
print("growth factor so far:", growth.cumprod().round(4))
print("brand value (crore) :", value.round(2))
print("factorials 1..6:", np.arange(1, 7).cumprod())text
growth factor so far: [1.1 1.155 1.386 1.3167]
brand value (crore) : [110. 115.5 138.6 131.67]
factorials 1..6: [ 1 2 6 24 120 720]How it works: after season 2 the value is 100 × 1.10 × 1.05 = 115.5, and so on. cumprod of 1, 2, 3, … gives factorials.
Example — running average and include_initial
python
import numpy as np
virat = np.array([72, 45, 88, 31, 64])
matches = np.arange(1, virat.size + 1)
print("running average:", (virat.cumsum() / matches).round(1))
print("with initial 0 :", np.cumulative_sum(virat, include_initial=True))text
running average: [72. 58.5 68.3 59. 60. ]
with initial 0 : [ 0 72 117 205 236 300]How it works: dividing the running total by the number of matches played so far gives the batting average after each match. np.cumulative_sum(..., include_initial=True) (NumPy 2.1+) prepends the starting 0 — handy for "score before the first match".
Common mistakes
- ❌ Calling
cumsum()on a 2D table withoutaxisand getting a flattened 1D result. ✅ Passaxis=1(per row) oraxis=0(per column). - ❌ Expecting
cumsumto return one number. ✅ It returns an array of the same size; usesum()for one number. - ❌
cumprodon large integers silently overflows (wraps around). ✅ Use floats ordtype=np.float64for big products. - ❌ Writing a Python loop with
total += x. ✅cumsumdoes the whole loop in C.
Comparison — reduce vs accumulate
sum | cumsum | prod | cumprod | |
|---|---|---|---|---|
Input [2, 3, 4] | 9 | [2, 5, 9] | 24 | [2, 6, 24] |
| Output size | 1 | same as input | 1 | same as input |
| Cricket use | season total | running score | — | compounding growth |
Interview tip
Q: Given daily runs, how do you find the first day the cumulative total crossed 500, without a loop?
A: np.argmax(runs.cumsum() >= 500) — build the running total, compare to get booleans, and argmax returns the index of the first True. (Check (runs.cumsum() >= 500).any() first, because if it never crosses, argmax returns 0.)
Key points to remember
- cumsum returns running totals; cumprod returns running products — both keep the same length as the input.
- The last element of cumsum equals sum(); the last element of cumprod equals prod().
- Without axis, cumsum/cumprod flatten the array first — pass axis=1 for per-row running totals on a table.
- cumsum / count-so-far gives a running average after each match.
- np.argmax(a.cumsum() >= target) finds the first position where the running total crosses a target.
- cumprod is for compounding (growth rates, factorials); watch for integer overflow.
- NumPy 2.1+ adds np.cumulative_sum / np.cumulative_prod with include_initial=True.
Try it yourself
Scoreboard after each over
Running season totals per player
Compounding with cumprod
Practice: running batting average
Revision Flashcards
Tap a card to flip it and see the answer.
