appending multidimensional elements into numpy arrays without reshaping - python

I have a few simple questions I'm not able to find the answer to. They are both stated in the following example code. Thank you for any help!
import numpy as np
#here are two arrays to join together
a = np.array([1,2,3,4,5])
b = np.array([6,7,8,9,10])
#here comes the joining step I don't know how to do better
#QUESTION 1: How to form all permutations of two 1D arrays?
temp = np.array([]) #empty array to be filled with values
for aa in a:
for bb in b:
temp = np.append(temp,[aa,bb]) #fill the array
#QUESTION 2: Why do I have to reshape? How can I avoid this?
temp = temp.reshape((int(temp.size/2),2))
edit: made code more minimal

To answer your first question, you can use np.meshgrid to form those combinations between elements of the two input arrays and get to the final version of temp in a vectorized manner avoiding those loops, like so -
np.array(np.meshgrid(a,b)).transpose(2,1,0).reshape(-1,2)
As seen, we would still need a reshape if you intend to get a 2-column output array.
There are other ways we could construct the array with the meshed structure and thus avoid a reshape. One of those ways would be with np.column_stack, as shown below -
r,c = np.meshgrid(a,b)
temp = np.column_stack((r.ravel('F'), c.ravel('F')))

The proper way to build an array iteratively is with list append. np.append is poorly named, and often mis used.
In [274]: a = np.array([1,2,3,4,5])
...: b = np.array([6,7,8,9,10])
...:
In [275]: temp = []
In [276]: for aa in a:
...: for bb in b:
...: temp.append([aa,bb])
...:
In [277]: temp
Out[277]:
[[1, 6],
[1, 7],
[1, 8],
[1, 9],
[1, 10],
[2, 6],
....
[5, 9],
[5, 10]]
In [278]: np.array(temp).shape
Out[278]: (25, 2)
It's better to avoid loops at all, but if you must, use this list append approach.

Related

Accessing a numpy array with an array of indices

I'm trying to access a numpy array A using another array B providing the indices at each position:
A = np.array([[1,2],[3,4]])
B = np.array([[[0,0],[0,0]],[[0,1],[0,1]]])
Desired output:
C = array([[1,1],[3,3]])
I haven't gotten it to work using np.take() or the advanced indexing.
I could do it iteratively but my arrays are on the order of 10**7 so I was hoping for a faster way.
I probably should have insisted on seeing the iterative solution first, but here's the array one:
In [45]: A[B[:,:,1], B[:,:,0]]
Out[45]:
array([[1, 1],
[3, 3]])
I first tried A[B[:,:,0], B[:,:,1]], the natural order of the inner dimension. Your own code could have saved me that trial.
The key with advanced indexing is that you have to create or define separate arrays (broadcastable) for each dimension. We can think of that index as a tuple:
idx = (B[:,:,0], B[:,:,1])
A[idx]
Adding on #hpaulj an alternative way is:
idx = tuple(B[:,:,[1,0]].transpose(2,0,1))
A[idx]
# array([[1, 1], [3, 3]])

Get all the rows with same values in python?

So, suppose I have this 2D array in python
a = [[1,2]
[2,3]
[3,2]
[1,3]]
How do get all array entries with the same row value and store them in a new matrix.
For example, I will have
b = [1,2]
[1,3]
after the query.
My approach is b = [a[i] for i in a if a[i][0] == 1][0]]
but it didn't seem to work?
I am new to Python and the whole index slicing thing is kind confusing. Thanks!
Since you tagged numpy, you can perform this task with NumPy arrays. First define your array:
a = np.array([[1, 2],
[2, 3],
[3, 2],
[1, 3]])
For all unique values in the first column, you can use a dictionary comprehension. This is useful to avoid duplicating operations.
d = {i: a[a[:, 0] == i] for i in np.unique(a[:, 0])}
{1: array([[1, 2],
[1, 3]]),
2: array([[2, 3]]),
3: array([[3, 2]])}
Then access your array where first column is equal to 1 via d[1].
For a single query, you can simply use a[a[:, 0] == 1].
The for i in a syntax gives you the actual items in the list..so for example:
list_of_strs = ['first', 'second', 'third']
first_letters = [s[0] for s in list_of_strs]
# first_letters == ['f', 's', 't']
What you are actually doing with b = [a[i] for i in a if a[i][0]==1] is trying to index an element of a with each of the elements of a. But since each element of a is itself a list, this won't work (you can't index lists with other lists)
Something like this should work:
b = [row for row in a if row[0] == 1]
Bonus points if you write it as a function so that you can pick which thing you want to filter on.
If you're working with arrays a lot, you might also check out the numpy library. With numpy, you can do stuff like this.
import numpy as np
a = np.array([[1,2], [2,3], [3,2], [1,3]])
b = a[a[:,0] == 1]
The last line is basically indexing the original array a with a boolean array defined inside the first set of square brackets. It's very flexible, so you could also modify this to filter on the second element, filter on other conditions (like > some_number), etc. etc.

