Of course! Let's break down numpy.fromfunction in detail.

What is numpy.fromfunction?
numpy.fromfunction is a NumPy function that creates a NumPy array by executing a function over each coordinate of an array. In simpler terms, it generates an array of a given shape, and for each element in that array, it calls a provided function, passing the coordinates (indices) of that element as arguments. The return value of the function becomes the value of the element.
This is a powerful tool for creating arrays with values that are defined by a mathematical relationship over their indices.
The Core Idea: Function of Coordinates
Imagine you want to create a 2D array (a matrix) where the value at any position (i, j) is i + j.
- For the element at
(0, 0), the value should be0 + 0 = 0. - For the element at
(0, 1), the value should be0 + 1 = 1. - For the element at
(1, 0), the value should be1 + 0 = 1. - For the element at
(1, 1), the value should be1 + 1 = 2.
fromfunction does this automatically. You provide it with a function that knows how to calculate the value from the coordinates, and it builds the entire array for you.

Syntax and Parameters
numpy.fromfunction(function, shape, **kwargs)
Parameters:
function(callable): This is the core of the function. It's a Python function that takes N arguments, where N is the number of dimensions of the output array. The arguments are NumPy arrays representing the coordinates of each point. The function should return a scalar value.shape(tuple of ints): Defines the shape of the output array.dtype(data-type, optional): The data type of the returned array. If not specified, it's inferred from the function's return value.- `kwargs
**: Additional keyword arguments to pass to thefunction`.
Return Value:
- An array with the specified
shape, where each element's value is determined by applyingfunctionto its coordinates.
How It Works: The "Magic" of Broadcasting
This is the most important concept to understand. When you pass a function to fromfunction, NumPy doesn't call it for each element one by one in a slow Python loop. Instead, it does something highly efficient:
- It creates N arrays, where N is the number of dimensions.
- Each of these arrays has the same
shapeas the final output. - The first array contains the row index (0, 0, 0, 1, 1, 1, ...) at every position.
- The second array contains the column index (0, 1, 2, 0, 1, 2, ...) at every position.
- And so on for higher dimensions.
These arrays are then passed to your function. Your function then operates on these entire arrays at once using NumPy's fast, vectorized operations. This is why fromfunction is fast, not because of the function itself, but because of how NumPy prepares the input for it.

Let's visualize this for a shape of (3, 2):
-
NumPy creates two coordinate arrays:
coords_x(for the first argument, rows):[[0 0] [1 1] [2 2]]coords_y(for the second argument, columns):[[0 1] [0 1] [0 1]]
-
These two arrays are passed to your function, e.g.,
lambda x, y: x + y. -
The function is executed, performing the addition on the entire arrays:
[[0+0 0+1] [1+0 1+1] [2+0 2+1]] [[0 1] [1 2] [2 3]]This final matrix is the returned array.
Practical Examples
Example 1: Creating a 2D Addition Table
Let's create a 4x5 array where A[i, j] = i + j.
import numpy as np def add_indices(i, j): """A function that adds its two arguments.""" return i + j # Create a 4x5 array using fromfunction # The shape is (4, 5) add_table = np.fromfunction(add_indices, (4, 5), dtype=int) print(add_table)
Output:
[[0 1 2 3 4]
[1 2 3 4 5]
[2 3 4 5 6]
[3 4 5 6 7]]
Notice how the row index i increases down the rows and the column index j increases across the columns.
Example 2: Creating a Distance Grid from the Center
Let's create a 5x5 grid where each cell's value is its Manhattan distance from the center (2, 2). The formula is distance = |x - center_x| + |y - center_y|.
import numpy as np def manhattan_distance_from_center(x, y): """Calculates Manhattan distance from the center of a 5x5 grid.""" center_x, center_y = 2, 2 return np.abs(x - center_x) + np.abs(y - center_y) # Note: We use np.abs for element-wise absolute value distance_grid = np.fromfunction(manhattan_distance_from_center, (5, 5), dtype=int) print(distance_grid)
Output:
[[4 3 2 3 4]
[3 2 1 2 3]
[2 1 0 1 2]
[3 2 1 2 3]
[4 3 2 3 4]]
This creates a beautiful diamond-shaped pattern, which is a common use case for fromfunction.
Example 3: Creating a 3D Array
fromfunction works for any number of dimensions. Let's create a 3x3x3 cube where C[i, j, k] = i * j - k.
import numpy as np
def cube_function(i, j, k):
"""A function of three dimensions."""
return i * j - k
# Create a 3x3x3 array
cube_array = np.fromfunction(cube_function, (3, 3, 3), dtype=int)
print("Shape of the array:", cube_array.shape)
print("\nFirst 'slice' (k=0):")
print(cube_array[:, :, 0])
print("\nSecond 'slice' (k=1):")
print(cube_array[:, :, 1])
Output:
Shape of the array: (3, 3, 3)
First 'slice' (k=0):
[[ 0 0 0]
[-1 -2 -3]
[-2 -4 -6]]
Second 'slice' (k=1):
[-1 -1 -1]
[-2 -3 -4]
[-3 -5 -7]]
You can see how the value of k is subtracted from the i*j product for each slice.
When to Use fromfunction vs. Alternatives
fromfunction is elegant, but not always the most efficient or readable choice.
| Method | Best For | Pros | Cons |
|---|---|---|---|
np.fromfunction |
Creating arrays where values are a direct function of their indices. | - Very concise and expressive for index-based math. - Can be faster than pure Python loops. |
- Can be slower than NumPy's native array creation functions. - Can be less intuitive for beginners. - Requires the function to handle NumPy arrays as input. |
np.meshgrid |
Evaluating a function over a grid of points (often for plotting). | - More explicit and flexible about the coordinate grids. - Standard for creating coordinate grids for 3D plots. |
- More verbose for simple array creation. Often requires np.vectorize or a list comprehension to get the final array. |
| NumPy Math | Simple, standard arrays (e.g., of zeros, ones, ranges). | - Extremely fast and memory-efficient. - The standard, idiomatic way. |
- Limited to pre-defined patterns (lines, planes, constants). |
| List Comprehension | Complex logic that is hard to vectorize. | - Maximum flexibility and readability for complex Python logic. - Easy to debug. |
- Slow for large arrays due to Python interpreter overhead. |
A Note on Performance
While fromfunction avoids a slow Python for loop, it still has some overhead from the function call mechanism. For simple patterns, NumPy's built-in functions are almost always faster.
# Fastest way to create an addition table
N, M = 1000, 500
i, j = np.ogrid[:N, :M]
fast_add_table = i + j
# fromfunction is elegant but slower
def add_func(x, y):
return x + y
elegant_add_table = np.fromfunction(add_func, (N, M), dtype=float)
# List comprehension is the slowest
# slow_add_table = np.array([[i+j for j in range(M)] for i in range(N)])
Summary
Use numpy.fromfunction when:
- You need to create an array where each element's value is a direct mathematical function of its
(row, col, ...)indices. - You want a concise, readable way to express this relationship.
- The logic is too complex for simple
np.arangeornp.mgridtricks.
For simple, standard patterns, always prefer native NumPy functions like np.zeros, np.ones, np.arange, and broadcasting (i + j). For complex logic that doesn't fit an index-based function, a list comprehension is often the clearest choice.
