Generating all combinations of 1 and -1 in n Dimensions with numpy? - python

I need a way to generate a numpy array with all possible combination of [-1, 1] given a number of dimensions.
For example If i have 2 dimensions I would get :
[[1, 1], [1, -1], [-1, 1], [-1, -1]]
If I have 3 dimensions I would get:
[[1, 1, 1], [1, 1, -1], [1, -1, 1], [1, -1, -1], [-1, 1, 1], [-1, 1, -1], [-1, -1, 1], [-1, -1, -1]],
I have tried something like this :
import numpy as np
def permgrid(n):
inds = np.indices((2,) * n)
return inds.reshape(n, -1).T
But this only returns all combinations of 0 and 1.

You can use the product function from itertools.
Basically, you get all the combinations with repeat of 2.
print (list(itertools.product([1,-1], repeat=2)))
itertools.product(*iterables[, repeat])
Cartesian product of input iterables.
Roughly equivalent to nested for-loops in a generator expression.
You can read more in here

Here's a NumPy's broadcasting based method -
def broadcasting_typecast(n):
return -2*((np.arange(2**n)[:,None] & (1 << np.arange(n-1,-1,-1))) != 0)+1
Sample runs -
In [231]: n = 2
In [232]: broadcasting_typecast(n)
Out[232]:
array([[ 1, 1],
[ 1, -1],
[-1, 1],
[-1, -1]])
In [233]: n = 3
In [234]: broadcasting_typecast(n)
Out[234]:
array([[ 1, 1, 1],
[ 1, 1, -1],
[ 1, -1, 1],
[ 1, -1, -1],
[-1, 1, 1],
[-1, 1, -1],
[-1, -1, 1],
[-1, -1, -1]])

Either replace,
def permgrid(n):
inds = np.indices((2,) * n)
out = inds.reshape(n, -1).T
return np.where(out==0, -np.ones_like(out), out)
or do it with math:
def permgrid(n):
inds = np.indices((2,) * n)
return inds.reshape(n, -1).T*2-1

You might want to have a look at itertools. It's a package for the generation of sorted sequences and the like.
import itertools as it
for element in it.combinations_with_replacement([1,-1],3):
print element

you could use np.ix_. advantage: you can easily replace -1,1 with whatever you like (other numbers, other dtypes, more than 2, etc.)
>>> n = 3
>>> out = np.empty(n*(2,)+(n,), dtype=int)
>>> for j, sl in enumerate(np.ix_(*(n*((-1,1),)))):
... out[..., j] = sl
...
>>> out
array([[[[-1, -1, -1],
[-1, -1, 1]],
[[-1, 1, -1],
[-1, 1, 1]]],
[[[ 1, -1, -1],
[ 1, -1, 1]],
[[ 1, 1, -1],
[ 1, 1, 1]]]])
Optionally:
flat_out = np.reshape(out, (-1, n))

Related

Why is this function being applied to a variable which is not called as a parameter?

