Related
As in the title, if I have a matrix a
a = np.diag(np.arange(5))
array([[0, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 2, 0, 0],
[0, 0, 0, 3, 0],
[0, 0, 0, 0, 4]])
How can I assign a new 4x4 matrix or even 3x4 matrix to a without i-th row and i-th column? Let's say
b = array([[1,1,1,1],
[1,1,1,1],
[1,1,1,1])
I want to slice a and remove the first and second row and the second column of the matrix, which is something in R like
a[c(-1,-2), -2] = b
a =
array([[0, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[1, 0, 1, 1, 1],
[1, 0, 1, 1, 1],
[1, 0, 1, 1, 1]])
But in python, I tried something like
a[[2,3,4],:][:,[0,1,3,4]]
output:
array([0, 2, 0, 0],
[0, 0, 3, 0],
[0, 0, 0, 4]])
This operation won't allow me to assign a new matrix to slices of a.
How can I do that? I really appreciate any help you can provide.
p.s.
I found in this special case, I can assign values by blocks. But what I actually want to ask is when we do slice like a[2:5, [0,2,3,4]], we can get a 3x4 matrix, and assign a new matrix to that position of the matrix. But I want to do is to slice 'a[[0,2,3,4],[0,2,3,4]]` to get a 4x4 matrix or other shapes(the index for row and column may even be random), and assign a new matrix to that position. But numpy gives me a 1d array.
newmatrix = a[[0, 1, 3, 4], :][:, [0, 1, 3, 4]]
Regarding setting the values of a matric part of a larger matrix, I think there is no direct option. But you can create the original matrix around the one to be added:
before = np.array([[0, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[0, 0, 2, 0, 0],
[0, 0, 0, 3, 0],
[0, 0, 0, 0, 4]])
insert_array = np.array([[1, 1, 1, 1],
[1, 1, 1, 1],
[1, 1, 1, 1]])
first two rows without second column
first_step = np.delete(before[:2, :], 1, 1)
or
first_step = before[:2, [0, 2, 3, 4]]
appended to insert matrix
second_step = np.insert(insert_array, 0, first_step, axis=0)
second column appended
third_step = np.insert(second_step, 1, before[:, 1], axis=1)
final matrix
third_step = np.array([[0, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[1, 0, 1, 1, 1],
[1, 0, 1, 1, 1],
[1, 0, 1, 1, 1]])
I can't find a one-step solution to do that. But I think we can assign matrix by block.
a[2:5, 0] = 1
a[2:5, 2:5] = 1
Then I can get what I want.
For editors: this is NOT stripping all strings in an array but stripping the array itself
So suppose i have an array like this:
[[0, 1, 8, 4, 0, 0],
[1, 2, 3, 0, 0, 0],
[3, 2, 3, 0, 5, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]]
I want a function stripArray(0, array) where the first argument is the "empty" value. After applying this function i want the returned array to look like this:
[[0, 1, 8, 4, 0],
[1, 2, 3, 0, 0],
[3, 2, 3, 0, 5]]
Values that were marked as empty (in this case 0) were stripped from the right and bottom sides. How would I go about implementing such a function?
In the real case where I want to use it in the array instead of numbers there are dictionaries.
It is better to do this vectorized
import numpy as np
arr = np.array([[0, 1, 8, 4, 0, 0],
[1, 2, 3, 0, 0, 0],
[3, 2, 3, 0, 5, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]])
def stripArray(e, arr):
return arr[(arr!=e).any(axis = 1), :][:, (arr!=e).any(axis = 0)]
stripArray(0, arr)
array([[0, 1, 8, 4, 0],
[1, 2, 3, 0, 0],
[3, 2, 3, 0, 5]])
Here is an answer which doesnt need numpy:
from typing import List, Any
def all_value(value: Any, arr: List[float]) -> bool:
return all(map(lambda x: x==value, arr))
def transpose_array(arr: List[List[float]]) -> List[List[float]]:
return list(map(list, zip(*arr)))
def strip_array(value: Any, arr: List[List[float]]) -> List[List[float]]:
# delete empty rows
arr = [row for row in arr if not all_value(value, row)]
#transpose and delete empty columns
arr = transpose_array(arr)
arr = [col for col in arr if not all_value(value, col)]
#transpose back
arr = transpose_array(arr)
return arr
test = [[0, 1, 8, 4, 0, 0],
[1, 2, 3, 0, 0, 0],
[3, 2, 3, 0, 5, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]]
result = strip_array(0, test)
Output:
result
[[0, 1, 8, 4, 0],
[1, 2, 3, 0, 0],
[3, 2, 3, 0, 5]]
Code:
def strip_array(array, empty_val=0):
num_bad_columns = 0
while np.all(array[:, -(num_bad_columns+1)] == 0):
num_bad_columns += 1
array = array[:, :(-num_bad_columns)]
num_bad_rows = 0
while np.all(array[-(num_bad_rows+1), :] == 0):
num_bad_rows += 1
array = array[:(-num_bad_rows), :]
return array
array = np.array(
[[0, 1, 8, 4, 0, 0],
[1, 2, 3, 0, 0, 0],
[3, 2, 3, 0, 5, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]]
)
print(array)
print(strip_array(array, 0))
Output:
[[0 1 8 4 0 0]
[1 2 3 0 0 0]
[3 2 3 0 5 0]
[0 0 0 0 0 0]
[0 0 0 0 0 0]]
[[0 1 8 4 0]
[1 2 3 0 0]
[3 2 3 0 5]]
try using np.delete to remove unwanted rows or columns
data=[[0, 1, 8, 4, 0, 0],
[1, 2, 3, 0, 0, 0],
[3, 2, 3, 0, 5, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]]
def drop_row(data):
lstIdx=[]
for i in range(len(data)):
count=0
for j in range(len(data[i])):
if data[i][j] == 0:
count+=1
if count==len(data[i]):
print("row zero")
lstIdx.append(i)
#for i in lstIdx:
data=np.delete(data,lstIdx,axis=0)
return data
def drop_column(data):
lstIdx=[]
if len(data)==0:
return data
for j in range(len(data[0])):
count=0
for i in range(len(data)):
if data[i][j] == 0:
count+=1
if count==len(data):
print("column zero")
lstIdx.append(j)
data=np.delete(data,lstIdx,axis=1)
return data
data=drop_row(data)
data=drop_column(data)
print(data)
output:
[[0 1 8 4 0]
[1 2 3 0 0]
[3 2 3 0 5]]
Say I have a numpy array like this:
np.array([[0, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[1, 1, 1, 0, 0],
[2, 2, 2, 1, 0],
[3, 3, 3, 2, 1]])
How can I get the indices of the values that have a zero orthogonaly adjacent to it?
By orthogonaly I mean it wouldn't look at diagonally adjacent items.
For the example the output should be:
np.array([[1, 1], [2, 0], [2, 2], [3, 3], [4, 4]])
Is there something wrong with the obvious approach ?
mask = arr == 0
out = np.zeros(mask.shape, dtype='bool')
out[:-1] = out[:-1] | mask[1:]
out[1:] = out[1:] | mask[:-1]
out[:,:-1] = out[:,:-1] | mask[:,1:]
out[:,1:] = out[:,1:] | mask[:,:-1]
np.argwhere(out & (arr!=0))
I have a numpy array of 0's and 1's.
def random_array(p):
return np.random.choice(2, 6, p=[p, 1-p])
my_matrix = np.array([random_array(j) for j in np.random.uniform(0.3, 1.0, 4)])
I can get indices of all num-zero elements as np.nonzero(my_matrix). May you help me to allocate all these indices to the column number (even column indices will work, where we start from 0 rather 1) in which it is present. For example,
array([[0, 2, 0, 4, 5, 6],
[0, 0, 0, 4, 5, 0],
[1, 2, 0, 4, 0, 0],
[0, 2, 3, 4, 5, 6]])
Here, all 1's have been replaced with the column number. Thus, this is for a matrix whose all non-zero elements were 1. If you can find the indices of column then that would also be fibulas because, I can get the same by adding 1.
Note: I do not wish to use any loop for this task.
If I understand well, you want to replace all no zero elements with their column indices (starting from 1 instead of 0) right ?
Then you can do it like:
idx = np.nonzero(my_matrix)
my_matrix[idx[0], idx[1]] = idx[1]+1
You can multiply your array with another array of the same size containing the corresponding row/column index:
## Dummy data
# Array size
s = (6,4)
# Axis along which we need to calculate the index:
a = 0
# Random binary array
x = np.random.rand(*s).round()
# Get the index along one axis using broadcasting (starting with 1)
x = x*(np.expand_dims(range(s[a]),len(s)-a-1)+1)
In [169]: def random_array(p):
...: return np.random.choice(2, 6, p=[p, 1-p])
...: my_matrix = np.array([random_array(j) for j in np.random.uniform(0.3, 1.0, 4)])
In [170]: my_matrix
Out[170]:
array([[0, 0, 0, 0, 0, 0],
[0, 1, 1, 0, 0, 0],
[0, 0, 1, 1, 0, 1],
[1, 1, 0, 0, 1, 0]])
Just multiply the range index. By broadcasting (6,) arange is fine for columns:
In [171]: np.arange(1,7)*my_matrix
Out[171]:
array([[0, 0, 0, 0, 0, 0],
[0, 2, 3, 0, 0, 0],
[0, 0, 3, 4, 0, 6],
[1, 2, 0, 0, 5, 0]])
for rows
In [172]: np.arange(1,5)[:,None]*my_matrix
Out[172]:
array([[0, 0, 0, 0, 0, 0],
[0, 2, 2, 0, 0, 0],
[0, 0, 3, 3, 0, 3],
[4, 4, 0, 0, 4, 0]])
I have three (n,n) arrays that I need to combine in a very specific way, in order to yield n*n new arrays, that have to be combined into one big array.
I essentially need to take one element from each array and create a new (3,3) array, wherein the diagonal is the three elements (the rest is empty) and then combine these new arrays into one.
It's a bit difficult to explain properly. I've attempted to give an example below which hopefully gives an idea of what I'm trying to do.
Example: Given three (2,3) arrays:
a = np.array([[2,5,9], [7,2,4]])
b = np.array([[3,6,2], [1,6,8]])
c = np.array([[8,7,4], [9,3,1]])
create six arrays with the elements from a, b, and c as the diagonals:
T1 = ([[ 2, 0, 0],
[ 0, 3, 0],
[ 0, 0, 8]])
T2 = ([[ 5, 0, 0],
[ 0, 6, 0],
[ 0, 0, 7]])
T3 = ([[ 9, 0, 0],
[ 0, 2, 0],
[ 0, 0, 4]])
T4 = ([[ 7, 0, 0],
[ 0, 1, 0],
[ 0, 0, 9]])
T5 = ([[ 2, 0, 0],
[ 0, 6, 0],
[ 0, 0, 3])
T6 = ([[ 4, 0, 0],
[ 0, 8, 0],
[ 0, 0, 1]])
combine the six arrays to yield
array([[ 2, 0, 0, 5, 0, 0, 9, 0, 0],
[ 0, 3, 0, 0, 6, 0, 0, 2, 0],
[ 0, 0, 8, 0, 0, 7, 0, 0, 4],
[ 7, 0, 0, 2, 0, 0, 4, 0, 0],
[ 0, 1, 0, 0, 6, 0, 0, 8, 0],
[ 0, 0, 9, 0, 0, 3, 0, 0, 1]])
as in
array([[ T1, T2, T3],
[ T4, T5, T6]])
*The six arrays are not needed in themselves as separate arrays, only the final array is needed. I've just chosen this route as it makes it a bit more apparent what the final one consists of.
It can be done with einsum:
ABC = np.array((a,b,c))
i,j,k = ABC.shape
out = np.zeros((i*j,i*k),ABC.dtype)
np.einsum("jiki->ijk",out.reshape(j,i,k,i))[...] = ABC
out
# array([[2, 0, 0, 5, 0, 0, 9, 0, 0],
# [0, 3, 0, 0, 6, 0, 0, 2, 0],
# [0, 0, 8, 0, 0, 7, 0, 0, 4],
# [7, 0, 0, 2, 0, 0, 4, 0, 0],
# [0, 1, 0, 0, 6, 0, 0, 8, 0],
# [0, 0, 9, 0, 0, 3, 0, 0, 1]])
Explanation:
What does the reshape do?
axis 2 (size k)
/-----------------------\
axis 3 (size i)
/-----\ /-----\ /-----\
a s / a s / [[2, 0, 0, 5, 0, 0, 9, 0, 0],
x i | x i | [0, 3, 0, 0, 6, 0, 0, 2, 0],
i z | i z \ [0, 0, 8, 0, 0, 7, 0, 0, 4],
s e | s e / [7, 0, 0, 2, 0, 0, 4, 0, 0],
| | [0, 1, 0, 0, 6, 0, 0, 8, 0],
0 j \ 1 i \ [0, 0, 9, 0, 0, 3, 0, 0, 1]]
It isolates the 3x3 diagonal matrices into axes 1,3.
What does einsum do here?
It maps the axes of the reshaped out to those of ABC;
"jiki->ijk" means that axis 0 ("j") maps to axis 1, axes 1 and 3 ("i") map to axis 0, and axis 2 ("k") maps to axis 2.
Mapping two axes to one (as with "i") has the special meaning of taking the diagonal.
einsum creates a writeable view, so all that's left to do is assigning ABC to that.
Note: that we use the same letters i,j,k for the shape and for the einsum spec doesn't syntactically mean anything, it just makes the thing a lot more readable.
We can combine the 3 arrays with stack (or np.array):
In [65]: a = np.array([[2,5,9], [7,2,4]])
...: b = np.array([[3,6,2], [1,6,8]])
...: c = np.array([[8,7,4], [9,3,1]])
In [66]: abc = np.stack((a,b,c))
In [67]: abc.shape
Out[67]: (3, 2, 3)
One 'column' of abc is one of your diagonals:
In [68]: abc[:,0,0]
Out[68]: array([2, 3, 8])
Make a target array to hold all 6 diagonals:
In [69]: TT = np.zeros((6,3,3),int)
We can then set one diagonal with:
In [70]: idx=np.arange(3)
In [71]: TT[0,idx,idx] = abc[:,0,0]
In [72]: TT
Out[72]:
array([[[2, 0, 0],
[0, 3, 0],
[0, 0, 8]],
...
To set all 6 we need an array that matches this shape:
In [74]: TT[:,idx,idx].shape
Out[74]: (6, 3)
Reshape abc. The result is (3,6). Transpose to make a (6,3):
In [75]: abc.reshape(3,6)
Out[75]:
array([[2, 5, 9, 7, 2, 4],
[3, 6, 2, 1, 6, 8],
[8, 7, 4, 9, 3, 1]])
In [76]: TT[:,idx,idx] = abc.reshape(3,6).T
In [77]: TT
Out[77]:
array([[[2, 0, 0],
[0, 3, 0],
[0, 0, 8]],
[[5, 0, 0],
[0, 6, 0],
[0, 0, 7]],
[[9, 0, 0],
[0, 2, 0],
[0, 0, 4]],
[[7, 0, 0],
[0, 1, 0],
[0, 0, 9]],
[[2, 0, 0],
[0, 6, 0],
[0, 0, 3]],
[[4, 0, 0],
[0, 8, 0],
[0, 0, 1]]])
Rearrange elements with reshapes and transpose:
In [82]: TT.reshape(2,3,3,3).transpose(0,2,1,3).reshape(6,9)
Out[82]:
array([[2, 0, 0, 5, 0, 0, 9, 0, 0],
[0, 3, 0, 0, 6, 0, 0, 2, 0],
[0, 0, 8, 0, 0, 7, 0, 0, 4],
[7, 0, 0, 2, 0, 0, 4, 0, 0],
[0, 1, 0, 0, 6, 0, 0, 8, 0],
[0, 0, 9, 0, 0, 3, 0, 0, 1]])
I came up that, step by step. You may want to recreate those steps for yourself. I won't take up the space here.
There may be more direct ways of creating this, but I think the steps are instructive.
Here is one way to do that with advanced indexing:
import numpy as np
a = np.array([[2, 5, 9], [7, 2, 4]])
b = np.array([[3, 6, 2], [1, 6, 8]])
c = np.array([[8, 7, 4], [9, 3, 1]])
# Put all input arrays together
abc = np.stack([a, b, c])
# Works with any shape and number of arrays
n, r, c = abc.shape
# Row and column index grid
ii, jj = np.ogrid[:r, :c]
# Shift row and column indices over submatrices of result
idx = np.arange(n)[:, np.newaxis, np.newaxis]
row_idx = ii * n + idx
col_idx = jj * n + idx
# Broadcast indices
row_idx, col_idx = np.broadcast_arrays(row_idx, col_idx)
# Make output
out = np.zeros((n * r, n * c), abc.dtype)
out[row_idx, col_idx] = abc
print(out)
# [[2 0 0 5 0 0 9 0 0]
# [0 3 0 0 6 0 0 2 0]
# [0 0 8 0 0 7 0 0 4]
# [7 0 0 2 0 0 4 0 0]
# [0 1 0 0 6 0 0 8 0]
# [0 0 9 0 0 3 0 0 1]]
I am unsure as to why you would need to do this, but I believe that I have answered your question anyway. The code is roughly commented, and the variable names are slightly odd, however, it does what you wanted it to do and it does it in the way you suggested above. The code is not very efficient or fast, though it could be cleaned up and made much faster. It takes the arrays you want to convert into the larger output array, makes them the diagonals of 6 3x3 arrays, and then inserts them into the required spot in the output array.
# Import numpy
import numpy as np
# Create your arrays
a = np.array([[2,5,9], [7,2,4]])
b = np.array([[3,6,2], [1,6,8]])
c = np.array([[8,7,4], [9,3,1]])
# Make them into a list
abc = []
abc.append(a)
abc.append(b)
abc.append(c)
# Create an array that will contain T1, T2, ...
arrays = []
for i in range(6):
arr = np.ndarray(shape=(3, 3))
# Fill the array with zeros
for x in range(3):
for y in range(3):
arr[x][y] = 0
for j in range(3):
arr[j][j] = abc[j][0 if i < 3 else 1][i % 3]
arrays.append(arr)
# Combine the arrays into one, in the way specified
bigarr = np.ndarray(shape=(6, 9))
offsetX = 0
offsetY = 0
arr = 0
# Loop over all of the arrays (T1, T2, etc.)
for arr in range(6):
for i in range(3):
for j in range(3):
bigarr[i + offsetX][j + offsetY] = arrays[arr][i][j]
# Offset the place the arrays will be inserted
offsetY += 3
if offsetY >= 9:
offsetY = 0
offsetX += 3
# The final output is bigarr
print(bigarr)
I hope this answers your question, and if not helps you find another answer.