Sum Prod Cumsum¶
Mental Model
sum adds elements, prod multiplies them, and cumsum/cumprod do the same but keep all intermediate results. The cumulative variants are especially useful for running totals, CDFs, and prefix-sum algorithms. All accept axis to operate along a specific dimension and keepdims to preserve the reduced axis as size 1.
Key insight: cumsum is discrete integration — it converts rates into totals, just as integration sums up infinitesimal slices. Similarly, np.diff (in the utility page) is discrete differentiation. Together they form a discrete calculus pair.
sum and np.sum¶
1. Basic Usage¶
Sum all elements or along an axis.
```python import numpy as np
def main(): a = np.array([[1, 2], [3, 1], [2, 3]])
print("a =")
print(a)
print()
print(f"{a.sum() = }")
print(f"{a.sum(axis=0) = }")
print(f"{a.sum(axis=1) = }")
if name == "main": main() ```
Output:
``` a = [[1 2] [3 1] [2 3]]
a.sum() = 12 a.sum(axis=0) = array([6, 6]) a.sum(axis=1) = array([3, 4, 5]) ```
2. Function Syntax¶
```python import numpy as np
def main(): a = np.array([[1, 2], [3, 1], [2, 3]])
print("a =")
print(a)
print()
print(f"{np.sum(a) = }")
print(f"{np.sum(a, axis=0) = }")
print(f"{np.sum(a, axis=1) = }")
if name == "main": main() ```
3. Row and Column Sum¶
```python import numpy as np
def main(): a = np.array([ [8, 3, 9, 0, 10], [3, 5, 17, 1, 1], [2, 8, 6, 23, 1], [15, 7, 3, 2, 9], [6, 14, 2, 6, 0], ])
print("Matrix:")
print(a)
print()
print(f"Total Sum : {a.sum()}")
print(f"Column Sum : {a.sum(axis=0)}")
print(f"Row Sum : {a.sum(axis=1)}")
if name == "main": main() ```
Output:
``` Matrix: [[ 8 3 9 0 10] [ 3 5 17 1 1] [ 2 8 6 23 1] [15 7 3 2 9] [ 6 14 2 6 0]]
Total Sum : 150 Column Sum : [34 37 37 32 21] Row Sum : [30 27 40 36 28] ```
cumsum and np.cumsum¶
1. Basic Usage¶
Cumulative sum returns running totals.
```python import numpy as np
def main(): a = np.array([1, 2, 3, 4, 5])
print(f"a = {a}")
print(f"cumsum = {a.cumsum()}")
# [1, 1+2, 1+2+3, 1+2+3+4, 1+2+3+4+5]
# [1, 3, 6, 10, 15]
if name == "main": main() ```
2. With axis Parameter¶
```python import numpy as np
def main(): a = np.array([[1, 2], [3, 1], [2, 3]])
print("a =")
print(a)
print()
print("a.cumsum() (flattened):")
print(a.cumsum())
print()
print("a.cumsum(axis=0) (down columns):")
print(a.cumsum(axis=0))
print()
print("a.cumsum(axis=1) (across rows):")
print(a.cumsum(axis=1))
if name == "main": main() ```
Output:
``` a = [[1 2] [3 1] [2 3]]
a.cumsum() (flattened): [ 1 3 6 7 9 12]
a.cumsum(axis=0) (down columns): [[1 2] [4 3] [6 6]]
a.cumsum(axis=1) (across rows): [[1 3] [3 4] [2 5]] ```
3. Function Syntax¶
```python import numpy as np
def main(): a = np.array([[1, 2], [3, 1], [2, 3]])
print("a =")
print(a)
print()
print("np.cumsum(a):")
print(np.cumsum(a))
print()
print("np.cumsum(a, axis=0):")
print(np.cumsum(a, axis=0))
print()
print("np.cumsum(a, axis=1):")
print(np.cumsum(a, axis=1))
if name == "main": main() ```
prod and np.prod¶
1. Basic Usage¶
Product of all elements or along an axis.
```python import numpy as np
def main(): a = np.array([[1, 2], [3, 1], [2, 3]])
print("a =")
print(a)
print()
print(f"{a.prod() = }")
print(f"{a.prod(axis=0) = }")
print(f"{a.prod(axis=1) = }")
if name == "main": main() ```
Output:
``` a = [[1 2] [3 1] [2 3]]
a.prod() = 36 a.prod(axis=0) = array([6, 6]) a.prod(axis=1) = array([2, 3, 6]) ```
2. Function Syntax¶
```python import numpy as np
def main(): a = np.array([[1, 2], [3, 1], [2, 3]])
print("a =")
print(a)
print()
print(f"{np.prod(a) = }")
print(f"{np.prod(a, axis=0) = }")
print(f"{np.prod(a, axis=1) = }")
if name == "main": main() ```
3. Factorial Example¶
```python import numpy as np
def main(): # Compute 5! = 1 * 2 * 3 * 4 * 5 n = 5 factorial = np.arange(1, n + 1).prod()
print(f"{n}! = {factorial}")
# Multiple factorials
for n in range(1, 8):
fact = np.arange(1, n + 1).prod()
print(f"{n}! = {fact}")
if name == "main": main() ```
cumprod and np.cumprod¶
1. Basic Usage¶
Cumulative product returns running products.
```python import numpy as np
def main(): a = np.array([1, 2, 3, 4, 5])
print(f"a = {a}")
print(f"cumprod = {a.cumprod()}")
# [1, 1*2, 1*2*3, 1*2*3*4, 1*2*3*4*5]
# [1, 2, 6, 24, 120]
if name == "main": main() ```
2. With axis Parameter¶
```python import numpy as np
def main(): a = np.array([[1, 2], [3, 1], [2, 3]])
print("a =")
print(a)
print()
print("a.cumprod() (flattened):")
print(a.cumprod())
print()
print("a.cumprod(axis=0) (down columns):")
print(a.cumprod(axis=0))
print()
print("a.cumprod(axis=1) (across rows):")
print(a.cumprod(axis=1))
if name == "main": main() ```
Output:
``` a = [[1 2] [3 1] [2 3]]
a.cumprod() (flattened): [ 1 2 6 6 12 36]
a.cumprod(axis=0) (down columns): [[1 2] [3 2] [6 6]]
a.cumprod(axis=1) (across rows): [[1 2] [3 3] [2 6]] ```
3. Function Syntax¶
```python import numpy as np
def main(): a = np.array([[1, 2], [3, 1], [2, 3]])
print("a =")
print(a)
print()
print("np.cumprod(a):")
print(np.cumprod(a))
print()
print("np.cumprod(a, axis=0):")
print(np.cumprod(a, axis=0))
print()
print("np.cumprod(a, axis=1):")
print(np.cumprod(a, axis=1))
if name == "main": main() ```
np.squeeze¶
1. Remove Size-1 Dims¶
np.squeeze removes dimensions of size 1.
```python import numpy as np
def main(): A = np.zeros((1, 3, 1)) B = A.squeeze() C = A.squeeze(axis=2)
print(f"{A.shape = }")
print(f"{B.shape = }")
print(f"{C.shape = }")
if name == "main": main() ```
Output:
A.shape = (1, 3, 1)
B.shape = (3,)
C.shape = (1, 3)
2. Selective Squeeze¶
```python import numpy as np
def main(): a = np.zeros((1, 4, 1, 5, 1))
print(f"Original: {a.shape}")
print()
# Squeeze all size-1 dimensions
print(f"squeeze(): {a.squeeze().shape}")
# Squeeze specific axis
print(f"squeeze(axis=0): {a.squeeze(axis=0).shape}")
print(f"squeeze(axis=2): {a.squeeze(axis=2).shape}")
print(f"squeeze(axis=4): {a.squeeze(axis=4).shape}")
if name == "main": main() ```
3. After Reduction¶
```python import numpy as np
def main(): a = np.array([[1, 2, 3], [4, 5, 6]])
# keepdims creates size-1 dimension
row_sum = a.sum(axis=1, keepdims=True)
print(f"With keepdims: {row_sum.shape}")
print(row_sum)
print()
# Squeeze removes it
squeezed = row_sum.squeeze()
print(f"After squeeze: {squeezed.shape}")
print(squeezed)
if name == "main": main() ```
Exercises¶
Exercise 1.
Create a 3x4 matrix and compute the sum along both axes as well as the global sum. Verify that np.sum(a) equals np.sum(np.sum(a, axis=0)).
Solution to Exercise 1
import numpy as np
a = np.arange(12).reshape(3, 4)
print(f"Global sum: {np.sum(a)}")
print(f"Sum of column sums: {np.sum(np.sum(a, axis=0))}")
print(f"Match: {np.sum(a) == np.sum(np.sum(a, axis=0))}")
Exercise 2.
Use np.cumsum on a 1D array [1, 2, 3, 4, 5] and verify the last element of the cumulative sum equals the total sum. Then apply np.cumprod to the same array and verify the last element equals np.prod(a).
Solution to Exercise 2
import numpy as np
a = np.array([1, 2, 3, 4, 5])
cs = np.cumsum(a)
print(f"cumsum: {cs}, last == sum: {cs[-1] == np.sum(a)}")
cp = np.cumprod(a)
print(f"cumprod: {cp}, last == prod: {cp[-1] == np.prod(a)}")
Exercise 3.
Use np.nansum and np.nanmean to compute the sum and mean of an array that contains np.nan values: a = np.array([1, 2, np.nan, 4, np.nan, 6]). Compare with np.sum(a) and np.mean(a) which propagate NaN.
Solution to Exercise 3
import numpy as np
a = np.array([1, 2, np.nan, 4, np.nan, 6])
print(f"np.sum: {np.sum(a)}") # nan
print(f"np.nansum: {np.nansum(a)}") # 13.0
print(f"np.mean: {np.mean(a)}") # nan
print(f"np.nanmean: {np.nanmean(a)}") # 3.25
Exercise 4.
Demonstrate that cumsum is discrete integration and np.diff is discrete differentiation. Starting with velocities = np.array([0, 2, 5, 3, 1]), compute positions via cumsum. Then recover the original velocities by applying np.diff to the positions (prepend 0). Verify the round-trip.
Solution to Exercise 4
import numpy as np
velocities = np.array([0, 2, 5, 3, 1])
# Integration: velocity → position
positions = np.cumsum(velocities)
print(f"Velocities: {velocities}")
print(f"Positions: {positions}") # [0 2 7 10 11]
# Differentiation: position → velocity
recovered = np.diff(positions, prepend=0)
print(f"Recovered: {recovered}") # [0 2 5 3 1]
print(f"Round-trip: {np.array_equal(velocities, recovered)}") # True
Exercise 5.
Use np.cumprod to compute the growth of a $1000 investment given daily returns r = np.array([0.01, -0.005, 0.02, -0.01, 0.015]). The portfolio value after day k is 1000 * cumprod(1 + r)[:k+1]. Print the portfolio value after each day.
Solution to Exercise 5
import numpy as np
initial = 1000
r = np.array([0.01, -0.005, 0.02, -0.01, 0.015])
growth = np.cumprod(1 + r)
portfolio = initial * growth
print(f"Daily returns: {r}")
print(f"Portfolio: {np.round(portfolio, 2)}")
# [1010.0, 1004.95, 1025.05, 1014.80, 1030.02]