Convert array from np.triu_indices to symmetric matrix - python

I can easily convert a symmetric matrix to an array/1-d matrix of one of its triangular components using
A_symmetric = np.matrix([[1,2][2,3]])
A_array = A_symmetric[np.triu_indices(2)] == np.matrix([1,2,3])
(and similary with tril_indices)
But how can I reverse this operations, i.e. how do I get a symmetric matrix from the array/1-d matrix given by triu_indices?

If you are ok with arrays instead of matrices you could simply preallocate and assign:
>>> i, j = np.triu_indices(8)
>>> a = np.arange(i.size)
>>> M = np.empty((8, 8), a.dtype)
>>> M[i, j] = a
>>> M[j, i] = a
>>> M
array([[ 0, 1, 2, 3, 4, 5, 6, 7],
[ 1, 8, 9, 10, 11, 12, 13, 14],
[ 2, 9, 15, 16, 17, 18, 19, 20],
[ 3, 10, 16, 21, 22, 23, 24, 25],
[ 4, 11, 17, 22, 26, 27, 28, 29],
[ 5, 12, 18, 23, 27, 30, 31, 32],
[ 6, 13, 19, 24, 28, 31, 33, 34],
[ 7, 14, 20, 25, 29, 32, 34, 35]])

Related

Advanced slicing over 1D numpy array

Is it possible to apply few conditions to numpy.array while indexing them? In my case I want to show first 10 elements and then 2 neighbour elements with step 5:
numpy.arange(40)
#Output is:
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
34, 35, 36, 37, 38, 39])
Applying my conditions to this array I want to get this:
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 14, 15, 20, 21, 26, 27, 32,
33, 38, 39])
I haven't found any solution. I thought it should look something like this:
np.arange(40)[0:10, 10:len(np.arange(40)):5]
But it's not working for me.
You can try custom indexing on reshaped array:
n = 40
idx = np.zeros(n//2, dtype=bool)
idx[:5] = True
idx[4:None:3] = True
>>> np.arange(n).reshape(-1,2)[idx]
array([[ 0, 1],
[ 2, 3],
[ 4, 5],
[ 6, 7],
[ 8, 9],
[14, 15],
[20, 21],
[26, 27],
[32, 33],
[38, 39]])
>>> np.arange(n).reshape(-1,2)[idx].ravel()
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 14, 15, 20, 21, 26, 27, 32,
33, 38, 39])

More efficient way to write this numpy code

Hi I'd like to create a 10 x 5 matrix with the first column filled with 1 to 10 and the subsequent columns filled with 2, 3, 4 and 5 times the values of the first column.
I've made things work with the following code, but is there a shorter way to do this?
import numpy as np
mat = np.zeros([10,5])
mat[:,0] = np.arange(1,11)
mat[:,1] = np.dot(mat[:,0],2)
mat[:,2] = np.dot(mat[:,0],3)
mat[:,3] = np.dot(mat[:,0],4)
mat[:,4] = np.dot(mat[:,0],5)
I think you can achieve this by outer product.
Try:
import numpy as np
a = np.arange(1, 11).reshape(-1, 1) # column vector (1,2,3,...,10)
b = np.arange(1, 6).reshape(1, -1) # row vector (1,2,3,...,5)
np.matmul(a, b) # matrix of entries of multiplication of the indices (1-based indices)
or the one-liner:
np.arange(1, 11).reshape(-1, 1) * np.arange(1, 6).reshape(1, -1)
This works for me:
>>> np.array([np.array([1,2,3,4,5]) * i for i in range(1,11)])
array([[ 1, 2, 3, 4, 5],
[ 2, 4, 6, 8, 10],
[ 3, 6, 9, 12, 15],
[ 4, 8, 12, 16, 20],
[ 5, 10, 15, 20, 25],
[ 6, 12, 18, 24, 30],
[ 7, 14, 21, 28, 35],
[ 8, 16, 24, 32, 40],
[ 9, 18, 27, 36, 45],
[10, 20, 30, 40, 50]])
This is exactly what builtin numpy outer does:
>>> np.outer(np.arange(1, 11), np.arange(1, 6))
array([[ 1, 2, 3, 4, 5],
[ 2, 4, 6, 8, 10],
[ 3, 6, 9, 12, 15],
[ 4, 8, 12, 16, 20],
[ 5, 10, 15, 20, 25],
[ 6, 12, 18, 24, 30],
[ 7, 14, 21, 28, 35],
[ 8, 16, 24, 32, 40],
[ 9, 18, 27, 36, 45],
[10, 20, 30, 40, 50]])

