Chapter 7: NumPy Products
1. What do we mean by “products” in NumPy?
In NumPy, “products” usually refer to multiplication operations and product reductions (like summing, but multiplying).
There are two main families:
- Element-wise multiplication → happens between arrays of the same shape (or broadcastable shapes) → this is the most common operation you do every day
- Product reduction (like sum, but multiply all elements together) → collapses an axis (or the whole array) into a single product → very useful in probability, logarithms, factorial-like calculations, cumulative products, etc.
2. The three most important product-related ufuncs
| Function | What it does | Returns | Common use case |
|---|---|---|---|
| np.multiply / * | element-wise multiplication | new array | almost everything |
| np.prod() | product of all elements (or along axis) | scalar or reduced array | total product, probability chains |
| np.cumprod() | cumulative product along axis | array of same shape | running product, growth factors |
3. Element-wise multiplication – the most common operation
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
a = np.array([2, 3, 4, 5]) b = np.array([10, 20, 30, 40]) print("a * b =", a * b) # [ 20 60 120 200] print("np.multiply(a, b) =", np.multiply(a, b)) # same # With scalar print("a * 100 =", a * 100) # [200 300 400 500] # With broadcasting print("a * [1, 2, 3, 4] =", a * np.array([1, 2, 3, 4])) |
2D example – very frequent in real work
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
matrix = np.arange(1, 13).reshape(3, 4) print("matrix =\n", matrix) weights = np.array([1.5, 2.0, 0.5, 3.0]) print("\nmatrix * weights (broadcasts row-wise):") print(matrix * weights) |
Output:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
matrix = [[ 1 2 3 4] [ 5 6 7 8] [ 9 10 11 12]] matrix * weights: [[ 1.5 4. 1.5 12. ] [ 7.5 12. 3.5 24. ] [13.5 20. 5.5 36. ]] |
4. np.prod() – the “sum, but multiply” function
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
a = np.array([1, 2, 3, 4, 5]) print("np.prod(a) =", np.prod(a)) # 120 print("a.prod() =", a.prod()) # 120 # With axis m = np.arange(1, 13).reshape(3, 4) print("\nm =\n", m) print("\nProduct of each column:", m.prod(axis=0)) # [ 45 240 693 1344] print("Product of each row:", m.prod(axis=1)) # [ 24 1680 11880] |
Important trap: Product can become huge very quickly → overflow is common with integers.
|
0 1 2 3 4 5 6 7 |
big = np.arange(1, 25) print(np.prod(big)) # → huge number or overflow if int32/int64 |
Safe habit — use dtype=float or dtype=object when product might overflow:
|
0 1 2 3 4 5 6 |
print(np.prod(big, dtype=float)) # safe, but loses precision at very large values |
5. np.cumprod() – cumulative / running product
Very useful in:
- growth factors
- probability chains
- compound interest
- sequential multiplication
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
returns = np.array([1.02, 0.98, 1.05, 1.03, 0.99]) # daily multipliers cumulative_growth = np.cumprod(returns) print("Daily multipliers:", returns) print("Cumulative product:", cumulative_growth.round(3)) # [1. 0.98 1.029 1.06 1.049] |
Plot example – very common in finance / growth simulation
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
days = np.arange(len(cumulative_growth)) plt.plot(days, cumulative_growth, marker='o', ms=6, lw=1.8, color='teal') plt.axhline(1, color='gray', ls='--', lw=0.8) plt.title("Cumulative growth from daily returns") plt.xlabel("Day") plt.ylabel("Growth factor") plt.show() |
6. Realistic patterns you will write again and again
Pattern 1 – Normalize probabilities / softmax style
|
0 1 2 3 4 5 6 7 8 9 10 11 |
scores = np.random.randn(1000, 5) * 2 # Very common mistake: product of probabilities → underflow # Better: work in log space log_probs = scores - np.log(np.sum(np.exp(scores), axis=1, keepdims=True)) probs = np.exp(log_probs) |
Pattern 2 – Cumulative product for investment returns
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
monthly_returns = 1 + np.random.normal(0.008, 0.04, 60) # ~10% annual wealth_path = 100000 * np.cumprod(monthly_returns) plt.plot(wealth_path, lw=1.5, color='darkblue') plt.title("Simulated portfolio growth (60 months)") plt.ylabel("Portfolio value ($)") plt.xlabel("Month") plt.show() |
Pattern 3 – Product along axis for ratios / factors
|
0 1 2 3 4 5 6 7 8 9 |
ratios = np.random.uniform(0.8, 1.2, size=(100, 12)) # monthly growth factors yearly_product = ratios.prod(axis=1) # annual growth factor per row print("Average annual growth factor:", yearly_product.mean().round(3)) |
Pattern 4 – Safe product with very small numbers
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
small_probs = np.random.uniform(0.001, 0.1, 100) # Direct product → underflow → 0.0 print("Direct product:", small_probs.prod()) # Safe way: sum of logs log_prod = np.sum(np.log(small_probs)) safe_product = np.exp(log_prod) print("Safe product via log:", safe_product.round(8)) |
7. Summary – NumPy Product Operations Quick Reference
| Operation / Function | Syntax example | Typical use case |
|---|---|---|
| Element-wise multiply | a * b, np.multiply(a, b) | scaling, weighting, masking |
| Total product | arr.prod() or np.prod(arr) | total multiplication |
| Product along axis | arr.prod(axis=0) | row/column products |
| Cumulative product | np.cumprod(arr) | running product, growth |
| Safe product (no underflow) | np.exp(np.sum(np.log(arr))) | very small probabilities |
Final teacher advice (very important)
Golden rule #1 Use * for element-wise multiplication — it is clean, fast, and idiomatic.
Golden rule #2 When you need the product of many numbers (especially small ones), work in log space:
|
0 1 2 3 4 5 6 7 |
log_result = np.sum(np.log(values)) result = np.exp(log_result) |
This avoids underflow (product → 0) and overflow.
Golden rule #3 When you see loops like this:
|
0 1 2 3 4 5 6 7 8 |
product = 1 for x in arr: product *= x |
→ replace it immediately with arr.prod() or np.prod(arr).
Would you like to continue with any of these topics?
- Handling underflow/overflow in products
- Cumulative product vs cumulative sum in time series
- Products along multiple axes (3D, 4D arrays)
- Realistic mini-project: simulate investment growth or probability chains
- Difference between prod vs multiply vs dot
Just tell me what you want to focus on next! 😊
