how to modify a numpy matrix element-wise - python

I am currently trying to iterate over a matrix and modifying the elements inside it following some logic.
I tried using the standard procedure for iterating matrices, but this only outputs the element at the current index, without updating the matrix itself.
This is what i have tried:
for row in initial_matrix:
for element in row:
if np.random.rand() > 0.5: element = 0
print(element)
print(initial_matrix)
This, however, does not update initial matrix, I also tried:
for row in range(len(initial_matrix)):
for element in range(row):
if np.random.rand() > 0.5: initial_matrix[row, element] = 0
print(element)
print(initial_matrix)
This is somehow working, but only in the lower diagonal of the matrix, while the upper remains unchanged.
Here is the output:
0
0
1
0
1
2
0
1
2
3
[[1. 1. 1. 1. 1.]
[0. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[0. 0. 1. 1. 1.]
[0. 1. 1. 0. 1.]]

import numpy as np
initial_matrix = np.ones([10,5])
print(initial_matrix)
for row in initial_matrix:
for element in row:
if np.random.rand() > 0.5:
element = 0
# Nothing will change
print(initial_matrix)
Basically you're not changing the values for the initial matrix with this approach
[[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1.]]
to better understand this let's take a simple example
initial_list=[1,1,1,1]
for i in initial_list:
i=0
print(initial_list)
this will output the initial list as it is without any modifications because you're modifying the variable i and not the contents of the list itself, if you want to modify the list you can do something like this instead :
initial_list=[1,1,1,1]
for i in range(len(initial_list)):
initial_list[i]=0
print(initial_list)
Now let's apply the same thing to your problem
#Iterate through the rows and columns and change the initial matrix
for i in range(initial_matrix.shape[0]):
for j in range(initial_matrix.shape[1]):
if np.random.rand() > 0.5:
initial_matrix[i,j] = 0
print(initial_matrix)
[[0. 0. 0. 0. 0.]
[0. 1. 1. 1. 0.]
[0. 1. 0. 0. 1.]
[0. 1. 0. 1. 1.]
[1. 0. 1. 0. 1.]
[0. 1. 1. 0. 0.]
[0. 1. 0. 0. 1.]
[1. 0. 0. 1. 0.]
[1. 0. 0. 0. 0.]
[0. 1. 0. 0. 0.]]

Here's a minimalist modification (UPDATED to use np.array throughout) to your code which will do what I believe you are asking:
import numpy as np
initial_matrix = np.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,1]])
for row in range(len(initial_matrix)):
for element in range(len(initial_matrix[row])):
if np.random.rand() > 0.5:
initial_matrix[row, element] = 0
print(initial_matrix)
Output:
[[0 1 1 1 0]
[1 1 1 0 0]
[0 0 0 0 0]
[0 1 1 0 0]
[1 0 0 1 0]]
Here, I have assumed that you start with a matrix containing 1 in every position and that you want to change this to 0 where your random() criterion is met.
As you can see, an adjustment to the inner loop logic of your original code was helpful in getting this to work.

import numpy as np
a = np.random.rand(3,4)
print(a)
b = np.random.rand(3,4)
print(b)
a[ b > 0.5]=0
print(a)
a = a > 0.5
print(a.astype(int))
You can index into the array with boolean results like this.
Output:
[[0.21577153 0.4810459 0.88036672 0.93817657]
[0.48424368 0.88673521 0.26706288 0.47468637]
[0.02435961 0.75210616 0.18391152 0.80976478]]
[[0.27385928 0.84570069 0.55326907 0.57076882]
[0.11333208 0.26364198 0.26381841 0.57497278]
[0.29163378 0.08612894 0.37857834 0.59921316]]
[[0.21577153 0. 0. 0. ]
[0.48424368 0.88673521 0.26706288 0. ]
[0.02435961 0.75210616 0.18391152 0. ]]
[[0 0 0 0]
[0 1 0 0]
[0 1 0 0]]
If you want to output boolean array in terms of integers you can use astype() function.

Related

How to modify values of a column of a 2D tensor based on condition - Tensorflow?

I have a 2D tensor and want values of its last column to be 0 if values > 0 and 1 otherwise. It should behave somewhat similar to the following block of numpy code:
x = np.random.rand(8, 4)
x[:, -1] = np.where(x[:, -1] > 0, 0, 1)
Is there a way to achieve the same behavior for a 2D tensor in Tensorflow?
This might not be the most elegant solution, but it works:
x=tf.ones((5,10))
rows=tf.stack(tf.range(tf.shape(x)[0]))
column=tf.ones_like(rows)*tf.shape(x)[1]-1
idx=tf.stack((rows,column),axis=1)
x_new=tf.tensor_scatter_nd_update(x, idx, tf.where(x[:, -1] > 0, 0., 1.))
print(x_new)
And the result looks like this (the original x is a tf.ones):
[[1. 1. 1. 1. 1. 1. 1. 1. 1. 0.]
[1. 1. 1. 1. 1. 1. 1. 1. 1. 0.]
[1. 1. 1. 1. 1. 1. 1. 1. 1. 0.]
[1. 1. 1. 1. 1. 1. 1. 1. 1. 0.]
[1. 1. 1. 1. 1. 1. 1. 1. 1. 0.]]

Generating incorrect graphs from adjacency matrices using graph-tool on Python

