Using numpy to build an array of all combinations of two arrays
In newer version of numpy
(>1.8.x), numpy.meshgrid()
provides a much faster implementation:
@pv's solution
In [113]:
%timeit cartesian(([1, 2, 3], [4, 5], [6, 7]))
10000 loops, best of 3: 135 µs per loop
In [114]:
cartesian(([1, 2, 3], [4, 5], [6, 7]))
Out[114]:
array([[1, 4, 6],
[1, 4, 7],
[1, 5, 6],
[1, 5, 7],
[2, 4, 6],
[2, 4, 7],
[2, 5, 6],
[2, 5, 7],
[3, 4, 6],
[3, 4, 7],
[3, 5, 6],
[3, 5, 7]])
numpy.meshgrid()
use to be 2D only, now it is capable of ND. In this case, 3D:
In [115]:
%timeit np.array(np.meshgrid([1, 2, 3], [4, 5], [6, 7])).T.reshape(-1,3)
10000 loops, best of 3: 74.1 µs per loop
In [116]:
np.array(np.meshgrid([1, 2, 3], [4, 5], [6, 7])).T.reshape(-1,3)
Out[116]:
array([[1, 4, 6],
[1, 5, 6],
[2, 4, 6],
[2, 5, 6],
[3, 4, 6],
[3, 5, 6],
[1, 4, 7],
[1, 5, 7],
[2, 4, 7],
[2, 5, 7],
[3, 4, 7],
[3, 5, 7]])
Note that the order of the final resultant is slightly different.
Using numpy to build an array of all combinations of two arrays
In newer version of numpy
(>1.8.x), numpy.meshgrid()
provides a much faster implementation:
@pv's solution
In [113]:
%timeit cartesian(([1, 2, 3], [4, 5], [6, 7]))
10000 loops, best of 3: 135 µs per loop
In [114]:
cartesian(([1, 2, 3], [4, 5], [6, 7]))
Out[114]:
array([[1, 4, 6],
[1, 4, 7],
[1, 5, 6],
[1, 5, 7],
[2, 4, 6],
[2, 4, 7],
[2, 5, 6],
[2, 5, 7],
[3, 4, 6],
[3, 4, 7],
[3, 5, 6],
[3, 5, 7]])
numpy.meshgrid()
use to be 2D only, now it is capable of ND. In this case, 3D:
In [115]:
%timeit np.array(np.meshgrid([1, 2, 3], [4, 5], [6, 7])).T.reshape(-1,3)
10000 loops, best of 3: 74.1 µs per loop
In [116]:
np.array(np.meshgrid([1, 2, 3], [4, 5], [6, 7])).T.reshape(-1,3)
Out[116]:
array([[1, 4, 6],
[1, 5, 6],
[2, 4, 6],
[2, 5, 6],
[3, 4, 6],
[3, 5, 6],
[1, 4, 7],
[1, 5, 7],
[2, 4, 7],
[2, 5, 7],
[3, 4, 7],
[3, 5, 7]])
Note that the order of the final resultant is slightly different.
Python - Find all possible combination between two arrays
If your purpose is to get x/y combinations for 3D plotting, what you're looking for is numpy.meshgrid
# input arrays
x = np.arange(1, 10)
y = np.arange(10, 100, 20)
# x = array([1, 2, 3, 4, 5, 6, 7, 8, 9])
# y = array([10, 30, 50, 70, 90])
# computing the meshgrid
X, Y = np.meshgrid(x,y)
output:
# X
array([[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9],
[1, 2, 3, 4, 5, 6, 7, 8, 9]])
# Y
array([[10, 10, 10, 10, 10, 10, 10, 10, 10],
[30, 30, 30, 30, 30, 30, 30, 30, 30],
[50, 50, 50, 50, 50, 50, 50, 50, 50],
[70, 70, 70, 70, 70, 70, 70, 70, 70],
[90, 90, 90, 90, 90, 90, 90, 90, 90]])
Numpy: efficient way to generate combinations from given ranges
I think what you're looking for is np.mgrid
. Unfortunately, this returns the array in a format that's different from what you need, so you'll need to do a little post-processing:
a = np.mgrid[0:4, 0:4, 0:11] # All points in a 3D grid within the given ranges
a = np.rollaxis(a, 0, 4) # Make the 0th axis into the last axis
a = a.reshape((4 * 4 * 11, 3)) # Now you can safely reshape while preserving order
Explanation
np.mgrid
gives you a set of grid points in N-dimensional space. Let me try to show this with a smaller example, to make things clearer:
>>> a = np.mgrid[0:2, 0:2]
>>> a
array([[[0, 0],
[1, 1]],
[[0, 1],
[0, 1]]])
Since I've given two sets of ranges, 0:2, 0:2
, I get a 2D grid. What mgrid
returns is the x-values and the y-values corresponding to the grid points (0, 0), (0, 1), (1, 0) and (1, 1) in 2D space. a[0]
tells you what the x-values of the four points are, and a[1]
tells you what the y-values are.
But what you really want is that list of actual grid points that I've written out, not the x- and y-values of those points separately. First instinct is to just reshape the array as desired:
>>> a.reshape((4, 2))
array([[0, 0],
[1, 1],
[0, 1],
[0, 1]])
But clearly this doesn't work, because it effectively reshapes the flattened array (the array obtained by just reading all elements in order), and that's not what you want.
What you want to do is to look down the third dimension of a
, and create an array:
[ [a[0][0, 0], a[1][0, 0]],
[a[0][0, 1], a[1][0, 1]],
[a[0][1, 0], a[1][1, 0]],
[a[0][1, 1], a[1][1, 1]] ]
which reads "First tell me the first point (x1, y1), then the second point (x2, y2), ..." and so on. Perhaps this is better explained with a figure, of sorts. This is what a
looks like:
you want to read
in this direction
(0, 0) (0, 1)
| |
| |
v v
/ 0--------0 +----> axis0
x-values | /| /| /|
| / | / | axis1 / |
\ 1--------1 | L |
| | | | v
/ | 0-----|--1 axis2
y-values | | / | /
| |/ |/
\ 0--------1
| |
| |
v v
(1, 0) (1, 1)
np.rollaxis
gives you a way to do this. np.rollaxis(a, 0, 3)
in the above example says "take the 0th (or outermost) axis and make it into the last (or innermost) axis. (Note: only axes 0, 1 and 2 actually exist here. So saying "send the 0th axis to the 3rd position" is a way of telling python to put the 0th axis after the last axis). You might also want to read this.
>>> a = np.rollaxis(a, 0, 3)
>>> a
array([[[0, 0],
[0, 1]],
[[1, 0],
[1, 1]]])
This is starting to look like what you want, except there's an extra array dimension. We want to merge dimensions 0 and 1 to get just get a single array of grid points. But now that the flattened array reads in the manner that you expect, you can safely reshape it to give you the desired result.
>>> a = a.reshape((4, 2))
>>> a
array([[0, 0],
[0, 1],
[1, 0],
[1, 1]])
The 3D version does just the same thing, except, I couldn't make a figure for that, since it'd be in 4D.
Numpy - Product of All Unique Combinations or Paths along an axis
One solution is to use itertools
to generate all the possible indices and then use Numpy indirect indexing so to extract the actual paths:
a = np.array([[1, 2, 3, 4, 5, 6],
[7, 8, 9, 10, 11, 12]])
rowCount = a.shape[0]**a.shape[1]
x = np.tile(np.arange(a.shape[1]), rowCount).reshape(-1, a.shape[1])
y = np.array(list(itertools.product(*[range(2)]*6)))
result = a[y, x]
product = np.prod(a[y, x], axis=-1)
Note that this solution is not very efficient with bigger arrays. In fact, as said in the comment, the number of possible paths grows exponentially (n**m
where n, m = a.shape
) and thus the required memory space grow in O(n**m * m)
. Even the size of the output product
grows experientially (n**m
).
All binary combinations in a 2d Numpy
Here is a vectorized solution that can work for small matrix sizes; comments in the code illustrate a 3 by 3 case:
def get_binary_mats(n):
# all possible n by n binary matrices up to rotation:
bin_mats = (np.bitwise_and(np.arange(2**(n*n))[:,None], 2 ** np.arange(n*n)) > 0)\
.reshape(-1, n, n)
# define a score for each matrix based on position of ones
score = 2 ** np.arange(n*n).reshape(n,n)
# array([[ 1, 2, 4],
# [ 8, 16, 32],
# [ 64, 128, 256]])
score_arr = np.stack([np.rot90(score, k=k) for k in range(4)])
# array([[[ 1, 2, 4],
# [ 8, 16, 32],
# [ 64, 128, 256]],
# [[ 4, 32, 256],
# [ 2, 16, 128],
# [ 1, 8, 64]],
# [[256, 128, 64],
# [ 32, 16, 8],
# [ 4, 2, 1]],
# [[ 64, 8, 1],
# [128, 16, 2],
# [256, 32, 4]]])
scores = np.einsum("ijk,ljk->il", bin_mats, score_arr)
_, idx = np.unique(scores.min(1), return_index=True)
return bin_mats[idx,...]
The main idea is that we can take a "dot product" of an N by N binary matrix with an N by N matrix consisting of first N^2 powers of 2 (I call the latter the score matrix). When we do this, we get a unique integer for each possible binary matrix.
To account for rotations, we can take a "dot product" with 4 rotations of the "score" matrix, and associate each binary matrix to the minimum of the four results. We can refer to this minimum as a score of a binary matrix.
Finally, we can pick a matrix for each unique score with np.unique
. For example, here is the output for n = 2:
array([[[False, False],
[False, False]],
[[ True, False],
[False, False]],
[[ True, True],
[False, False]],
[[False, True],
[ True, False]],
[[ True, True],
[ True, False]],
[[ True, True],
[ True, True]]])
As a sanity check, the number of binary matrices up to rotation lines up with this OEIS sequence for n up to 5:
%time assert [get_binary_mats(k).shape[0] for k in range(1, 6)] == [2, 6, 140, 16456, 8390720]
# takes 20 seconds on my machine
python efficiently applying function over multiple arrays
Use numpy.meshgrid
to make an array of all the combinations of values of the three variables.
products = np.array(np.meshgrid(accuracy, tranChance, numChoices)).T.reshape(-1, 3)
Then transpose this again and extract three longer arrays with the values of the three variables in every combination:
accuracy_, tranChance_, numChoices_ = products.T
Your function contains only operations that can be carried out on numpy arrays, so you can then simply feed these arrays as parameters into the function:
reward = ?? # you need to set the reward value
results = plot_ev(accuracy_, tranChance_, numChoices_, reward)
Alternatively consider using a pandas dataframe which will provide clearer labeling of the columns.
import pandas as pd
df = pd.DataFrame(products, columns=["accuracy", "tranChance", "numChoices"])
df["ev"] = plot_ev(df["accuracy"], df["tranChance"], df["numChoices"], reward)
All possible combinations of 2D numpy array
Here is a numpy
solution, based on the Cartesian product implementation from here.
arr = np.stack([a1, a2, a3, a4])
print(arr.shape) # (4, 6, 2)
n, m, k = arr.shape
# from https://stackoverflow.com/questions/11144513/cartesian-product-of-x-and-y-array-points-into-single-array-of-2d-points
def cartesian_product(*arrays):
la = len(arrays)
dtype = np.result_type(*arrays)
arr = np.empty([len(a) for a in arrays] + [la], dtype=dtype)
for i, a in enumerate(np.ix_(*arrays)):
arr[...,i] = a
return arr.reshape(-1, la)
inds = cartesian_product(*([np.arange(m)] * n))
res = np.take_along_axis(arr, inds.T[...,None], 1).swapaxes(0,1).reshape(-1, n*k)
print(res[0])
# [-24.4925 295.77 -26.0075 309.39 -25.5025 310.265 -27.0175 326.895 ]
In this example, the inds
array looks as follows:
print(inds[:10])
# [[0 0 0 0]
# [0 0 0 1]
# [0 0 0 2]
# [0 0 0 3]
# [0 0 0 4]
# [0 0 0 5]
# [0 0 1 0]
# [0 0 1 1]
# [0 0 1 2]
# [0 0 1 3]]
We can then use np.take_along_axis
to select appropriate elements for each combination.
Related Topics
Loop "Forgets" to Remove Some Items
Flask View Raises Typeerror: 'Bool' Object Is Not Callable
How to Create a Tuple With Only One Element
How to Get the Source Code of a Python Function
How Do Python'S Any and All Functions Work
Unicodeencodeerror: 'Charmap' Codec Can't Encode Characters
How to Open a Chrome Profile Through Python
Equivalent to Time.Sleep For a Pyqt Application
Pygame Doesn't Let Me Use Float For Rect.Move, But I Need It
How to Translate an Iso 8601 Datetime String into a Python Datetime Object
Pygame Window Not Responding After a Few Seconds
C Function Called from Python Via Ctypes Returns Incorrect Value
How to Add a New Column to an Existing Dataframe
Valueerror: Invalid Literal For Int() With Base 10: ''