I am having trouble with some code I am attempting to write.
I am attempting to take a list of lists of coordinates (representing possible positions of a shape in 3D) and form a list which consists of all the elements in the original list and additionally the elements in the original list rotated so that the [x, y, z] coordinates are shifted to include [z, x, y] and [y, z, x] also.
I think this is better illustrated with an example:
Taking the list (representing the possible positions of a 2x2x1 block, hence "two_by_two"):
two_by_two = [
[[-1, -1, 1], [-1, -1, 0], [-1, 0, 0], [-1, 0, 1]],
[[-1, -1, 0], [-1, -1, -1], [-1, 0, -1], [-1, 0, 0]]
...
]
(the ellipses representing more similar lists of coordinates) I am attempting to form the complete list:
two_by_two_comp = [
[[-1, -1, 1], [-1, -1, 0], [-1, 0, 0], [-1, 0, 1]],
[[-1, -1, 0], [-1, -1, -1], [-1, 0, -1], [-1, 0, 0]]
...
[[1, -1, -1], [0, -1, -1], [0, -1, 0], [1, -1, 0]],
[[0, -1, -1], [-1, -1, -1], [-1, -1, 0], [0, -1, 0]]
...
[[-1, 1, -1], [-1, 0, -1], [0, 0, -1], [0, 1, -1]],
[[-1, 0, -1], [-1, -1, -1], [0, -1, -1], [0, 0, -1]]
...
]
I hope that this is clear.
I am attempting to achieve this by using a function which shifts all of the coordinates in two_by_two:
# function to change [x, y, z] to [z, x, y]
def rotate_coordinates(parameter):
coord_list = parameter[len(parameter) - 1]
coordinates = coord_list[len(coord_list) - 1]
z_coordinate = coordinates[2]
coordinates.pop()
coordinates.insert(0, z_coordinate)
# function to change list[x, y, z] to list[z, x, y]
def rotate_coord_list(parameter):
coord_list = parameter[len(parameter) - 1]
a = len(coord_list)
while a > 0:
coordinates = coord_list[len(coord_list) - 1]
rotate_coordinates(parameter)
coord_list.pop()
coord_list.insert(0, coordinates)
a = a - 1
# function to change list[list[x, y, z]] to list[list[z, x, y]]
def rotate_positions_list(parameter):
b = len(parameter)
while b > 0:
coord_list = parameter[len(parameter) - 1]
rotate_coord_list(parameter)
parameter.pop()
parameter.insert(0, coord_list)
b = b - 1
This seems to me to be successful in that when I run:
print(two_by_two)
rotate_positions_list(two_by_two)
print(two_by_two)
It outputs:
[[[-1, -1, 1], [-1, -1, 0], [-1, 0, 0], [-1, 0, 1]],
[[-1, -1, 0], [-1, -1, -1], [-1, 0, -1], [-1, 0, 0]]
...]
[[[1, -1, -1], [0, -1, -1], [0, -1, 0], [1, -1, 0]],
[[0, -1, -1], [-1, -1, -1], [-1, -1, 0], [0, -1, 0]]
...]
And so it shifts all of the coordinates as I intended, the issue arises when I try to begin creating two_by_two_comp as so:
two_by_two_comp = []
two_by_two_comp.extend(two_by_two)
print(two_by_two_comp)
rotate_positions_list(two_by_two)
two_by_two_comp.extend(two_by_two)
print(two_by_two_comp)
Which returns:
[[[-1, -1, 1], [-1, -1, 0], [-1, 0, 0], [-1, 0, 1]],
[[-1, -1, 0], [-1, -1, -1], [-1, 0, -1], [-1, 0, 0]]
...]
[[[1, -1, -1], [0, -1, -1], [0, -1, 0], [1, -1, 0]],
[[0, -1, -1], [-1, -1, -1], [-1, -1, 0], [0, -1, 0]],
...
[[1, -1, -1], [0, -1, -1], [0, -1, 0], [1, -1, 0]],
[[0, -1, -1], [-1, -1, -1], [-1, -1, 0], [0, -1, 0]]
...]
So I end up with the same "version" of two_by_two copied as opposed to the shifted and original version, and I have no idea why the section of two_by_two_comp which I print out first gets affected by the rotate_positons_list(two_by_two) function.
If anyone could clear up my confusion, I would be very grateful. I will include the full script in one piece below.
Thank you,
Dan
two_by_two = [
[[-1, -1, 1], [-1, -1, 0], [-1, 0, 0], [-1, 0, 1]],
[[-1, -1, 0], [-1, -1, -1], [-1, 0, -1], [-1, 0, 0]],
[[-1, 0, 0], [-1, 0, -1], [-1, 1, -1], [-1, 1, 0]],
[[-1, 0, 1], [-1, 0, 0], [-1, 1, 0], [-1, 1, 1]],
[[0, -1, 1], [0, -1, 0], [0, 0, 0], [0, 0, 1]],
[[0, -1, 0], [0, -1, -1], [0, 0, -1], [0, 0, 0]],
[[0, 0, 0], [0, 0, -1], [0, 1, -1], [0, 1, 0]],
[[0, 0, 1], [0, 0, 0], [0, 1, 0], [0, 1, 1]],
[[1, -1, 1], [1, -1, 0], [1, 0, 0], [1, 0, 1]],
[[1, -1, 0], [1, -1, -1], [1, 0, -1], [1, 0, 0]],
[[1, 0, 0], [1, 0, -1], [1, 1, -1], [1, 1, 0]],
[[1, 0, 1], [1, 0, 0], [1, 1, 0], [1, 1, 1]],
]
# function to change [x, y, z] to [z, x, y]
def rotate_coordinates(parameter):
coord_list = parameter[len(parameter) - 1]
coordinates = coord_list[len(coord_list) - 1]
z_coordinate = coordinates[2]
coordinates.pop()
coordinates.insert(0, z_coordinate)
# function to change list[x, y, z] to list[z, x, y]
def rotate_coord_list(parameter):
coord_list = parameter[len(parameter) - 1]
a = len(coord_list)
while a > 0:
coordinates = coord_list[len(coord_list) - 1]
rotate_coordinates(parameter)
coord_list.pop()
coord_list.insert(0, coordinates)
a = a - 1
# function to change list[list[x, y, z]] to list[list[z, x, y]]
def rotate_positions_list(parameter):
b = len(parameter)
while b > 0:
coord_list = parameter[len(parameter) - 1]
rotate_coord_list(parameter)
parameter.pop()
parameter.insert(0, coord_list)
b = b - 1
two_by_two_comp = []
two_by_two_comp.extend(two_by_two)
print(two_by_two_comp)
rotate_positions_list(two_by_two)
two_by_two_comp.extend(two_by_two)
print(two_by_two_comp)
Your problem lies in the difference between deep copy and shallow copy. As per the docs
Assignment statements in Python do not copy objects, they create bindings between a target and an object. For collections that are mutable or contain mutable items, a copy is sometimes needed so one can change one copy without changing the other.
The problematic line is thus:
two_by_two_comp.extend(two_by_two)
Let me illustrate with an example using two lists a and b:
a = [[2, 3, 4], [1, 2, 3]]
b = []
b.extend(a)
Now let's say I modify something inside a:
a[0].append(3)
print(a) # [[2, 3, 4, 3], [1, 2, 3]]
Everything is fine, but have a look at what happened to b in the meantime:
print(b) # [[2, 3, 4, 3], [1, 2, 3]]
It was also modified.
To achieve what you want, you need to create a deep copy of two_by_two otherwise you will just be referencing the same memory address. Long story short, instead of:
two_by_two_comp.extend(two_by_two)
You must do:
two_by_two_comp.extend(copy.deepcopy(two_by_two))
Don't forget to import the copy module at the top of your script:
import copy