I am trying to generate a graph from an adjacency matrix. I know it is something that has already been asked here but I can't get to generate one correctly. My code is
import numpy as np
import graph_tool.all as gt
L = 10; p = 0.6
Adj = np.zeros((L,L))
for i in range(0,L):
for j in range(i+1,L):
if np.random.rand() < p:
Adj[i,j] = 1
Adj = Adj + np.transpose(Adj)
print('Adjacency matrix is \n', Adj)
g = gt.Graph(directed=False)
g.add_edge_list(Adj.nonzero())
gt.graph_draw(g, vertex_text=g.vertex_index, output="two-nodes.pdf")
It generates an adjacency matrix with each connection happening with a probability of 60%. One result is
Adjacency matrix is
[[0. 1. 1. 0. 1. 0. 1. 1. 1. 0.]
[1. 0. 1. 1. 1. 1. 1. 0. 1. 1.]
[1. 1. 0. 1. 1. 0. 1. 1. 1. 0.]
[0. 1. 1. 0. 1. 1. 1. 0. 1. 1.]
[1. 1. 1. 1. 0. 1. 1. 1. 0. 1.]
[0. 1. 0. 1. 1. 0. 0. 0. 1. 0.]
[1. 1. 1. 1. 1. 0. 0. 1. 0. 1.]
[1. 0. 1. 0. 1. 0. 1. 0. 0. 0.]
[1. 1. 1. 1. 0. 1. 0. 0. 0. 1.]
[0. 1. 0. 1. 1. 0. 1. 0. 1. 0.]]
But I don't know why the graphical result is this one which is clearly incorrect.
As stated in add_edge_list docs, you need
an iterator of (source, target) pairs where both source and target are vertex indexes, or a numpy.ndarray of shape (E,2), where E is the number of edges, and each line specifies a (source, target) pair
In your case, you're passing a single tuple (check the result of Adj.nonzero()). To fix it, just try this:
g.add_edge_list(np.transpose(Adj.nonzero()))

How to insert the entered numbers into the diagonal

As usual, was practicing python and got this exercise:
Create a 4 * 4 matrix with values of 1,2,3,4 on the main diagonal of the
array.
Input and output legend:
Enter your value: 4
[[1. 0. 0. 0.]
[0. 2. 0. 0.]
[0. 0. 3. 0.]
[0. 0. 0. 4.]]
Did this so far, not much yeah but can't get how to approach to this task
import numpy as ar
x = ar.zeros((4, 4))
x[::2, 1::2] = 0
x[1::2, ::2] = 0
print(x)
You could use np.fill_diagonal() documentation
import numpy as np
x = np.zeros((4, 4))
np.fill_diagonal(x, [1,2,3,4])
print(x)
Output:
[[1. 0. 0. 0.]
[0. 2. 0. 0.]
[0. 0. 3. 0.]
[0. 0. 0. 4.]]
Automatically using the number from your input:
import numpy as np
input_val = int(input('Enter your value:'))
x = np.zeros((input_val, input_val))
np.fill_diagonal(x, range(1,input_val+1))
print(x)
Output:

In a 10 by 10 matrix, why does this array notation not work?

I would like to know why doesn't my following code output a 1. on the left and right side of the matrix:
import numpy as np
import matplotlib.pyplot as plt
N = 10
def dungeon(N):
dungeon = np.zeros((N, N))
# Top and bottom
dungeon[0] = 1
dungeon[-1] = 1
# Right side
#dungeon[1][0] = 1
#dungeon[2][0] = 1
# etc...
dungeon[0:3][0]
print(dungeon)
Why doesn't dungeon[0:3][0] output 1.'s on the left column? How can I fix this without individually writing dungeon[1][0], dungeon[2][0], etc...?
You are generating a 10x10 Matrix.
dungeon[0] = 1 sets all the elements in the 1st row to 1.
dungeon[-1] = 1 sets all the elements in the last row to 1.
You didn't set the right and left sides.
There are two ways to generate a 10x10 matrix which contains 0s in the center and 1s at the sides:
dungeon = np.ones((10, 10))
dungeon [1:-1, 1:-1] = 0
OR
dungeon = np.zeros((10, 10))
dungeon[0:N,0:1] = 1
dungeon[0:N,-1:N] = 1
dungeon[0:1,0:N] = 1
dungeon[-1:N,0:N] = 1
OUTPUT:
[[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[1. 0. 0. 0. 0. 0. 0. 0. 0. 1.]
[1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]]
Just take the transpose!
import numpy as np
N = 10
def dungeon(N):
dungeon = np.zeros((N, N))
# Top and bottom
dungeon[0] = 1
dungeon[-1] = 1
# Right and left side
dungeon = dungeon.T
dungeon[0] = 1
dungeon[-1] = 1
print(dungeon)

Update 3 and 4 dimension elements of numpy array

I have a numpy array of shape [12, 8, 5, 5]. I want to modify the values of 3rd and 4th dimension for each element.
For e.g.
import numpy as np
x = np.zeros((12, 80, 5, 5))
print(x[0,0,:,:])
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.]]
Modify values:
y = np.ones((5,5))
x[0,0,:,:] = y
print(x[0,0,:,:])
Output:
[[ 1. 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 can modify for all x[i,j,:,:] using two for loops. But, I was wondering if there is any pythonic way to do it without running two loops. Just curious to know :)
UPDATE
Actual use case:
dict_weights = copy.deepcopy(combined_weights)
for i in range(0, len(combined_weights[each_layer][:, 0, 0, 0])):
for j in range(0, len(combined_weights[each_layer][0, :, 0, 0])):
# Extract 5x5
trans_weight = combined_weights[each_layer][i,j]
trans_weight = np.fliplr(np.flipud(trans_weight ))
# Update
dict_weights[each_layer][i, j] = trans_weight
NOTE: The dimensions i, j of combined_weights can vary. There are around 200 elements in this list with varied i and j dimensions, but 3rd and 4th dimensions are always same (i.e. 5x5).
I just want to know if I can updated the elements combined_weights[:,:,5, 5] with transposed values without running 2 for loops.
Thanks.
Simply do -
dict_weights[each_layer] = combined_weights[each_layer][...,::-1,::-1]

Categories