How to construct matrix based on condition and position? - python

I have a matrix A=
np.matrix([[0, 0, 0, 0, 0],
[0, 0, 0, 1, 1],
[0, 0, 1, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 1, 1, 1]]
I wanna build a matrix B where B[i,j]=5 if A[i,j]=1 and (i+1)%3=0; B[i,j]=0 otherwise.
The B should be: B=
np.matrix([[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 5, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 5, 0, 0]]
Is there any possible method to achieve this without using for loop, just like matrix calculation? Thank you.

UPDATED ANSWER:
I have not thought of a way to eliminate the for-loop in the list comprehension for filtering on the remainder condition, but the "heavy" part of the computation exploits numpy's optimizations.
import numpy as np
newdata = 5 * np.array([(i + 1) % 3 == 0 for i in range(data.shape[-1])]) * np.array(data)
ORIGINAL ANSWER (prior to condition that for-loops cannot be used):
Assuming your matrix is stored as data, then you can use list comprehension syntax to get what you want.
newdata = [[5 if val == 1 and (idx + 1) % 3 == 0 else 0
for idx, val in enumerate(row)]
for row in data]

Related

How to set values in a 2d numpy array given 1D indices for each row?

In numpy you can set the indices of a 1d array to a value
import numpy as np
b = np.array([0, 0, 0, 0, 0])
indices = [1, 3]
b[indices] = 1
b
array([0, 1, 0, 1, 0])
I'm trying to do this with multi-rows and an index for each row in the most programmatically elegant and computationally efficient way possible. For example
b = np.array([[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]])
indices = [[1, 3], [0, 1], [0, 3]]
The desired result is
array([[0, 1, 0, 1, 0],
[1, 1, 0, 0, 0],
[1, 0, 0, 1, 0]])
I tried b[indices] and b[:,indices] but they resulted in an error or undesired result.
From searching, there are a few work arounds, but each tends to need at least 1 loop in python.
Solution 1: Run a loop through each row of the 2d array. The draw back for this is that the loop runs in python, and this part won't take advantage of numpy's c processing.
Solution 2: Use numpy put. The draw back is put works on a flattened version of the input array, so the indices need to be flattened too, and altered by the row size and number of rows, which would use a double for loop in python.
Solution 3: put_along_axis seems to only be able to set 1 value per row, so I would need to repeat this function for the number of values per row.
What would be the most computationally and programatically elegant solution? Anything where numpy would handle all the operations?
In [330]: b = np.zeros((3,5),int)
To set the (3,2) columns, the row indices need to be (3,1) shape (matching by broadcasting):
In [331]: indices = np.array([[1,3],[0,1],[0,3]])
In [332]: b[np.arange(3)[:,None], indices] = 1
In [333]: b
Out[333]:
array([[0, 1, 0, 1, 0],
[1, 1, 0, 0, 0],
[1, 0, 0, 1, 0]])
put along does the same thing:
In [335]: b = np.zeros((3,5),int)
In [337]: np.put_along_axis(b, indices,1,axis=1)
In [338]: b
Out[338]:
array([[0, 1, 0, 1, 0],
[1, 1, 0, 0, 0],
[1, 0, 0, 1, 0]])
On solution to build the indices in each dimension and then use a basic indexing:
from itertools import chain
b = np.array([[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]])
# Find the indices along the axis 0
y = np.arange(len(indices)).repeat(np.fromiter(map(len, indices), dtype=np.int_))
# Flatten the list and convert it to an array
x = np.fromiter(chain.from_iterable(indices), dtype=np.int_)
# Finaly set the items
b[y, x] = 1
It works even for indices lists with variable-sized sub-lists like indices = [[1, 3], [0, 1], [0, 2, 3]]. If your indices list always contains the same number of items in each sub-list then you can use the (more efficient) following code:
b = np.array([[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]])
indices = np.array(indices)
n, m = indices.shape
y = np.arange(n).repeat(m)
x = indices.ravel()
b[y, x] = 1
Simple one-liner based on Jérôme's answer (requires all items of indices to be equal-length):
>>> b[np.arange(np.size(indices)) // len(indices[0]), np.ravel(indices)] = 1
>>> b
array([[0, 1, 0, 1, 0],
[1, 1, 0, 0, 0],
[1, 0, 0, 1, 0]])

How to loop by specific step

I have a nested loop data=
[[1,43,344,546],[2,34,45,565]....[10,1,1,1],
[1,15,111,151],[2,152,28,19]....[10,2,2,2],
[1,21,45,1647],[2,288,65,90]....[10,3,3,3]
.....]
so basically, all the inside lists can be grouped by 10lists with the first element always starting from 1 to 10. taking the every 10th list as a key, so I want to calculate a newlist by subtracting every list's number by the 10th list accordingly, for example
[[1,43-1,344-2,546-3], [2,34-1,45-2,565-3].... [10,1,2,3],
[1,15-21,111-22,151-23],[2,152-21,28-22,19-23]....[10,21,22,23],
[1,21-31,45-32,1647-33],[2,288-31,65-32,90-33]....[10,31,32,33]
.....]
My code seems don't work, can someone plz help with this? thanks
line = 0
while line <= (len(data) - 10):
for i in range(line, line + 10):
temp = data[i]
if temp[0] == 10: #find the every 10 th keys and store them to x, y, z
x = temp[1]
y = temp[2]
z = temp[3]
break
for sublist in data:
sublist[1] = sublist[1] - x# assign new elements to original list data
sublist[2] = sublist[2] - y
sublist[3] = sublist[3] - z
line += 10
return data
This is one way to do what you want
from pprint import pprint
def next_mul(n):
"returns next multiple of 10 greater than n"
return n + (10 - n % 10)
# sample data
data = [[i for i in range(4)] for i in range(20)]
for i in range(len(data)):
if i % 10 == 0:
continue
data[i-1][1:] = [k-l for k,l in zip(data[next_mul(i)-1][1:], data[i-1][1:])]
pprint(data)
Output (originally every sublist of data was just [0, 1, 2, 3]):
[[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 1, 2, 3],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 1, 2, 3]]
Hope this helps. Please let me know if there are any questions/concerns!

Edit every other item in an array

matrix = np.array([[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]])
vector = np.array([0,0,0,0])
For vectors, you can edit every other element like so
vector[1::2] = 1
This gives
np.array([0,1,0,1])
However;
matrix[1::2] = 1
yields
np.array([[0,0,0,0],[1,1,1,1],[0,0,0,0],[1,1,1,1]])
I would like the output
np.array([[0,1,0,1],[0,1,0,1],[0,1,0,1],[0,1,0,1]])
There is a brute force approach to take the shape of the array, flatten it, use [1::2], and reshape, but i'm sure there is a more elegant solution i am missing.
Any help would be appreciated.
You can do something similar with multidimensional indexing
>>> matrix
array([[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]])
>>> matrix[:,1::2] = 1
>>> matrix
array([[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 1]])

Initialise a diagonal matrix in python

i'd like create in python a matrix with all zeros except for some value selected from a list. For example (very stupid) from
l=[0,1,2,3]
i'd like create a matrix (a list of list) with the letter "x" in position l[0] l[1] etc.. like this:
[x, 0, 0, 0]
[0, x, 0, 0]
[0, 0, x, 0]
[0, 0, 0, x]
i'd like make it interactive, with a variable length (not always 4) maybe giving on input
You should use numpy's diag function.
import numpy as np
np.diag(l)
array([[0, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 2, 0],
[0, 0, 0, 3]])
With pure python, initialise an empty 2D list and populate the diagonal after.
diag = [[0] * len(l) for _ in range(len(l))]
for i, e in enumerate(l):
diag[i][i] = e
diag
# [[0, 0, 0, 0], [0, 1, 0, 0], [0, 0, 2, 0], [0, 0, 0, 3]]
If I understood you correctly, you want non-zero values to be placed based on the list. So, maybe something like this:
val = 'x'
l = [2,1,0,3]
matrix = [[val if i == e else 0 for i in range(len(l))] for e in l]
print(matrix)
Output will be like this:
[[0, 0, 'x', 0], [0, 'x', 0, 0], ['x', 0, 0, 0], [0, 0, 0, 'x']]

Insert newline print statement into (nested)? for loop?

I'm trying to print a matrix with user assigned variable for the length and width, but have used manual assignment for easier reading. Right now my output doesn't include any new lines, but that is what I'm attempting to do.
def matrix(rows,cols):
grid = [[0 for i in range(cols)] for i in range(rows)]
return grid
rows = 5
cols = 5
print(matrix(rows,cols))
Is it possible to insert a print("\n") statement into the for statement to properly print out the matrix. Currently the output is as follows:
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
Desired output:
[[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]]
That happens to be the exact behaviour of pprint.
from pprint import pprint
def matrix(rows,cols):
grid = [[0 for i in range(cols)] for i in range(rows)]
return grid
rows = 5
cols = 5
pprint(matrix(rows,cols))
pprint doing what you need is nice, but shouldn't you be using a class for your matrix type? Using a naked list of lists may bite you later.

Categories