Transform matrix via rotation of values

How would you go about transofrming the values of a matrix from
A=
[0, 1, 2]
[-1, 0, 1]
[-2, -1, 0]
To this:
[0, -1, -2]
[1, 0, -1]
[2, 1, 0]
The operation is a mirror over the y=-x axis
In numpy, do .T:
>>> A = np.array([[0, 1, 2],
[-1, 0, 1],
[-2, -1, 0]])
>>> A.T
array([[ 0, -1, -2],
[ 1, 0, -1],
[ 2, 1, 0]])
>>>
In regular python, do zip:
>>> A = [[0, 1, 2],
[-1, 0, 1],
[-2, -1, 0]]
>>> list(zip(*A))
[(0, -1, -2), (1, 0, -1), (2, 1, 0)]
>>>

How to remove duplicates in nested lists?

I want to remove both duplicates and permutations from my nested list.
Input:
[[-1, 0, 1], [-1, 1, 0], [-1, 2, -1], [-1, 2, -1], [-1, -1, 2]]
Expected Output:
[[-1, 0, 1], [-1, 2, -1]]
I tried using a list comprehension but I end up with the output as
[[-1, 1, 0], [-1, 2, -1], [-1, 0, 1], [-1, -1, 2]]
Here is what I attempted.
a = [[-1, 0, 1], [-1, 1, 0], [-1, 2, -1], [-1, 2, -1], [-1, -1, 2]]
b_set = set(tuple(x) for x in a)
b = [ list(x) for x in b_set ]
print(b)
The result is expected because [-1, 0, 1] != [-1, 1, 0]. You can sort the inner tuples if you want to make sure that they are considered equal:
b_set = set(tuple(sorted(x)) for x in a)
Or with map:
b_set = set(map(lambda x: tuple(sorted(x)),a))

Logical indexing in python for nd arrays

