New to Rust? Grab our free Rust for Beginners eBook Get it free →
np.outer in Python: compute the outer product of two vectors

Vectors show up everywhere in numerical Python work, from basic linear algebra to feature engineering for machine learning models. One operation that trips up a lot of beginners is the outer product, and NumPy gives us a direct way to calculate it through np.outer.
We shall cover the following in this article:
- What np.outer does and how it differs from other product functions
- The syntax and parameters of np.outer
- Worked examples with vectors of different lengths and dimensions
- How np.outer behaves with strings and other NumPy functions
- np.linalg.outer, the newer array API compatible version
- A full FAQ section
What is np.outer in NumPy
np.outer computes the outer product of two vectors, returning a matrix where every element from the first vector is multiplied against every element of the second. Given a vector a with M elements and a vector b with N elements, the result is an M by N matrix where position [i, j] holds a[i] * b[j].
Think of it as building a multiplication table. One vector runs down the rows, the other runs across the columns and every cell holds the product of the row value and the column value. This is different from the dot product, which collapses two vectors into a single number instead of expanding them into a grid.
Syntax and parameters of np.outer
The function signature looks like this:
numpy.outer(a, b, out=None)
Parameters:
- a: the first input vector. If it is not already one-dimensional, NumPy flattens it first.
- b: the second input vector. Same flattening rule applies.
- out: an optional ndarray of the correct shape where NumPy stores the result instead of allocating a new array. Most beginners can skip this and leave it as the default
None.
Returns: a two-dimensional ndarray with shape (M, N), where M is the length of a and N is the length of b.
A basic example of the outer product
Let’s start with two short one-dimensional arrays and see what np.outer produces.
import numpy as np
a = np.array([2, 5, 7])
b = np.array([1, 3])
result = np.outer(a, b)
print(result)
Output:
[[ 2 6]
[ 5 15]
[ 7 21]]
Here is what happened step by step. The first element of a, which is 2, gets multiplied by each element of b in turn: 2*1=2 and 2*3=6. That becomes the first row. The process then repeats for 5 and then for 7, giving three rows total since a has three elements and two columns since b has two elements.
What shape does np.outer return
The output of np.outer always has shape (M, N), where M is the number of elements in the first vector and N is the number of elements in the second vector. The two input vectors do not need to be the same length, unlike element-wise multiplication which requires matching shapes. This is one of the most common points of confusion for people coming from basic array math, so it is worth checking the shape of your inputs before you call the function if the output looks larger or smaller than you expected.
Using np.outer with arrays of different lengths
np.outer does not require equal-length vectors, which is part of what makes it useful. Let’s try a case where the vectors have different sizes.
import numpy as np
heights = np.array([150, 165, 180])
weights = np.array([55, 70])
combo = np.outer(heights, weights)
print(combo)
print("Shape:", combo.shape)
Output:
[[ 8250 10500]
[ 9075 11550]
[ 9900 12600]]
Shape: (3, 2)
The result has three rows matching the three heights and two columns matching the two weights. Every possible pairing between the two arrays shows up exactly once in the grid.
np.outer and multi-dimensional arrays
If you pass in an array that is already 2D or higher, np.outer flattens it into a 1D vector before doing the math. This catches people off guard the first time they see it, since it means the shape of your original array does not carry through to the result.
import numpy as np
grid_a = np.array([[3, 6], [9, 12]])
grid_b = np.array([[1, 2], [4, 5]])
flat_result = np.outer(grid_a, grid_b)
print(flat_result)
print("Shape:", flat_result.shape)
Output:
[[ 3 6 12 15]
[ 6 12 24 30]
[ 9 18 36 45]
[12 24 48 60]]
Shape: (4, 4)
Both grid_a and grid_b have four elements once flattened, so the result is a 4×4 matrix regardless of their original 2×2 shape. If you need to preserve the original structure and compute something dimension-aware instead, look at np.einsum, which lets you control exactly which axes get multiplied and summed, or reshape the flattened output back with np.reshape if you know the target dimensions in advance.
np.outer vs np.dot and np.inner
A question that comes up constantly is whether the outer product and the dot product are just two names for the same thing. They are not. np.dot and np.inner take two vectors of matching length and collapse them down to a single scalar value by multiplying corresponding elements and adding the results together. np.outer does the opposite: it expands two vectors into a full matrix without summing anything.
import numpy as np
x = np.array([2, 4])
y = np.array([1, 3])
print("Outer product:\n", np.outer(x, y))
print("Dot product:", np.dot(x, y))
Output:
Outer product:
[[2 6]
[4 12]]
Dot product: 10
Reach for np.dot, np.inner or np.vdot when you need a single similarity score or a projection. Reach for np.outer when you need every combination between two sets of values represented as a matrix, which is common in feature engineering, kernel methods and building coefficient grids for simulations.
Order matters too. np.outer(x, y) is not the same as np.outer(y, x), since swapping the arguments swaps which vector runs down the rows and which runs across the columns. The dot product does not have this issue for real-valued vectors, since addition and multiplication are commutative there.
Computing the outer product manually with broadcasting
You can reproduce what np.outer does using NumPy’s broadcasting rules directly, which helps build intuition for what is happening under the hood.
import numpy as np
a = np.array([2, 5, 7])
b = np.array([1, 3])
manual = a[:, np.newaxis] * b
built_in = np.outer(a, b)
print("Manual broadcasting result:\n", manual)
print("Matches np.outer?", np.array_equal(manual, built_in))
Output:
Manual broadcasting result:
[[ 2 6]
[ 5 15]
[ 7 21]]
Matches np.outer? True
a[:, np.newaxis] reshapes a into a column, turning it from shape (3,) into shape (3, 1). When you multiply that against b, which has shape (2,), NumPy’s broadcasting rules stretch both arrays until they line up, producing the same 3×2 result you get from np.outer. Seeing the manual version helps build intuition for more complex broadcasting patterns that np.outer alone cannot express.
A practical use case for np.outer in data work
Outer products show up naturally any time you need every pairwise combination between two feature sets. Say you are building a small recommendation model and you have a vector of user scores for a set of movies and a vector of weight adjustments for different genres.
import numpy as np
user_scores = np.array([4.2, 3.8, 5.0])
genre_weights = np.array([0.9, 1.1])
interaction_matrix = np.outer(user_scores, genre_weights)
print(interaction_matrix)
Output:
[[3.78 4.62]
[3.42 4.18]
[4.5 5.5 ]]
Every score gets paired against every weight in one call, without writing a nested loop. This same pattern shows up in polynomial feature expansion, building covariance-style matrices and initializing weight tables before further processing. Once you have a matrix like this, it is common to follow up with np.cumsum for running totals across a row or np.min to pull out the weakest interaction in the grid.
Using np.outer with strings and objects
np.outer works with more than plain numbers. If you set the array’s dtype to object, you can pass in strings and NumPy will repeat them the number of times specified by the second array instead of multiplying numerically.
import numpy as np
letters = np.array(['p', 'q', 'r'], dtype=object)
counts = np.array([1, 2, 3])
repeated = np.outer(letters, counts)
print(repeated)
Output:
[['p' 'pp' 'ppp']
['q' 'qq' 'qqq']
['r' 'rr' 'rrr']]
This works because Python’s string multiplication operator repeats a string N times when multiplied by an integer, and NumPy applies that same behavior element by element across the grid.
Combining np.outer with other NumPy functions
np.outer pairs naturally with array generation functions like np.ones and np.linspace when you need a quick grid of values for plotting or testing. Here is a small example that builds a coordinate-style grid.
import numpy as np
column_of_ones = np.ones(3)
spaced_values = np.linspace(0, 6, 3)
grid = np.outer(column_of_ones, spaced_values)
print(grid)
Output:
[[0. 3. 6.]
[0. 3. 6.]
[0. 3. 6.]]
Since every row of column_of_ones is 1, each row of the result is just a copy of spaced_values. This trick is handy for quickly building test matrices with a repeating row or column pattern, which is a common setup step before applying a transformation across a grid of coordinates. If you need a similar evenly spaced sequence but with a fixed step size instead of a fixed count, np.arange is the closer fit than np.linspace.
np.linalg.outer, the newer array API version
Recent NumPy releases added np.linalg.outer as part of the effort to align NumPy with the standard Python array API. Functionally, it computes the same result as np.outer, but it is stricter about its inputs since it only accepts genuinely one-dimensional arrays and will raise an error instead of silently flattening a multi-dimensional input.
import numpy as np
a = np.array([2, 5, 7])
b = np.array([1, 3])
print(np.linalg.outer(a, b))
Output:
[[ 2 6]
[ 5 15]
[ 7 21]]
If you are writing code meant to run across multiple array libraries that follow the array API standard, or you want NumPy to catch accidental multi-dimensional inputs instead of quietly flattening them, np.linalg.outer is worth reaching for over the classic np.outer.
Key Takeaways
- np.outer(a, b) multiplies every element of a by every element of b, forming a matrix instead of a single number
- The output shape is always (M, N) based on the input vector lengths
- Non-1D inputs get flattened automatically before the calculation runs
- np.outer differs from np.dot and np.inner, which collapse vectors into a scalar
- a[:, np.newaxis] * b reproduces np.outer manually through broadcasting
- Order matters, since np.outer(a, b) is not the same as np.outer(b, a)
- np.linalg.outer offers a stricter, array API compliant alternative for 1D-only inputs
FAQs
What does np.outer actually compute?
It computes the outer product of two vectors, returning a matrix where each entry is the product of one element from each input vector.
Is np.outer the same as matrix multiplication?
No. Matrix multiplication uses np.matmul or the @ operator and sums products along shared axes, while np.outer never sums anything and always expands to a full grid.
Do the two input vectors need to be the same length?
No, np.outer accepts vectors of different lengths and returns a matrix shaped (M, N) based on each vector’s length.
What happens if I pass a 2D array into np.outer?
NumPy flattens it into a 1D vector first, so the original shape does not affect the output beyond changing the total element count.
Can np.outer work with text data?
Yes, if you set the array dtype to object, np.outer will repeat string values instead of multiplying numbers.
When should I use np.linalg.outer instead of np.outer?
Use np.linalg.outer when you want NumPy to reject multi-dimensional inputs outright rather than silently flattening them, or when writing array API compliant code.
How is np.outer useful in machine learning?
It is commonly used to build pairwise interaction matrices between two feature vectors, such as combining user scores with category weights without writing a manual nested loop.




