Sub matrix of a list of lists (without numpy) - python

Suppose I have a matrix composed of a list of lists like so:
>>> LoL=[list(range(10)) for i in range(10)]
>>> LoL
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]
Assume, also, that I have a numpy matrix of the same structure called LoLa:
>>> LoLa=np.array(LoL)
Using numpy, I could get a submatrix of this matrix like this:
>>> LoLa[1:4,2:5]
array([[2, 3, 4],
[2, 3, 4],
[2, 3, 4]])
I can replicate the numpy matrix slice in pure Python like so:
>>> r=(1,4)
>>> s=(2,5)
>>> [LoL[i][s[0]:s[1]] for i in range(len(LoL))][r[0]:r[1]]
[[2, 3, 4], [2, 3, 4], [2, 3, 4]]
Which is not the easiest thing in the world to read nor the most efficient :-)
Question: Is there an easier way (in pure Python) to slice an arbitrary matrix as a sub matrix?

In [74]: [row[2:5] for row in LoL[1:4]]
Out[74]: [[2, 3, 4], [2, 3, 4], [2, 3, 4]]
You could also mimic NumPy's syntax by defining a subclass of list:
class LoL(list):
def __init__(self, *args):
list.__init__(self, *args)
def __getitem__(self, item):
try:
return list.__getitem__(self, item)
except TypeError:
rows, cols = item
return [row[cols] for row in self[rows]]
lol = LoL([list(range(10)) for i in range(10)])
print(lol[1:4, 2:5])
also yields
[[2, 3, 4], [2, 3, 4], [2, 3, 4]]
Using the LoL subclass won't win any speed tests:
In [85]: %timeit [row[2:5] for row in x[1:4]]
1000000 loops, best of 3: 538 ns per loop
In [82]: %timeit lol[1:4, 2:5]
100000 loops, best of 3: 3.07 us per loop
but speed isn't everything -- sometimes readability is more important.

For one, you can use slice objects directly, which helps a bit with both the readability and performance:
r = slice(1,4)
s = slice(2,5)
[LoL[i][s] for i in range(len(LoL))[r]]
And if you just iterate over the list-of-lists directly, you can write that as:
[row[s] for row in LoL[r]]

Do this,
submat = [ [ mat[ i ][ j ] for j in range( index1, index2 ) ] for i in range( index3, index4 ) ]
the submat will be the rectangular (square if index3 == index1 and index2 == index4) chunk of your original big matrix.

I dont know if its easier, but let me throw an idea to the table:
from itertools import product
r = (1+1, 4+1)
s = (2+1, 5+1)
array = [LoL[i][j] for i,j in product(range(*r), range(*s))]
This is a flattened version of the submatrix you want.

Related

Create array from randomly extracted data from array of arrays

Suppose that I have the following array or arrays:
[[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],[0, 1, 2, 3, 4, 5],[0, 1, 2, 3, 4, 5, 6, 7]]
What's the best way to loop into the main array and randomly extract one number from each sub-array every time and create another array with them? For instance, in the first pass, the result would be:
[2,5,6]
The second pass could be:
[8,0,7]
etc. At this stage I don't have any clues how to do it.
If you have python lists, you can use random.choice in a list comprehension:
L = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],[0, 1, 2, 3, 4, 5],[0, 1, 2, 3, 4, 5, 6, 7]]
from random import choice
out = [choice(l) for l in L]
example output:
[3, 0, 5]
variant
Imagine you want to pick each item only a single time in each iteration, you could also use pop on a random position:
L = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],[0, 1, 2, 3, 4, 5],[0, 1, 2, 3, 4, 5, 6, 7]]
for i in range(6):
print([l.pop(np.random.randint(len(l))) for l in L])
Obviously, here you cannot have a number of iterations greater than the length of the shortest sublist
example output:
[5, 4, 0]
[2, 2, 6]
[3, 5, 7]
[0, 1, 1]
[4, 3, 2]
[9, 0, 3]

Reorder array in Python based on a column