I am trying to extract all the indexes from an (N x N x N) numpy array, where values in both A and B arrays are equal to some value x - find the common overlap.
I am trying:
A[A==1 and B==1]
but get an error:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
How do I get around this?
Numpy cannot overload the "and" keyword. However it overloads the binary AND operator & for this. Try:
A[(A==1) & (B==1)]
The parantheses are important. I find it often (not always)better readable then logical_and
A == 1 and B == 1 are boolean arrays, while (A==1)*(B==1) is an array of integers. You can find the nonzero entries of this array through NumPy's where:
np.where((A==1)*(B==1))
Demo
Consider the following 3-dimensional arrays, which are randomly populated with values -1, 0 and 1:
In [1066]: import numpy as np
In [1067]: np.random.seed(2016) # this is to get the same results on multiple runs
In [1068]: N = 3
...: A = np.random.randint(low=-1, high=2, size=(N, N, N))
...: B = np.random.randint(low=-1, high=2, size=(N, N, N))
In [1069]: A
Out[1069]:
array([[[ 1, 1, 0],
[-1, 1, -1],
[-1, -1, -1]],
[[ 0, 1, 1],
[-1, 1, 1],
[ 0, 1, 0]],
[[ 0, 1, 0],
[-1, 1, 1],
[-1, 1, 0]]])
In [1070]: B
Out[1070]:
array([[[-1, 0, 0],
[-1, -1, 1],
[ 0, -1, -1]],
[[-1, -1, -1],
[-1, 1, 1],
[-1, 1, 1]],
[[ 1, 1, -1],
[-1, 0, 1],
[-1, 1, -1]]])
The function where returns a tuple of integer arrays which triggers advanced indexing:
In [1071]: idx = np.where((A==1)*(B==1))
In [1072]: idx
Out[1072]:
(array([1, 1, 1, 2, 2, 2], dtype=int64),
array([1, 1, 2, 0, 1, 2], dtype=int64),
array([1, 2, 1, 1, 2, 1], dtype=int64))
In [1073]: A[idx]
Out[1073]: array([1, 1, 1, 1, 1, 1])
In [1074]: B[idx]
Out[1074]: array([1, 1, 1, 1, 1, 1])
perhaps slightly hasty posting this question. Used numpy's
logical_and(x1, x2[, out])
in the end which did the job perfectly!

Matrix multiplying arrays with Numpy

I have a 5x5 array of arrays and I'm trying to matrix multiply the transpose of one row with another row.
import numpy as np
a = np.array([1, 4, 6, 4, 1])
b = np.array([-1, -2, 0, 2, 1])
c = np.array([-1, 2, 0, -2, 1])
d = np.array([-1, 0, 2, 0, -1])
e = np.array([1, -4, 6, -4, 1])
f = np.vstack([a, b, c, d, e])
result = np.dot(f[1, :].T, f[1, :])
I assumed this would work but apparently
f[1, :].T
ends up becoming
[-1, -2, 0, 2, 1]
rather than
[[-1]
[-2]
[ 0]
[ 2]
[ 1]]
and so np.dot treats it like a real dot product rather than doing matrix multiplication.
I found out that list slicing where one index is an integer and all others are :s reduces the dimension by one so so the shape of f[1, :] is not (1, 5) but (5,) and so transposing it does nothing.
I've been able to get it to working using f[1, :].reshape((1, 5)) but is there a better way of doing this? Am I missing a simple way of getting the transpose without having to reshape it?
You can use np.newaxis to add a dimension when slicing, to compensate for the dimension that is otherwise lost.
f[1, :, np.newaxis]
produces the single-column 2D array you want. Putting np.newaxis before the colon would give a single-row 2D array.
For numpy arrays it is often favorable to have this behavior, to circumvent this you can always use the numpy matrix class.
>>> f = np.matrix(f)
>>> f
matrix([[ 1, 4, 6, 4, 1],
[-1, -2, 0, 2, 1],
[-1, 2, 0, -2, 1],
[-1, 0, 2, 0, -1],
[ 1, -4, 6, -4, 1]])
>>> f[1,:].T
matrix([[-1],
[-2],
[ 0],
[ 2],
[ 1]])
>>> np.dot(f[1, :].T, f[1, :])
matrix([[ 1, 2, 0, -2, -1],
[ 2, 4, 0, -4, -2],
[ 0, 0, 0, 0, 0],
[-2, -4, 0, 4, 2],
[-1, -2, 0, 2, 1]])
As this is the matrix class * will denote matrix multiplication, therefore you can simply use:
f[1,:].T * f[1,:]
Also you may want to consider np.outer for this kind of operation:
>>> np.outer(f[1,:],f[1,:])
array([[ 1, 2, 0, -2, -1],
[ 2, 4, 0, -4, -2],
[ 0, 0, 0, 0, 0],
[-2, -4, 0, 4, 2],
[-1, -2, 0, 2, 1]])
If you want the individual slices to retain their "matrixness" then you should cast f to a numpy.matrix, which preserves the matrixness.
fm = numpy.matrix(f)
then
numpy.dot(fm[1,:].T,fm[1,:])
will return an nxn matrix
Following the accepted answer, I prefer to use None instead of np.newaxis, which is a little verbose for my tastes. For example,
f[:,None]
does the same thing as f[:,np.newaxis].
I am late to the party here, it is worth noting that the NumPy webpage I accessed today warns that
class numpy.matrix(data, dtype=None, copy=True)
might be removed.

Categories

Resources