How to cast a list into an array with specific ordering of the elements in the array

If I have a list:
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
I would like to cast the above list into an array with the following arrangements of the elements:
array([[ 1, 2, 3, 7, 8, 9]
[ 4, 5, 6, 10, 11, 12]
[13, 14, 15, 19, 20, 21]
[16, 17, 18, 22, 23, 24]])
How do I do this or what is the best way to do this? Many thanks.
I have done this in a crude way below where I will just get all the sub-matrix and then concatenate all of them at the end:
np.array(results[arr.shape[0]*arr.shape[1]*0:arr.shape[0]*arr.shape[1]*1]).reshape(arr.shape[0], arr.shape[1])
array([[1, 2, 3],
[4, 5, 6]])
np.array(results[arr.shape[0]*arr.shape[1]*1:arr.shape[0]*arr.shape[1]*2]).reshape(arr.shape[0], arr.shape[1])
array([[ 7, 8, 9],
[ 10, 11, 12]])
etc,
But I will need a more generalized way of doing this (if there is one) as I will need to do this for an array of any size.
You could use the reshape function from numpy, with a bit of indexing :
a = np.arange(24)
>>> a
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23])
Using reshape and a bit of indexing :
a = a.reshape((8,3))
idx = np.arange(2)
idx = np.concatenate((idx,idx+4))
idx = np.ravel([idx,idx+2],'F')
b = a[idx,:].reshape((4,6))
Ouptut :
>>> b
array([[ 0, 1, 2, 6, 7, 8],
[ 3, 4, 5, 9, 10, 11],
[12, 13, 14, 18, 19, 20],
[15, 16, 17, 21, 22, 23]])
Here the tuple (4,6) passed to reshape indicates that you want your array to be 2 dimensional, and have 4 arrays of 6 elements. Those values can be computed.
Then we compute the index to set the correct order of the data. Obvisouly, this a complicated bit here. As I'm not sure what you mean by "any size of data", its difficult for me to give you a agnostic way to compute that index.
Obviously, if you are using a list and not an np.array, you might have to convert the list first, for example by using np.array(your_list).
Edit :
I'm not sure if this exactly what you are after, but this should work for any array evenly divisible by 6 :
def custom_order(size):
a = np.arange(size)
a = a.reshape((size//3,3))
idx = np.arange(2)
idx = np.concatenate([idx+4*i for i in range(0,size//(6*2))])
idx = np.ravel([idx,idx+2],'F')
b = a[idx,:].reshape((size//6,6))
return b
>>> custom_order(48)
array([[ 0, 1, 2, 6, 7, 8],
[ 3, 4, 5, 9, 10, 11],
[12, 13, 14, 18, 19, 20],
[15, 16, 17, 21, 22, 23],
[24, 25, 26, 30, 31, 32],
[27, 28, 29, 33, 34, 35],
[36, 37, 38, 42, 43, 44],
[39, 40, 41, 45, 46, 47]])

Reshape 3D array to 2D array Python

If I have a 3D array of ([4,3,3]) like this:
[[0,1,2] [[9,10,11 ] [[18,19,20] [[27,28,29]
[3,4,5] [12,13,14] [21,22,23] [30,31,32]
[6,7,8]] , [15,16,17]] , [24,25,26]] , [33,34,35]]
How would I convert it to a 2D array of ([6,6]) like this so that the 1st half of arrays are at the top half of the 160x160 and the 2nd half are at the bottom:
[[0,1,2,9,10,11]
[3,4,5,12,13,14]
[6,7,8,15,16,17]
[18,19,20,27,28,29]
[21,22,23,30,31,32]
[24,25,26,33,34,35]]
My array creation:
qDCTReversed = np.zeros((400,8,8), dtype=np.int)
And I need a (160,160) array.
A very fast one line solution using no for-loops is this:
# initialization
qDCTReversed = np.arange(4*3*3).reshape((4,3,3))
# calculation
qDCTReversed = qDCTReversed.reshape((2,2,3,3)).transpose((0,2,1,3)).reshape((6,6))
or for the (400,8,8) array:
qDCTReversed.reshape((20,20,8,8)).transpose((0,2,1,3)).reshape((160,160))
Speed comparison:
Mstaino's answer: 0.393 ms
yatu's answer: 0.138 ms
This answer: 0.016 ms
You can do this by looping over the list as such:
a = [[[ 0, 1, 2], [ 9,10,11]],
[[ 3, 4, 5], [12,13,14]],
[[ 6, 7, 8], [15,16,17]],
[[18,19,20], [27,28,29]],
[[21,22,23], [30,31,32]],
[[24,25,26], [33,34,35]]]
b = [[i for j in k for i in j ] for k in a]
print(b)
outputs:
[ 0, 1, 2, 9, 10, 11]
[ 3, 4, 5, 12, 13, 14]
[ 6, 7, 8, 15, 16, 17]
[18, 19, 20, 27, 28, 29]
[21, 22, 23, 30, 31, 32]
[24, 25, 26, 33, 34, 35]
The reshape you ask can be done with:
x = np.arange(36).reshape((4,3,3))
np.vstack(np.hstack(x[2*i:2+2*i]) for i in range(x.shape[0]//2))
>>array([[ 0, 1, 2, 9, 10, 11],
[ 3, 4, 5, 12, 13, 14],
[ 6, 7, 8, 15, 16, 17],
[18, 19, 20, 27, 28, 29],
[21, 22, 23, 30, 31, 32],
[24, 25, 26, 33, 34, 35]])

How to split a list into N random-but-min-sized chunks

For example: I want to split range(37) in n=5 chunks, which each chunk having
len(chunk) >= 4.
>>> def divide(lst, min_size, split_size):
it = iter(lst)
from itertools import islice
size = len(lst)
for i in range(split_size - 1,0,-1):
s = random.randint(min_size, size - min_size * i)
yield list(islice(it,0,s))
size -= s
yield list(it)
>>> list(divide(range(37), 4, 5))
[[0, 1, 2, 3], [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22], [23, 24, 25, 26, 27], [28, 29, 30, 31], [32, 33, 34, 35, 36]]
>>> list(divide(range(37), 4, 5))
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], [17, 18, 19, 20, 21, 22], [23, 24, 25, 26], [27, 28, 29, 30, 31], [32, 33, 34, 35, 36]]
>>> list(divide(range(37), 4, 5))
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23], [24, 25, 26, 27, 28], [29, 30, 31, 32], [33, 34, 35, 36]]
>>> list(divide(range(37), 4, 5))
[[0, 1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15, 16], [17, 18, 19, 20, 21, 22, 23, 24], [25, 26, 27, 28, 29, 30, 31], [32, 33, 34, 35, 36]]
>>>
For example you could initialy set each of n chunks size to 4 and then calculate: r = (m=37 mod n), if m>=20. And then just add 1 to the first chunk and decrease r, 1 to second chunk and decrease r....and repeat until r = 0. Then you have your chunks and you can fill them.
def divide(val, num=5, minSize=4):
''' Divides val into # num chunks with each being at least of size minSize.
It limits max size of a chunk using math.ceil(val/(num-len(chunks)))'''
import random
import math
chunks = []
for i in xrange(num-1):
maxSize = math.ceil(val/(num-len(chunks)))
newSize = random.randint(minSize, maxSize)
val = val - newSize
chunks.append(newSize)
chunks.append(val)
return chunks
Calling divide with different parameters:
>>> divide(37,5,4)
>>> [7, 5, 4, 10, 11]
>>> divide(37,5,4)
>>> [4, 5, 4, 10, 14]
>>> divide(50,6,5)
>>> [6, 8, 8, 5, 9, 14]

Categories

Resources