I have an array like this:
a = [[ 8, 7, 6, 5, 9],
[1, 2, 1, 6, 4],
[4, 2, 5, 4, 2]]`
I want to change the order of that array based on second row with an order like this:
b = [2, 6, 1, 1, 4]
So, I want the result becomes like this:
a = [[7, 5, 8, 6, 9],
[2, 6, 1, 1, 4],
[2, 4, 4, 5, 2]]
How can I solve this problem in Python?
a = [[ 8, 7, 6, 5, 9],
[1, 2, 1, 6, 4],
[4, 2, 5, 4, 2]]
a[1] = [2, 6, 1, 1, 4]
Try that.
In this answer, I'm making the following two assumptions:
All sub-lists are 5 elements in length
The desired logic is to move the 2nd and 4th elements to be 1st and 2nd respectively
If both of the assumptions made above are true, you can use list comprehension on a nested list, and create a list to specify how the lists should be reordered.
a = [[8, 7, 6, 5, 9],
[1, 2, 1, 6, 4],
[4, 2, 5, 4, 2]]
new_ord = [1, 3, 0, 2, 4]
b = [[l[i] for i in new_ord] for l in a]
print(b) #prints: [[7, 5, 8, 6, 9], [2, 6, 1, 1, 4], [2, 4, 4, 5, 2]]

Extracting portions of low-dimensional numpy array into final axes of higher-dimensional array

I have a static shape-(l,l) array C. I want to extract portions of it into some other array K, which has shape (m,m,n,n). The starting index of what I want to extract from C is given in array i0, which has shape (m,m).
Some element of K will be given by K[i,j,:,:] = C[i0[i,j]:i0[i,j]+n, i0[i,j]:i0[i,j]+n]. So going off some other similar questions it seemed like this might do the job...
C[i0[None, None, ...] + np.arange(n)[..., None, None],
i0[None, None, ...] + np.arange(n)[..., None, None], I, J]
which raises an IndexError. I guess this is because C is only 2D, and the dimensions can't be increased. Though that could be easily fixed by tiling C, since C is large, that would be rather expensive to remake m*m times.
So my question is how to extract different (2D) portions of a 2D array into corresponding portions of a 4D array.
One way would be with np.meshgrid to create 2D indexing meshes corresponding to the window of (n,n) shape, adding those with i0 that's extended with two new axes along which broadcasting would take place. Finally, we simply index into C to give us the desired 4D output. Thus, one implementation would be like so -
N = np.arange(n)
X,Y = np.meshgrid(N,N)
out = C[i0[...,None,None] + Y,i0[...,None,None] + X]
Sample run -
In [153]: C
Out[153]:
array([[3, 5, 1, 6, 3, 5, 8, 7, 0, 2],
[8, 4, 6, 8, 7, 2, 6, 2, 5, 0],
[3, 7, 7, 7, 3, 4, 4, 6, 7, 6],
[7, 0, 8, 2, 1, 1, 0, 4, 4, 6],
[2, 4, 6, 0, 0, 5, 6, 8, 0, 0],
[4, 6, 1, 0, 5, 6, 2, 1, 7, 4],
[0, 5, 5, 3, 7, 5, 7, 1, 4, 0],
[6, 4, 4, 7, 2, 4, 6, 6, 6, 5],
[5, 2, 3, 2, 2, 5, 4, 5, 2, 5],
[3, 7, 1, 0, 4, 4, 6, 6, 2, 2]])
In [154]: i0
Out[154]:
array([[1, 0, 4, 4],
[0, 4, 4, 0],
[2, 3, 1, 3],
[2, 2, 0, 4]])
In [155]: n = 3
In [157]: out[0,0,:,:]
Out[157]:
array([[4, 6, 8],
[7, 7, 7],
[0, 8, 2]])
In [158]: C[i0[0,0]:i0[0,0]+n,i0[0,0]:i0[0,0]+n]
Out[158]:
array([[4, 6, 8],
[7, 7, 7],
[0, 8, 2]])

combining two arrays in numpy,

I use genfromtxt to read in an array from a text file and i need to split this array in half do a calculation on them and recombine them. However i am struggling with recombining the two arrays. here is my code:
X2WIN_IMAGE = np.genfromtxt('means.txt').T[1]
X2WINa = X2WIN_IMAGE[0:31]
z = np.mean(X2WINa)
X2WINa = X2WINa-z
X2WINb = X2WIN_IMAGE[31:63]
ww = np.mean(X2WINb)
X2WINb = X2WINb-ww
X2WIN = str(X2WINa)+str(X2WINb)
print X2WIN
How do i go about recombining X2WINa and X2WINb in one array? I just want one array with 62 components
X2WINc = np.append(X2WINa, X2WINb)
if you want to combine row-wise use np.vstack(), and if column-wise use np.hstack(). Example:
np.hstack( (np.arange(10), np.arange(10)) )
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
np.vstack( (np.arange(10), np.arange(10)) )
array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]])
combined_array = np.concatenate((X2WINa, X2Winb))
And another one using numpy.r_:
X2WINc = np.r_[X2WINa,X2WINb]
e.g.:
>>> import numpy as np
>>> np.r_[np.arange(10),np.arange(10)]
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
There's also np.c_ to column stack:
>>> np.c_[np.arange(10),np.arange(10)]
array([[0, 0],
[1, 1],
[2, 2],
[3, 3],
[4, 4],
[5, 5],
[6, 6],
[7, 7],
[8, 8],
[9, 9]])

Resizing and stretching a NumPy array

I am working in Python and I have a NumPy array like this:
[1,5,9]
[2,7,3]
[8,4,6]
How do I stretch it to something like the following?
[1,1,5,5,9,9]
[1,1,5,5,9,9]
[2,2,7,7,3,3]
[2,2,7,7,3,3]
[8,8,4,4,6,6]
[8,8,4,4,6,6]
These are just some example arrays, I will actually be resizing several sizes of arrays, not just these.
I'm new at this, and I just can't seem to wrap my head around what I need to do.
#KennyTM's answer is very slick, and really works for your case but as an alternative that might offer a bit more flexibility for expanding arrays try np.repeat:
>>> a = np.array([[1, 5, 9],
[2, 7, 3],
[8, 4, 6]])
>>> np.repeat(a,2, axis=1)
array([[1, 1, 5, 5, 9, 9],
[2, 2, 7, 7, 3, 3],
[8, 8, 4, 4, 6, 6]])
So, this accomplishes repeating along one axis, to get it along multiple axes (as you might want), simply nest the np.repeat calls:
>>> np.repeat(np.repeat(a,2, axis=0), 2, axis=1)
array([[1, 1, 5, 5, 9, 9],
[1, 1, 5, 5, 9, 9],
[2, 2, 7, 7, 3, 3],
[2, 2, 7, 7, 3, 3],
[8, 8, 4, 4, 6, 6],
[8, 8, 4, 4, 6, 6]])
You can also vary the number of repeats for any initial row or column. For example, if you wanted two repeats of each row aside from the last row:
>>> np.repeat(a, [2,2,1], axis=0)
array([[1, 5, 9],
[1, 5, 9],
[2, 7, 3],
[2, 7, 3],
[8, 4, 6]])
Here when the second argument is a list it specifies a row-wise (rows in this case because axis=0) repeats for each row.
>>> a = numpy.array([[1,5,9],[2,7,3],[8,4,6]])
>>> numpy.kron(a, [[1,1],[1,1]])
array([[1, 1, 5, 5, 9, 9],
[1, 1, 5, 5, 9, 9],
[2, 2, 7, 7, 3, 3],
[2, 2, 7, 7, 3, 3],
[8, 8, 4, 4, 6, 6],
[8, 8, 4, 4, 6, 6]])
Unfortunately numpy does not allow fractional steps (as far as I am aware). Here is a workaround. It's not as clever as Kenny's solution, but it makes use of traditional indexing:
>>> a = numpy.array([[1,5,9],[2,7,3],[8,4,6]])
>>> step = .5
>>> xstop, ystop = a.shape
>>> x = numpy.arange(0,xstop,step).astype(int)
>>> y = numpy.arange(0,ystop,step).astype(int)
>>> mg = numpy.meshgrid(x,y)
>>> b = a[mg].T
>>> b
array([[1, 1, 5, 5, 9, 9],
[1, 1, 5, 5, 9, 9],
[2, 2, 7, 7, 3, 3],
[2, 2, 7, 7, 3, 3],
[8, 8, 4, 4, 6, 6],
[8, 8, 4, 4, 6, 6]])
(dtlussier's solution is better)

Categories