I am working with numpy arrays and and I would like to move a certain value located at certain index by 2 positions on the right while shifting the others element involved on the left by one position each. To be more precise, here below is what I would like to achieve:
from:
start = [0., 1., 2.],
[0., 0., 0.],
[5., 0., 1.]
to:
end = [1., 2., 0.],
[0., 0., 0.],
[5., 0., 1.]
As you can see from the first row, 0 has been moved by two position on the right and 1 and 2 by one position on the left. So far, I succeded by moving an element by one position and moving the other by defining:
def right_move(arr, index, n_steps:int):
row = index[0]
col = index[1]
try:
arr[row,col], arr[row, col + n_steps] = arr[row, col + n_steps], arr[row, col]
except:
pass
return arr
where n_steps=1. If I input n_steps=2, it won't work since the element in the middle of the swapping remain unchanged.
Can someone help me? Other easier solution are more then welcome!
Thanks
You can use numpy.roll:
import numpy as np
arr = np.array([[0., 1., 2.],
[0., 0., 0.],
[5., 0., 1.]])
arr[0] = np.roll(arr[0], 2)
arr
# output:
# [[1., 2., 0.],
# [0., 0., 0.],
# [5., 0., 1.]]
It is supposed the functionality of np.roll. However, np.roll doesn't allow roll on individual row. It piqued my interest to implement a function using as_strided to extend this functionality to np.roll
from numpy.lib.stride_tricks import as_strided
def custom_roll(arr, r_tup):
m = np.asarray(r_tup)
arr_roll = arr[:, [*range(arr.shape[1]),*range(arr.shape[1]-1)]].copy() #need `copy`
strd_0, strd_1 = arr_roll.strides
n = arr.shape[1]
result = as_strided(arr_roll, (*arr.shape, n), (strd_0 ,strd_1, strd_1))
return result[np.arange(arr.shape[0]), (n-m)%n]
Just passing a tuple indicate number of steps for each row. In your case, it rolls only row 0 either 2 positions forward or -1 position backward. so the tuple is (2,0,0) or (-1,0,0)
start = np.array([[0., 1., 2.],
[0., 0., 0.],
[5., 0., 1.])
out = custom_roll(start, (2,0,0))
Out[780]:
array([[1., 2., 0.],
[0., 0., 0.],
[5., 0., 1.]])
Or
out = custom_roll(start, (-1,0,0))
Out[782]:
array([[1., 2., 0.],
[0., 0., 0.],
[5., 0., 1.]])
Roll first and last row: (2,0,2)
out = custom_roll(start, (2,0,2))
Out[784]:
array([[1., 2., 0.],
[0., 0., 0.],
[0., 1., 5.]])
Related
I want to create m number of matrices, each of which is an n x 1 numpy arrays. Moreover those matrices should have only two nonzero entries in the two rows, all other rows should have 0 as their entries, meaning that matrix number m=1 should have entries m[0,:]=m[1,:]=1, rest elements are 0. And similarly the last matrix m=m should have entries like m[n-1,:]=m[n,:]=1, where rest of the elements in other rows are 0. So for consecutive two matrices, the nonzero elements shift by two rows. And finally, I would like them to be stored into a dictionary or in a file.
What would be a neat way to do this?
Is this what you're looking for?
In [2]: num_rows = 10 # should be divisible by 2
In [3]: np.repeat(np.eye(num_rows // 2), 2, axis=0)
Out[3]:
array([[1., 0., 0., 0., 0.],
[1., 0., 0., 0., 0.],
[0., 1., 0., 0., 0.],
[0., 1., 0., 0., 0.],
[0., 0., 1., 0., 0.],
[0., 0., 1., 0., 0.],
[0., 0., 0., 1., 0.],
[0., 0., 0., 1., 0.],
[0., 0., 0., 0., 1.],
[0., 0., 0., 0., 1.]])
In terms of storage in a file, you can use np.save and np.load.
Note that the default data type for np.eye will be float64. If you expect your values to be small when you begin integrating or whatever you're planning on doing with your state vectors, I'd recommend setting the data type appropriately (like np.uint8 for positive integers < 256 for example).
i am able to get the checkerboard pattern, the + pattern and the one with 1's on the border but i am not able to figure this out. Can somebody help?
If you're sticking with whole dimensions, then as #Péter Leéh pointed out:
>>> np.eye(n) + np.fliplr(np.eye(n))
array([[1., 0., 0., 1.],
[0., 1., 1., 0.],
[0., 1., 1., 0.],
[1., 0., 0., 1.]])
will suffice, np.fliplr(x) (horizontal flip) is identical to np.flip(x, axis=1).
However if n is odd, then you will have to replace the center element with a 1. e.g. n=5:
>>> x = np.eye(n) + np.fliplr(np.eye(n))
>>> x[n//2, n//2] = 1
array([[1., 0., 0., 0., 1.],
[0., 1., 0., 1., 0.],
[0., 0., 1., 0., 0.],
[0., 1., 0., 1., 0.],
[1., 0., 0., 0., 1.]])
I have an 2 dimensional array with np.shape(input)=(a,b) and that looks like
input=array[array_1[0,0,0,1,0,1,2,0,3,3,2,...,entry_b],...array_a[1,0,0,1,2,2,0,3,1,3,3,...,entry_b]]
Now I want to create an array np.shape(output)=(a,b,b) in which every entry that had the same value in the input get the value 1 and 0 otherwise
for example:
input=[[1,0,0,0,1,2]]
output=[array([[1., 0., 0., 0., 1., 0.],
[0., 1., 1., 1., 0., 0.],
[0., 1., 1., 1., 0., 0.],
[0., 1., 1., 1., 0., 0.],
[1., 0., 0., 0., 1., 0.],
[0., 0., 0., 0., 0., 1.]])]
My code so far is looking like:
def get_matrix(svdata,padding_size):
List=[]
for k in svdata:
matrix=np.zeros((padding_size,padding_size))
for l in range(padding_size):
for m in range(padding_size):
if k[l]==k[m]:
matrix[l][m]=1
List.append(matrix)
return List
But it takes 2:30 min for an input array of shape (2000,256). How can I become more effiecient by using built in numpy solutions?
res = input[:,:,None]==input[:,None,:]
Should give boolean (a,b,b) array
res = res.astype(int)
to get a 0/1 array
You're trying to create the array y where y[i,j,k] is 1 if input[i,j] == input[i, k]. At least that's what I think you're trying to do.
So y = input[:,:,None] == input[:,None,:] will give you a boolean array. You can then convert that to np.dtype('float64') using astype(...) if you want.
pop=np.zeros((population_size,chromosome_length))
for i in range(population_size):
for j in range(i,chromosome_length):
pop[i,j] = random.randint(0, 1)
pop
array([[0., 1., 0., 1., 1., 1., 0., 0., 1., 1.],
[0., 0., 1., 0., 1., 0., 1., 1., 0., 0.],
[0., 0., 1., 0., 0., 1., 1., 0., 0., 1.],
[0., 0., 0., 0., 1., 0., 1., 1., 1., 0.],
[0., 0., 0., 0., 1., 0., 1., 1., 0., 1.],
[0., 0., 0., 0., 0., 0., 0., 1., 0., 0.]])
I have another array expected, generated from un-shown code, with an example below:
array([[1.99214608],
[1.45140389],
[0.07068525],
[0.69507167],
[1.08384057],
[0.70685254]])
I then want to bin the values of expected based on custom intervals:
actual=np.zeros((population_size,1))
for i in range(len(expected)):
if expected[i]>=1.5:
actual[i]=2
elif 1.5>expected[i]>=0.9:
actual[i]=1
else:
actual[i]=0
actual=actual.astype(int)
total_count=int(np.sum(actual))
print(total_count)
[[2]
[1]
[0]
[0]
[1]
[0]]
4
and I want the final output as:
array([[0., 1., 0., 1., 1., 1., 0., 0., 1., 1.],
[0., 1., 0., 1., 1., 1., 0., 0., 1., 1.],
[0., 0., 1., 0., 1., 0., 1., 1., 0., 0.],
[0., 0., 0., 0., 1., 0., 1., 1., 0., 1.]])
based on the values in total_count. The first row of pop got copied twice, the second row once and the fifth row once. In short, what I want is repeat/copy/duplicate elements of an array based on the integer element of another array.
I'll try address this question in sections as you are using NumPy arrays as though they are lists, and therefore losing a lot of the purpose of the library in the first place. Although the syntax is much more compact, it comes with significant speed increases.
Creating the population
This one is simple enough. We can make a direct replacement for generating pop by using numpy.random.randint. We need to specify values for population_size and chromosome length and use those to specify the output size.
population_size = 6
chromosome_length = 10
pop = np.random.randint(0, 2, (population_size, chromosome_length))
NOTE: This won't give the exact same values as you've included in your actual question because we haven't set a seed for the random number generator. However, the code is directly equivalent to your for loop but more performant.
Generating expected
I can't make an exact replacement for this section because it's too much to replace your loops, with some variables also being undefined. So, I'm just assuming that I'll get the same 2D array as you have shown:
expected = np.array([[1.99214608],
[1.45140389],
[0.07068525],
[0.69507167],
[1.08384057],
[0.70685254]])
Binning the data
This is a bit more complex. We can make use of numpy.digitize to bin the data between your intervals (0, 0.9 and 1.5). However, this method will not work with 2D arrays so I'm going to use numpy.ravel() to flatten the array first.
This is going to give back a list of bin identities that each value of expected belongs to. However, bin identities start at 1, and we want to use these values as indicies of an array further on, so I'm also going to subtract 1 from the result at the same time.
bins = np.array([0, 0.9, 1.5])
dig = np.digitize(expected.ravel(), bins) - 1
Last Steps
I'm going to create an array of values that correspond to the bin categories. We can then use numpy.take to replace the values of dig with the corresponding replacement values.
replacements = np.array([0, 1, 2])
actual = np.take(replacements, dig)
And finally :), we can use numpy.repeat using actual to take rows from pop in the correct proportions to build the output.
Final Code
import numpy as np
population_size = 6
chromosome_length = 10
pop = np.random.randint(0, 2, (population_size, chromosome_length))
# But I'm going to deliberately overwrite the above to solve your particular case
pop = np.array([[0., 1., 0., 1., 1., 1., 0., 0., 1., 1.],
[0., 0., 1., 0., 1., 0., 1., 1., 0., 0.],
[0., 0., 1., 0., 0., 1., 1., 0., 0., 1.],
[0., 0., 0., 0., 1., 0., 1., 1., 1., 0.],
[0., 0., 0., 0., 1., 0., 1., 1., 0., 1.],
[0., 0., 0., 0., 0., 0., 0., 1., 0., 0.]])
# Hard-coded :/
expected = np.array([[1.99214608],
[1.45140389],
[0.07068525],
[0.69507167],
[1.08384057],
[0.70685254]])
bins = np.array([0, 0.9, 1.5])
dig = np.digitize(expected.ravel(), bins) - 1
replacements = np.array([0, 1, 2])
actual = np.take(replacements, dig)
out = np.repeat(pop, actual, axis=0)
print(out)
Gives:
[[0. 1. 0. 1. 1. 1. 0. 0. 1. 1.]
[0. 1. 0. 1. 1. 1. 0. 0. 1. 1.]
[0. 0. 1. 0. 1. 0. 1. 1. 0. 0.]
[0. 0. 0. 0. 1. 0. 1. 1. 0. 1.]]
I'm having trouble understanding how np.fill_diagonal is implemented here.
I found a post here explaining a way to fill the sub and super diagonals with certain values but I don't really understand the arguments of the function. Here is the code:
a = np.zeros((4, 4))
b = np.ones(3)
np.fill_diagonal(a[1:], b)
np.fill_diagonal(a[:,1:], -b)
I don't understand how fill_diagonal is used here. I thought that the second argument had to be a scalar. Also, I don't understand what is happening with the slices of 'a'.
"For an array a with a.ndim >= 2, the diagonal is the list of locations with indices a[i, ..., i] all identical. This function modifies the input array in-place, it does not return a value." (Source) The documentation for this method says b should be a scalar, however if b is an array of length equal to the length of the diagonal of the input array, then it will fill the values of b in for the diagonal.
The key is that the number of elements in b is equal to the number of elements along the diagonals of each sub-array of a. The nth diagonal value of the sub-array is filled in with the nth value of b.
The first sub-array of a that is modified is all but the first row of a (this means 3 rows, 4 columns), so the number of diagonal elements is 3.
The second sub-array of a is the last three columns (4 x 3 matrix) of a which also has only 3 diagonal elements.
==========================================================================
Thanks G. Anderson for the comment. I'm editing this into the post to draw attention to it:
"It's worth noting that b doesn't have to have the same length as the diagonal it's filling. if b is longer, then n elements of the diagonal will be filled with the first n elements of b. If n is shorter than the diagonal, then b will be repeated to fill the diagonal"
Your examples involve filling slices, views, of the original array.
In [79]: a = np.zeros((4, 4))
...: b = np.arange(1,5)
In [80]:
The simple case - filling the whole array:
In [80]: np.fill_diagonal(a,b)
In [81]: a
Out[81]:
array([[1., 0., 0., 0.],
[0., 2., 0., 0.],
[0., 0., 3., 0.],
[0., 0., 0., 4.]])
fill_diagonal takes an array to be filled, and values to put in the diagonal. The docs does say scalar, but that's overly restrictive. As I show, it can be a 1d array of the right size.
In [82]: a = np.zeros((4, 4))
...: b = np.arange(1,4)
filling the last 3 rows:
In [83]: a[1:]
Out[83]:
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
In [84]: np.fill_diagonal(a[1:],b)
In [85]: a
Out[85]:
array([[0., 0., 0., 0.],
[1., 0., 0., 0.],
[0., 2., 0., 0.],
[0., 0., 3., 0.]])
In [86]: a = np.zeros((4, 4))
...: b = np.arange(1,4)
filling the last 3 columns:
In [87]: a[:,1:]
Out[87]:
array([[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.],
[0., 0., 0.]])
In [88]: np.fill_diagonal(a[:,1:],b)
In [89]: a
Out[89]:
array([[0., 1., 0., 0.],
[0., 0., 2., 0.],
[0., 0., 0., 3.],
[0., 0., 0., 0.]])
The key is that fill_diagonal works in-place, and the a[1:] and a[:,1:] produce views of a.
Look at the slice of a after filling:
In [90]: a[:,1:]
Out[90]:
array([[1., 0., 0.],
[0., 2., 0.],
[0., 0., 3.],
[0., 0., 0.]])
The docs demonstrate the use with np.fliplr(a). That too, creates a view which can be modified in place.
The actual write is done with:
a.flat[:end:step] = val
where end and step have been calculated from the dimensions. For example to fill a 3x3 array, we can write to every 4th element.
In [96]: a[:,1:].ravel()
Out[96]: array([1., 0., 0., 0., 2., 0., 0., 0., 3., 0., 0., 0.])