Numpy: How to stack arrays in columns?

Let's say that I have n numpy arrays of the same length. I would like to now create a numpy matrix, sucht that each column of the matrix is one of the numpy arrays. How can I achieve this? Now I'm doing this in a loop and it produces the wrong results.
Note: I have to be able to stack them next to each other one by one iteratively.
my code looks like assume that get_array is a function that returns a certain array based on its argument. I don't know until after the loop, how many columns that I'm going to have.
matrix = np.empty((n_rows,))
for item in sorted_arrays:
array = get_array(item)
matrix = np.vstack((matrix,array))
any help would be appreciated
You could try putting all your arrays (or lists) into a matrix and then transposing it. This will work if all arrays are the same length.
mymatrix = np.asmatrix((array1, array2, array3)) #... putting arrays into matrix.
mymatrix = mymatrix.transpose()
This should output a matrix with each array as a column. Hope this helps.
Time and again, we recommend collecting the arrays in a list, and making the final array with one call. That's more efficient, and usually easier to get right.
alist = []
for item in sorted_arrays:
alist.append(get_array(item)
or
alist = [get_array(item) for item in sorted_arrays]
There are various ways of assembling the list. Since you want columns, and assuming get_array produces equal sized 1d arrays:
arr = np.column_stack(alist)
Collecting them in rows and transposing that works too:
arr = np.array(alist).T
arr = np.vstack(alist).T
arr = np.stack(alist).T
arr = np.stack(alist, axis=1)
If the arrays are already 2d
arr = np.concatenate(alist, axis=1)
All the stack variations use concatenate, just varying in how they tweak the shape(s) of the input arrays. The key to using concatenate is to understand the dimensions and shapes, and how to add dimensions as needed. That should, soon or later, become fluent in that kind of coding.
If they vary in shape or dimensions, things get messier.
Equally good is to put the arrays in a pre-allocated array. But you need to know the desired final shape
arr = np.zeros((m,n), dtype)
for i, item in enumerate(sorted_arrays):
arr[:,i] = get_array(item)
n is len(sorted_arrays), and m is the length of one of get_array(item). You also need to know the expected dtype (int, float etc).
If you have a, b, c, d np array of same length, the following code will accomplish what you want:
out_matrix = np.vstack([a, b, c, d]).transpose()
An example:
In [3]: a = np.array([1, 2, 3, 4])
In [4]: b = np.array([5, 6, 7, 8])
In [5]: c = np.array([2, 3, 4, 5])
In [6]: d = np.array([6, 8, 2, 4])
In [10]: np.vstack([a, b, c, d]).transpose()
Out[10]:
array([[1, 5, 2, 6],
[2, 6, 3, 8],
[3, 7, 4, 2],
[4, 8, 5, 4]])

Is there aliasing in a 2-D array in Python?

I know that list aliasing is an issue in Python, but I can't figure out a way around it.
def zeros(A):
new_mat = A
for i in range(len(A)):
for j in range(len(A[i])):
if A[i][j]==0:
for b in range(len(A)):
new_mat[b][j] = 0
else:
new_mat[i][j] = A[i][j]
return A
Even though I don't change the values of A at all, when I return A, it is still modified:
>>> Matrix = [[1,2,3],[5,0,78],[7,3,45]]
>>> zeros(Matrix)
[[1, 0, 3], [5, 0, 78], [7, 0, 45]]
Is this list aliasing? If so, how do you modify elements of a 2D array without aliasing occurring? Thanks so muhc <3.
new_mat = A does not create a new matrix. You have merely given a new name to the object you also knew as A. If it's a list of lists of numbers, you might want to use copy.deepcopy to create a full copy, and if it's a numpy array you can use the copy method.
new_mat = A[:]
This creates a copy of the list instead of just referencing it. Give it a try.
[:] just specifies a slice from beginning to end. You could have [1:] and it would be from element 1 to the end, or [1:4] and it would be element 1 to 4, for instance. Check out list slicing regarding that.
This might help someone else. just do this.
import copy
def zeros(A):
new_mat = copy.deepcopy(A)
QUICK EXPLANATION
If you want A to be the original matrix your function should be like this. Just use np.copy() function
def zeros(A):
new_mat = np.copy(A) #JUST COPY IT WITH np.copy() function
for i in range(len(A)):
for j in range(len(A[i])):
if A[i][j]==0:
for b in range(len(A)):
new_mat[b][j] = 0
else:
new_mat[i][j] = A[i][j]
return A
THOROUGH EXPLANATION
Let's say we don't want numpy ndarray a to have aliasing. For example, we want to prevent a to change any of its values when we take (for example) a slice of it, we assign this slice to b and then we modify one element of b. We want to AVOID this:
import numpy as np
a = np.array([[1,2,3],[4,5,6],[7,8,9]])
b = a[0]
b[2] = 50
a
Out[]:
array([[ 1, 2, 50],
[ 4, 5, 6],
[ 7, 8, 9]])
Now you might think that treating a numpy-ndarray object as if it was a list object might solve our problem. But it DOES NOT WORK either:
%reset #delete all previous variables
a = np.array([[1,2,3],[4,5,6],[7,8,9]])
b = a[:][0] #this is how you would have copied a slice of "a" if it was a list
b[2] = 50
a
Out[]:
array([[ 1, 2, 50], #problem persists
[ 4, 5, 6],
[ 7, 8, 9]])
The problem gets solved, in this case, if you think of numpy arrays as different objects than python lists. In order to copy numpy arrays you can't do copy = name_array_to_be_copied[:], but copy = np.copy(name_array_to_be_copied). Therefore, this would solve our aliasing problem:
%reset #delete all previous variables
import numpy as np
a = np.array([[1,2,3],[4,5,6],[7,8,9]])
b = np.copy(a)[0] #this is how you copy numpy ndarrays. Calling np.copy() function
b[2] = 50
a
Out[]:
array([[1, 2, 3], #problem solved
[4, 5, 6],
[7, 8, 9]])
P.S. Watch out with the zeros() function. Even after fixing the aliasing issue, your function does not convert to 0 the columns in the new_matrix who have at least one zero in the same column for the matrix A (this is what I think you wanted to acomplish by seeing the incorrectly reported output of your function [[1, 0, 3], [5, 0, 78], [7, 0, 45]], since it actually yields [[1,0,3],[5,0,78],[7,3,45]]). If you want that you can try this:
def zeros_2(A):
new_mat = np.copy(A)
for i in range(len(A[0])): #I assume each row has same length.
if 0 in new_mat[:,i]:
new_mat[:,i] = 0
print(new_mat)
return A

Subtracting a value from all elements of a 2D list in python

I have a 2D list and I want to subtract a floating point value from all the elements of the 2D list:
mat = [[1, 2], [3, 4]]
cons = 13
mat1 = cons - mat
But the last line doesn't work. How to do this in a 2D list in python?
Thank you.
If you do a lot of such things, consider using numpy:
In [1]: import numpy as np
In [2]: mat = np.array([[1, 2], [3, 4]])
In [3]: cons = 13
In [4]: cons-mat
Out[4]:
array([[12, 11],
[10, 9]])
you can use a nested list comprehension:
mat1 = [[(cons - x) for x in row] for row in mat]
You cannot substract a number from a list or the other way round.
If you want to stick to the builtin list-type you could do this with a nested comprehension:
mat1 = [[cons - m for m in sublist] for sublist in mat]
If your lists are, presumably, larger than the one in your example it might be worthwhile to check out the numpy module, which allows such operations.

Categories